Simple Calculation of Sunset Time required

Well quoting 1578 words of text to add a dozen lines of comment made me wince. Does that make us even? (-:

That's not surprising. At that point he was still locked into, and debating the nuances of the wrong solution and no one but Mr. Stockton ever thought to question otherwise. Groupthink at its very best. Ask yourself why he wanted to know when the sun rose and set. Not to set a watch, not to aim a celestial camera, not to brush his bleeding gums but to turn on the lights when it got dark, and off when it was light.

If I felt threatened by my assessment of his needs, I'd quote him again, but I'm not. Re-read with a view as to the most elemental of his "system requirements" and you'll discover it's really simply to turn his lights on at night and off during the daytime. The precision you're talking about is simply what happens in a "solution lock." People proceed to the details without exploring all possible options. Remarkably common in its occurrence. Military history abounds with examples. Ask Admiral Yamaguchi about his boss, Admiral Nagumo and his staff decision to change armament from bombs to torpedoes, exposing his planes to attack. Wait, not possible. He's at the FBOTO with the crabs now crewing his sunken ghost ship, the Hiryu, "groupthunked" to death. (That's "*Frigid* Bottom of the Ocean" in case you're wondering). (-:

formatting link
An even better example of groupthink was the Maginot line, which the Germans just went around. All the French generals thought it was a great idea, and rejected any comments to the contrary. Wiki's got a very good paragraph about groupthink that I'll just quote a part of:

formatting link
Sounds pretty much where we're at in this stage of the game.

I'm sorry, but there's just no arguing that it's far more efficient and reliable to use a light detector to detect light, not a calendar. The calculated method has so many liabilities a compelling case has to be made for its use because it's so inferior for this particular task. There has to be something the calculated version does that the photocell can't do for it to be the preferred solution. The OP's requirements were to turn lights on and off. No compelling case was made for the calculated solution.

The OP may have a reason to require the computed time; I believe Orthodox Jews have a religious requirement relating to sunrise and sunset, and other religions may as well. Like any number of other theories, it *could* be true, but we certainly can't ascertain it from any of the simpler, given facts. So I would consider it a zebra, as I would most of the assumptions proffered so far as to why the photocell is not the ideal solution.

No, not at all. When people arrive at the correct solution on Usenet they are very often never heard from again. I take it that when he discovered the photocell, he was done. Were he to use the calculated method, I would expect him to be back to discuss fine tuning or the various computational methods he could use or how compact he made the code. I'm assuming the silence was "Aha, why fool around writing code when I can install a photocell and forget about it?" Or, it could have been more like "Sidereal time? What planet am I on? I just want to turn the lights on when it's dark!" and then pfffft!

Don't you think you just asked basically the same question question twice twice? (-: He didn't respond to anything else either, so who's making assumption based on pretty thin evidence now? I think it's probably far more rational to assume that when the subject matter began careening like cars in Catskill ice storm, tempers flared and the discussion no longer had anything to do with his requirements that he simply did the rational thing and boogied and bought a photocell. (-:

I find it hard to understand the reluctance of some to the idea that photocells are the proper solution to the "lights on at night, off during the day problem" in all but the most exceptional cases of day/night light control.

I suggest reading a little more critically to distill out his true system requirements. They've already been somewhat corrupted by following the wrong solution set, but he's stated rather clearly that he wants to turn lights on and off. Calculating when to do it is a means to an end, not the basic requirement. Do you think he really wants the precise value of the sunrise and sunset for Druidical (or other) religious ceremonies or that he's trying to turn lights on when it's dark outside and off when it's not? How can a calendar tell you that the skies are incredibly overcast and artificial light might be needed? I've seen some pretty 'dark' daylight, haven't you? If not, consult the historical record, notably:

formatting link
(a.k.a Toledo in a Storm)

I believe someone already expressed the light levels involved in bright sunlight, twilight and heavily overcast twilight. Those lights levels are different enough to invalidate the calculated method. It offers no way of adjustment or compensation for such events. Those factors alone make it the simple and obvious solution to the OP defined task. It's so simple - unless you have premature solution lock.

This is just a small example of a remarkably serious problem in large tech companies. One team champions one approach and won't relinquish their bias even when management decides otherwise. Anyone working in a high tech environment for more than a year or so has already seen it. In those cases ("what language do we develop in?" is one of many perennial problems) the choice is not as clear cut as it is here. So, unless you:

1) live in cave, with no access to the sun, 2) have a religious, technical or vampirical need to know the precise time of the sun's rising or setting, 3) are trying to emulate the light cycle of another geographical latitude 4) or are using a home automation controller that has no available inputs or even any that are sharable 5) can't string a low voltage two wire cable to a window

then select the photocell option.

If you want long term reliability with no chance of a clock getting out of synch, select the photocell option.

If you want to provide light when it's actually dark, and not just when a lookup table tells you it is, select the photocell option.

If you want to know if it's dark or light outside, a photocell, not a calculation, is almost always the optimal solution. When it's not, it's because of highly unusual reasons, which we might reasonably expect the OP to mention if they existed. He didn't. He actually gave us a reason to implement photocells: he needs to conserve memory. For this application a photocell would take far fewer bytes to implement in the controller languages I know.

-- Bobby G.

Reply to
Robert Green
Loading thread data ...

.............

In particular when you were having a conversaion outside the WWW .... ;-)

Btw the taxonomically correct term would be "Internet", starting with a capital "I". An "internet" is a somewhat different thing, not at all likely to have large volumes of conversation.....

However, back in the pre-WWW days, Usenet often spanned outside the Internet. But nowadays the parts of Usenet which aren't also on the Internet are practically nonexistent.

Reply to
Paul Schlyter

The next time a misty eyed physicist appears on tv and dictates to the viewers about the deep insights of the Universe in large and tiny scales known to scientists,perhaps yuo will smile that they can't even give you san astronomical solution to when to turn on a porchlight using geocentric terms of sunrise/sunset.

There were no druids around 5 200 years ago when they constructed a monument which turns the natural lights on on a specific day and at a specific time,there were just keen observers who were familiar with the annual cycle -

formatting link
How it came to be that 5 200 years later that you concede the problem to a photocell solution can only highlight that something went drastically wrong somewhere and it most certainl;y did -

formatting link

You could not build a monument like Newgrange and its solstice marker with such a cretinous view of the Earth's motions,not even its geocentric equivalent is correct.It is as though men have lost their minds in the most bewildering sort of way where intelligence is entirely absent,They even have an organisation to maintain the phony

23 hour 56 minute 04 second value assigned to axial rotation through 360 degrees -

formatting link
Groupthink !,you have no idea just how bad it is,none !.

Reply to
oriel36

collaboration

That's not entirely true. For many, many readers, Usenet is accessed through Google Groups, formerly Deja Vu, so the lines are not very clearly drawn anymore. At least one participant in this thread is posting via Google, FWIW.

Which is why "the WWW" may be correct after all because it can never be mistaken for a company internet. This can happen when someone starts a sentence with the word and the upper/lower case distinction is lost. "Internet readers," for example, could be either those of some company, organization or other "network of computers" or they could be people reading information from the WWW. (-:

But the parts of Usenet that are accessed via a WWW front end are significant and seem to be growing. I do all my searching through Google Groups, I suspect many others do, too. On the other hand, I hate it for posting. Many don't, though.

Bobby G.

Reply to
Robert Green

"tomcee" wrote in message news: snipped-for-privacy@j28g2000hsj.googlegroups.com...

My reply is going to infuriate everyone because of its horrendous complexity, but I thought I would brush up on my vector algebra and solve this problem by...

(1) considering the earth's orbit as an ellipse (2) using Kepler's "equal area for equal time" rule of orbit speed (3) general mapping of 3D coordinates from one basis (Earth's lat/lon grid) to another (Earth's orbital plane 23.5 degrees oblique) (4) the astronomer's publishing of GMT for perihelion and summer solstice (5) looking iteratively for when the vector from the sun to the observer is basically perpendicular to the vector from the earth's center to the observer

Well, after working almost a month I've got a 'C' program that seems to get within 6 minutes of the U.S. Navy's published times. I actually learned a lot about vector operations. For now I'm going to put this thing to rest, but here's the code...

=============================================================================================================

/* SUNSET.C a program to calculate the time of sunset for any given latitude and longitude, year, and day of the year. It uses vector algebra as much as possible.

Sunset occurs when a line from the sun (considered a point source) runs tangent to the earth and touches earth exactly at the point of the observer's latitude and longitude. Consider this to basically be a problem in 3D coordinate space using rectangular coordinates (although polar coordinates are used initially to compute the earth's orbit).

The 3D coordinate space has the sun at the origin. The earth's orbit is an ellipse in the X-Y plane with the sun at one of the foci. When the earth is at perihelion, or closest to the sun, it is on the positive X-axis with Y=0.

We need two equations. One will determine the earth's center as a vector from the sun as a function of time. Call it the 'sun-earth' vector. The second will determine the distance and direction from the center of the earth to the observer's position on the surface as a function of time. Call it the 'earth-observer' vector. The sum of these two vectors will be a vector that goes from the sun to the observer's position on the surface of the earth. This third, computed, vector is called the 'sun-observer' vector. When the 'sun-observer' vector is perpendicular to the 'earth-observer' vector then the sun is sitting on the horizon and it's either sunrise or sunset. Perpendicular is tested for by taking the dot-product of the two vectors 'earth-observer' and 'sun-observer' and testing for zero.

CALCULATING THE sun-earth VECTOR AS A FUNCTION OF TIME t

Using polar coordinates with the sun at the origin and zero degrees being perihelion Kepler devised the following two equations...

(1) M = 2 * PI * t / T

where 'M' is the "mean" anomaly, 't' is the time since perihelion, and 'T' is the orbital period

(2) M = E - e * sin (E)

where 'E' is the "eccentric" anomaly, and 'e' is the eccentricity of the ellipse

The goal here is to first calculate 'M' from 't'. 'T' is 365 days 5 hours

49 minutes, or 31556940 seconds. Then we use the second equation to solve for 'E'. Unfortunately, equation (2) cannot be solved via algebra. But a formula involving an infinite series will get it accurately enough with about three terms...

E = M + (e - 1/8 * e^3) * sin (M) + 1/2 * e^2 * sin (2 * M) + 3/8 * e^3

  • sin(3 * M) + ...

Once we get 'E' we substitute this value into the following equation to get 'v' (angle of the earth from the sun)...

(3) v = arctan ( sqrt ((1 + e) / (1 - e)) * tan ( E / 2) ) * 2

where 'v' is the polar coordinate angle (from the sun) for the earth at time 't'

Then we simply plug 'v' into the equation for an ellipse (in polar coordinates) to get 'r' (the distance from the sun)...

(4) r = a * (1 - e^2) / (1 + e * cos (v) )

where 'a' is the semi-major axis

A final conversion of 'v' and 'r' into cartesian coordinates is made by

x = r * cos ( v ) y = r * sin ( v )

VISUALIZING THE EARTH'S SPHERICAL SHAPE, AXIS OF ROTATION, AND SEASON-CREATING TILT OF THE AXIS

Next we have to compute the vector 'earth_observer' based on any time 't'. This vector represents the direction and distance of travel from the center of the earth to the observer's location on the surface. This 'earth_observer' vector, expressed in the basis unit vectors of our earth-orbit coordinate system, is computed based on three new factors...

(1) Latitude and Longitude

The observer is on the surface of a sphere with an approximately 4000 mile radius. Using the observer's latitude and longitude will allow us to compute the surface coordinates and, thus, a basic 'observer' vector from the earth's center to the observer. To simplify this calculation we will invoke a new, secondary, coordinate system aligned with the latitude and longitude grid. The X-axis of this new system starts from the earth's center and leaves the earth where the prime meridian intersects with the equator. The Z-axis of this new system is the axis of rotation of the earth and starts at the earth's center and leaves the earth through the north pole. The Y-axis of this new system is now determined by the other two axes. It starts at the earth's center and leaves the earth where the 90 degree east longitude meridian intersects the equator. Using this secondary coordinate system we compute the observer's location with the three equations...

Z= 4000*sin(lat) Y=(4000*cos(lat))*sin(lon) X=(4000*cos(lat))*cos(lon)

Notice that the familiar notation of east and west longitude designation has been replaced with the mathematical requirement that east longitude is used as-is (a positive number from 0 to 180) but west longitude is negated in sign (so 0 to 180 west longitude becomes 0 to -180 degrees).

(2) Elapsed Time

The earth rotates on its own axis approximately every 23 hours 56 minutes 4 seconds (a sidereal day). So even though the observer is fixed in geographic latitude and longitude his effective longitude increases by 360 degrees every 23h56m4s. Therefore, an incremented longitude computed from the time 't' is added to the basic longitude specified in factor (1) to give a correct computation of the 'earth_observer' vector including both factors (1) and (2). At this point the 'earth_observer' vector is still relative to the secondary coordinate system. The equations now become...

Z= 4000*sin(lat) Y=(4000*cos(lat))*sin(lon+t/SIDEREAL_DAY*360) X=(4000*cos(lat))*cos(lon+t/SIDEREAL_DAY*360)

(3) Coordinate Conversion from Secondary to Primary Coordinate System for Earth's Axial Tilt

Because the earth's axis of rotation is "tilted" at an angle of 23.5 degrees with respect to the orbital plane the basic calculation of the 'earth_observer' vector done by using factors (1) and (2) with respect to a secondary coordinate system based on latitude and longitude will have to be converted to a vector based on the original coordinate system that was used to describe the earth's elliptical orbit before we can use it in an algebraic solution for the time of sunset.

Returning to the original coordinate system the earth's north pole is above the X-Y plane, always in positive Z space and "roughly" near the top of the sphere that is the earth. Due to the earth's tilt a line from the earth's center through the north pole will always make an angle of 23.45 degrees with the Z-axis of the original coordinate system. The direction of this line in the original coordinate system is called the 'tilt' vector. It is a constant vector and does not change over time. It never changes its direction. Its specific X, Y, and Z components can only be determined by researching astronomers' analysis of earth's orbit. This 'tilt' vector the astronomers give us is the Z-axis of our secondary coordinate system. What are the X and Y axes? Astronomers will also have to give us those since they determine the orientation of the prime meridian to the original coordinate system at time t=0. Once we have all three of these secondary coordinate system axes defined *as components of the original coordinate system* then we use the transformation that follows for any coordinate system conversion. Thus, the astronomers give us three vectors aligned with the axes of our secondary coordinate system but using the original coordinate system's basis vectors. We call them 'tilt_x', 'tilt_y', and 'tilt_z'.

'tilt_z' is calulated by examining the earth's axis of rotation at the time of summer solstice. 'tilt_x' is also computed by noting where the prime meridian is positioned at the time of summer solstice and then "unwinding" it around the earth's axis of rotation back to the time t=0. 'tilt_y' is simply the cross product of 'tilt_z' and 'tilt_x'. The rotation of the initial 'tilt_x' vector constructed from the geometry at summer solstice back to time t=0 is done using Rodrigues' solution for rotating vector X around vector U by angle theta... _ _ _ _ _ _ _ X' = X * cos(theta) + U * (U . X)(1 - cos(theta)) + (U x X) * sin(theta) //dot-prod, cross-prod, respectively

CONVERSION OF COORDINATES BETWEEN TWO COORDINATE SYSTEMS

The coordinate conversion from the secondary system to the original system is accomplished via the general-purpose linear transformation algebraic solution found on the top of page 534 of my VNR book. This solution handles the general case of both a translation of the origin and a rotation of the axes for two distinct coordinate systems. It requires the calculation of "direction cosines" for each of nine angles. Given any vectors aligned with the three axes for the two coordinate systems these nine direction cosines can be computed by using the dot-product relationship of two vectors. Because the dot-product of two vectors is the product of the magnitudes of both vectors multiplied by the cosine of the angle between them the cosine itself is the dot-product divided by the product of the magnitudes of both vectors. The calculation of the nine direction cosines follows:

a11 = dot-product(unit_x,tilt_x) / ( |unit_x|*|tilt_x| ) a12 = dot-product(unit_x,tilt_y) / ( |unit_x|*|tilt_y| ) a13 = dot-product(unit_x,tilt_z) / ( |unit_x|*|tilt_z| ) a21 = dot-product(unit_y,tilt_x) / ( |unit_y|*|tilt_x| ) a22 = dot-product(unit_y,tilt_y) / ( |unit_y|*|tilt_y| ) a23 = dot-product(unit_y,tilt_z) / ( |unit_y|*|tilt_z| ) a31 = dot-product(unit_z,tilt_x) / ( |unit_z|*|tilt_x| ) a32 = dot-product(unit_z,tilt_y) / ( |unit_z|*|tilt_y| ) a33 = dot-product(unit_z,tilt_z) / ( |unit_z|*|tilt_z| )

The direction cosines are the 'Amn' coefficients in the following equations. The translational distance between the origins of the two coordinate systems are the 'An' constants.

The equations for converting a coordinate in the 'prime' coordinate system to a coordinate in the 'non-prime' coordinate system are on the left-hand side. The equations for converting a coordinate in the 'non-prime' coordinate system to a coordinate in the 'prime' coordinate system are on the right-hand side.

x = a1 + a11 * x' + a12 * y' + a13 * z' | x' = a11 * (x - a1) + a21 * (y - a2) + a31 * (z - a3) y = a2 + a21 * x' + a22 * y' + a23 * z' | y' = a12 * (x - a1) + a22 * (y - a3) + a32 * (z - a3) z = a3 + a31 * x' + a32 * y' + a33 * z' | z' = a13 * (x - a1) + a23 * (y - a3) + a33 * (z - a3)

In both sets of equations a1, a2, and a3 are the coordinates (x, y, and z) of the origin of the 'prime' system with respect to the 'non-prime' system. That is, the origin point {0', 0', 0'} of the 'prime' system is at location {a1, a2, a3} in the 'non-prime' system.

In both sets of equations the 'Amn' direction cosines refer to the cosine of the angle between axis 'm' of the 'non-prime' system and axis 'n' of the 'prime' system (where m=1 is the X-axis of the 'non-prime' system, m=2 is the Y-axis of the 'non-prime' system, and m=3 is the Z-axis of the 'non-prime' system; where n=1 is the X-axis of the 'prime' system, n=2 is the Y-axis of the 'prime' system, and n=3 is the Z-axis of the 'prime' system). Notice that because of the symmetry of the cosine function about the zero angle it makes no difference whether the angle is rotated through according to a right-handed orientation or a left-handed orientation since cos(theta)=cos(360-theta). However, each of the nine angles rotated through must all be determined by choosing representative vectors of the axes of each coordinate system that have the same orientation to those axes with regard to positive (or negative) direction.

Since we are dealing with vectors and not actual coordinates there is no translation of the origin of our two systems to be considered. a1, a2, and a3 are all zero. We will have coordinates in the secondary system that need to be converted to the primary system and the equations on the left will be used.

*/ #include #include #include #include "VCTRSUPPORT.h" //SOME USEFUL CONSTANTS #define PI 3.141592653589793 #define SIDEREAL_DAY (23*3600.0+56*60.0+4) //Number of seconds in a sidereal day #define SUN_RISE_SET_ANGLE 89.167 //Angle between 'sun_earth' & 'earth_observer' at sunrise/sunset #define SEMI_MAJOR_AXIS 92955807.0 //Semi-major axis of earth's orbit ellipse double secs_per_orbit=365*86400+6*3600+13*60+53; //Number of seconds in a true orbit (anomalistic) of the sun double e=0.0167; //Earth's elliptical orbit eccentricity #define ECCENTRICITY 0.0167 //Earth's elliptical orbit eccentricity double a=92955807.0; //Semi-major axis of earth's orbit ellipse double obs_lat=40.267; //Observer's latitude (positive is northern hemispere, negative is southern hemisphere) double obs_lon=-80.117; //Observer's longitude (positive is east of Greenwich, negative is west of Greenwich) //double obs_lat=0.0; //Observer's latitude (positive is northern hemispere, negative is southern hemisphere) //double obs_lon=0.0; //Observer's longitude (positive is east of Greenwich, negative is west of Greenwich) typedef struct { int yr; //2008, 2009, 2010, etc int mo; //1=JAN, 2=FEB, etc int da; //1-31 int hh; //0-23 int mm; //0-59 int ss; //0-59 int tzone; //0=GMT -5=EST, etc } datim; // YYYY MM DD HH MM SS Z datim perihelion={2008, 1, 3, 0, 0, 0,0}; //Date/time (GMT) of perihelion (earth closest to sun, t=0) datim sp_equinox={2008, 3,20, 5,48, 0,0}; //Date/time (GMT) of spring equinox (sun_earth perpend to tilt_z) datim s_solstice={2008, 6,20,23,59, 0,0}; //Date/time (GMT) of summer solstice (tilt_z leans toward sun) datim aphelion ={2008, 7, 4, 8, 0, 0,0}; //Date/time (GMT) of aphelion (earth farthest from sun) datim fa_equinox={2008, 9,22,15,44, 0,0}; //Date/time (GMT) of fall equinox (sun_earth perpend to tilt_z again) datim w_solstice={2008,12,21,12, 4, 0,0}; //Date/time (GMT) of winter solstice (tilt_z leans away from sun) datim current_date; POINT sun={0,0,0}; //Fixed position of the sun def'd in *primary coords*. It never moves. //The three vectors for the secondary coord system (described in primary coord system components) VECTOR tilt_z; //Z-axis of secondary coord syst def'd in *primary coords* (earth's rotation axis) VECTOR tilt_x; //X-axis of secondary coord syst def'd in *primary coords* (prime meridian at t=0) VECTOR tilt_y; //Y-axis of secondary coord syst def'd in *primary coords* (90 degrees east longitude) //Three unit vectors described in the primary coord system used for computing direction cosines VECTOR unit_x={1,0,0}; VECTOR unit_y={0,1,0}; VECTOR unit_z={0,0,1}; //The nine direction cosines double a11,a12,a13,a21,a22,a23,a31,a32,a33; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - //Function prototypes int time_to_date(int t,datim *d); //Not coded yet. Not needed. int date_to_time(datim *d,long *t); int adjust_date (datim *dt,long time); int calc_angle (datim *dt,double *angle); int print_vec (VECTOR *v); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - int main() { int it; //Time 't' as an integer double t; //Time in seconds from initial position of earth double M; //The "mean" anomaly angle double E; //The "eccentric" anomaly angle double v; //Angle, in radians, of earth in its orbit from its initial position double r; //Polar coordinate 'radius' of earth VECTOR sun_earth; //Vector from sun to earth's center VECTOR normal; //Computed vector normal to orbital plane through the sun VECTOR earth_observer; //Vector from earth's center to the observer at given latitude, longitude, and time POINT obs; //Coordinates of observer's point (after time rotations) in *secondary coordinates* VECTOR obs_vec; //Vector from earth's center to observer's point on surface in *secondary coordinates* double lat,lon; VECTOR sun_observer; //Vector from sun to the observer's tangent point double dp,angle; double dangle; long delta_t; double temp; VECTOR tempu,tempx,tempv1,tempv2; double theta; /* OK, let's get started! First, calculate the constant 'tilt_z' vector (the earth's axis of rotation) in the primary coordinate system based on the fact that at summer solstice the axis of the earth (which always forms an angle of 23.45 degrees with the normal to the orbital plane) is, at that moment, pointing *directly* at the sun. Another way to put it is that the normal vector to the orbital plane, passing through the sun, will intersect the earth's extended axis at some point in the positive Z direction. Thus, a right triangle is formed by the earth, the sun, and that point of intersection in positive Z space. (This condition also happens at winter solstice, but then the intersection is in negative Z space.) The 'sun_earth' vector at time t=summer_solstice is the short leg of this right triangle. The normal vector to the earth's orbital plane and passing through the sun is the long leg of this triangle. The hypotenuse is the 'tilt_z' vector for the earth. The tangent of 23.45 degrees (0.4337751161) is the ratio of the 'sun_earth' vector's length to the length of the normal vector. So the length of the normal vector is computed by dividing the length of the 'sun_earth' vector by the tangent of 23.45 degrees.

*/ if (date_to_time(&s_solstice,&it)) { //Convert summer solstice date/time to seconds since perihelion printf("s_solstice conversion error to basic time\\n"); goto bad_exit; } t=it; //Make it floating point // printf("time (t) of summer solstice is %.1f\\n",t); //Now solve for the X and Y coordinates of the earth at summer solstice to get 'sun_earth' vector M=2*PI*t/secs_per_orbit; //Compute Kepler's 'Mean' anomaly E=M //Compute Kepler's 'Eccentric' anomaly (approximately) +(e-1.0/8.0*pow(e,3.0))*sin(1.0*M) //using the first three terms of an infinite series + 1.0/2.0*pow(e,2.0) *sin(2.0*M) // " + 3.0/8.0*pow(e,3.0) *sin(3.0*M); // " v=atan(sqrt((1.0+e)/(1.0-e))*tan(E/2.0))*2.0; //Use 'Eccentric' anomaly to solve for polar coord angle from sun r=a*(1.0-pow(e,2.0))/(1.0+e*cos(v)); //Equation for ellipse gives radius from polar coord angle sun_earth.x=r*cos(v); //Convert polar coord angle and radius to cartesian coords sun_earth.y=r*sin(v); // " sun_earth.z=0.0; // " // printf("sun_earth="); print_vec(&sun_earth); printf("\\n"); //OK, now have 'sun_earth' vector. Solve for normal vector's length from right triangle relationship. normal.x=0.0; //Normal vector has no X component normal.y=0.0; //Normal vector has no Y component normal.z=vector_length(&sun_earth)/tan(23.45*PI/180); //Z length is 'sun_earth' length divided by tan(23.45) // printf("normal(+Z)="); print_vec(&normal); printf("\\n"); //The 'tilt_z' vector is the sum of the 'sun_earth' vector (negated) and the 'normal' vector tilt_z.x=-sun_earth.x+normal.x; //Add 'em tilt_z.y=-sun_earth.y+normal.y; // " tilt_z.z=-sun_earth.z+normal.z; // " // printf("tilt_z="); print_vec(&tilt_z); printf("\\n"); /* OK, now have 'tilt_z' vector described in primary coordinates. We now need the 'tilt_x' and 'tilt_y' vectors. These are both perpendicular to the 'tilt_z' vector and the 3 of them are the axes for the secondary coordinate system. 'tilt_x' points from the earth's center through the earth's equator at the longitude of the prime meridian at time t=0. It will also be constant, not changing as the earth orbits or rotates. 'tilt_x' will tell us the exact orientation of the earth in its rotation about its axis at t=0. 'tilt_y' is nothing more than the vector from the earth's center through the equator at longitude east 90 degrees. It will be calculated via a cross product of 'tilt_z' and 'tilt_x' once 'tilt_x' has been ascertained.

Working with the summer solstice again, this time let's make a right triangle using the 'sun_earth' vector as the long leg. The normal vector to the earth's orbital plane going through the sun and going in the *negative* Z direction is the short leg. The hypotenuse is the 'tilt_x' vector at

12:00AM at the beginning of the date of summer solstice. If we then back out all the rotations of this 'tilt_x' vector that have happened since the date/time of perihelion then we will have the correct 'tilt_x' vector value we want. Now the tangent of 23.45 degrees is the ratio of the length of this negative-going normal vector to the 'sun_earth' vector's length. So the length of this negative-going normal vector is the product of the 'sun_earth' vector's length and the tangent of 23.45 degrees. */ normal.x=0.0; normal.y=0.0; normal.z=-vector_length(&sun_earth)*tan(23.45*PI/180); // printf("normal(-Z)="); print_vec(&normal); printf("\\n"); //The 'tilt_x' vector (as a starting point) is the sum of the 'sun_earth' vector and the 'normal' vector (negated) tilt_x.x=sun_earth.x-normal.x; tilt_x.y=sun_earth.y-normal.y; tilt_x.z=sun_earth.z-normal.z; // printf("tilt_x(12:00AM sum_solstice)="); print_vec(&tilt_x); printf("\\n"); //Unwind tilt_x vector to its position at perihelion (find the angle of clockwise rotation needed) current_date.yr=s_solstice.yr; current_date.mo=s_solstice.mo; current_date.da=s_solstice.da; current_date.hh=0; current_date.mm=0; current_date.ss=0; current_date.tzone=0; if (date_to_time(&current_date,&it)) { //Convert 12:00AM on summer solstice to 't' secs since perihelion printf("error converting date to time\\n"); goto bad_exit; } t=it; //Convert time to float // printf("time (t) of *midnight* on day of summer solstice is %.1f\\n",t); theta=-t/SIDEREAL_DAY*2*PI; //Get angle to unwind // printf("angle to unwind=%.1f\\n",theta); //Use rotation of vector around a vector to get the unwound 'tilt_x' vector at t=0 /* The rotation of vector X around *unit* vector U by an angle 'theta' is given by... _ _ _ _ _ _ _ X' = X * cos(theta) + U * (U . X)(1 - cos(theta)) + (U x X) * sin(theta) //dot-prod & cross-prod, respectively */ tempu=tilt_z; //Vector U is our tilt_z (earth's axis of rotation). Make a copy of it. vector_resize(&tempu,1.0); //Make it a unit vector for the following calculations

tempx=tilt_x; //Make copy of X to begin with vector_mpy(&tempx,cos(theta)); //Now have X*cos(theta)

temp=dot_product(&tempu,&tilt_x)*(1.0-cos(theta)); //Now have (U.X)(1-cos(theta)) tempv1=tempu; //Prepare to multiply U by result in previous line vector_mpy(&tempv1,temp); //Now have U*(U.X)(1-cos(theta))

cross_product(&tempu,&tilt_x,&tempv2); //Now have (UxX) vector_mpy(&tempv2,sin(theta)); //Now have (UxX)*sin(theta)

vector_add(&tempv1,&tempv2,&tilt_x); //Now have U*(U.X)(1-cos(theta))+(UxX)*sin(theta) vector_add(&tilt_x,&tempx,&tilt_x); //Now have X*cos(theta)+U*(U.X)(1-cos(theta))+(UxX)*sin(theta)

// printf("tilt_x(final, at t=0)="); print_vec(&tilt_x); printf("\\n"); //Take cross product of 'tilt_z' with 'tilt_x' to generate our 'tilt_y' perpendicular to both cross_product(&tilt_z,&tilt_x,&tilt_y); //Compute the nine direction cosines for secondary to primary axes conversion a11=dot_product(&unit_x,&tilt_x)/(vector_length(&unit_x)*vector_length(&tilt_x)); a12=dot_product(&unit_x,&tilt_y)/(vector_length(&unit_x)*vector_length(&tilt_y)); a13=dot_product(&unit_x,&tilt_z)/(vector_length(&unit_x)*vector_length(&tilt_z)); a21=dot_product(&unit_y,&tilt_x)/(vector_length(&unit_y)*vector_length(&tilt_x)); a22=dot_product(&unit_y,&tilt_y)/(vector_length(&unit_y)*vector_length(&tilt_y)); a23=dot_product(&unit_y,&tilt_z)/(vector_length(&unit_y)*vector_length(&tilt_z)); a31=dot_product(&unit_z,&tilt_x)/(vector_length(&unit_z)*vector_length(&tilt_x)); a32=dot_product(&unit_z,&tilt_y)/(vector_length(&unit_z)*vector_length(&tilt_y)); a33=dot_product(&unit_z,&tilt_z)/(vector_length(&unit_z)*vector_length(&tilt_z));/* OK, now test all our logic by choosing some date/time values and thencalculating the 'sun_earth' vector, the 'earth_observer' vector, the 'sun_observer' vector, and the angle betweenthe 'sun_observer' and 'earth_observer' vectors. When the angle is close to 90 degrees then wehave a sunrise or sunset.*//* current_date.yr=2008; current_date.mo=4; current_date.da=3; current_date.hh=0; current_date.mm=0; current_date.ss=0; current_date.tzone=-5; for (;current_date.hhyr > 2099) goto bad_datim; //Century years notleap unless divisible by 400 if (d->mo < 1 || d->mo > 12) goto bad_datim; //Month in range? max_days=days_in_month[d->mo-1]+((!(d->yr%4) && d->mo==2) ? 1 :

0);//Account for Feb in leap year if (d->da < 1 || d->da > max_days) goto bad_datim; //Day in range? if (d->hh < 0 || d->hh > 23) goto bad_datim; //Hour in range? if (d->mm < 0 || d->mm > 59) goto bad_datim; //Minute inrange? if (d->ss < 0 || d->ss > 59) goto bad_datim; //Second inrange? if (d->tzone < -12 || d->tzone > 11) goto bad_datim; //Time zone inrange?//Calc # seconds perihelion date/time is into its own year cum_d=cum_days[perihelion.mo-1]+((!(perihelion.yr%4) && perihelion.mo>2)? 1 : 0); //Add 1 beyond Feb in leap yr cum_d+=perihelion.da-1;//Add # days into month cum_sec_perihelion=cum_d*86400 //Cum secs totalstarts w/secs in prior days +(perihelion.hh-perihelion.tzone)*3600 //Plus secs inprior hrs of day (adj for tzone) +perihelion.mm*60 //Plus secs in prior mins +perihelion.ss; //Plus secs into current min//Calc # seconds input date/time is into its own year cum_d=cum_days[d->mo-1]+((!(d->yr%4) && d->mo>2) ? 1 : 0); //Add 1 beyondFeb in leap yr cum_d+=d->da-1; //Add # days intomonth cum_sec_input=cum_d*86400 //Cum secs totalstarts w/secs in prior days +(d->hh-d->tzone)*3600 //Plus secs inprior hrs of day (adj for tzone) +d->mm*60 //Plus secs in prior mins +d->ss; //Plus secs into currentmin//Calc # seconds in intervening years starting at beginning of perihelionyear for (cum_sec_intervening=0,y=perihelion.yr;yyr;y++) //For allintervening years cum_sec_intervening+=(365*86400)+(!(y%4) ? 86400 : 0); //Add # secs inyear, accounting for leap *t=cum_sec_intervening-cum_sec_perihelion+cum_sec_input; //Subtperihelion start fract, add input start fract return(0);bad_datim: return(1);}//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - -int adjust_date(datim *dt,long t_sec) {/* Take a date/time in 'dt' and adjust it forward (t_sec positive) orbackward (t_sec negative) and construct a new value for 'dt'. Do not move beyond midnight if movingbackward or 23:59:59 if moving forward. Return 0 as a normal status. Return 1 if a limit for themovement was reached, but otherwise set the value to the limit.*/long adj_hh,adj_mm,adj_ss; adj_hh=t_sec/3600; adj_mm=(t_sec-adj_hh*3600)/60; adj_ss=t_sec-adj_hh*3600-adj_mm*60; dt->ss+=adj_ss; dt->mm+=adj_mm; dt->hh+=adj_hh; if (dt->ss > 59) { dt->ss-=60; dt->mm++; } if (dt->ss < 0) { dt->ss+=60; dt->mm--; } if (dt->mm > 59) { dt->mm-=60; dt->hh++; } if (dt->mm < 0) { dt->mm+=60; dt->hh--; } if (dt->hh

0) { dt->hh=0; dt->mm=0; dt->ss=0; goto bad_exit; } return(0);bad_exit: return(1);}//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - -int calc_angle(datim *dt,double

*angle) {/* Given a date/time in 'dt' and using the observer's position ('obs_lat'and 'obs_lon') compute the angle between the 'earth_observer' vector and the 'sun_observer' vector.Return its value in degrees. Return 0 if no error. Return 1 if an error is found in the input date/time'dt'.*/long it;double t,M,E,v,r,lon,lat,dp;VECTOR sun_earth,obs,earth_observer,sun_observer; if (date_to_time(dt,&it)) { //Convert date/time structure to a time't' in seconds since perihelion printf("error converting date to time\\n"); goto bad_exit; } t=it; //Convert time to float//First calculate sun_earth vector based on the time 't' M=2*PI*t/secs_per_orbit; //CalculateKepler's "Mean" anomaly E=M //ComputeKepler's "Eccentric" anomaly (approximately) +(ECCENTRICITY-1.0/8.0*pow(ECCENTRICITY,3.0))*sin(1.0*M) //using thefirst three terms of an infinite series + 1.0/2.0*pow(ECCENTRICITY,2.0) *sin(2.0*M) // " + 3.0/8.0*pow(ECCENTRICITY,3.0) *sin(3.0*M); // " v=atan(sqrt((1.0+ECCENTRICITY)/(1.0-ECCENTRICITY))*tan(E/2.0))*2.0;//"Eccentric" anomaly gives polar coord angle r=SEMI_MAJOR_AXIS*(1.0-pow(ECCENTRICITY,2.0))/(1.0+ECCENTRICITY*cos(v));//Eq for ellipse gives radius from angle sun_earth.x=r*cos(v); //Convert polar to cartesian sun_earth.y=r*sin(v); // " sun_earth.z=0.0; // "//Second calculate observer's latitude/longitude in secondary coords withelapsed time increasing longitude lon=obs_lon+t/SIDEREAL_DAY*360.0; //Compute longitude with time rotationdone lat=obs_lat ; //Latitude always stays the same obs.z= 4000.0*sin(lat*PI/180.0); //Observer's vectorfrom earth's center (secondary coords) obs.y=(4000.0*cos(lat*PI/180.0))*sin(lon*PI/180.0); // " obs.x=(4000.0*cos(lat*PI/180.0))*cos(lon*PI/180.0); // "//Change vector in secondary system to vector in primary system coords earth_observer.x = a11 * obs.x + a12 * obs.y + a13 * obs.z; //Nowtransform to primary system earth_observer.y = a21 * obs.x + a22 * obs.y + a23 * obs.z; // " earth_observer.z = a31 * obs.x + a32 * obs.y + a33 * obs.z; // "//Vector from sun to observer is sum of vector from sun to earth plusobserver's vector from earth center sun_observer.x=sun_earth.x+earth_observer.x; sun_observer.y=sun_earth.y+earth_observer.y; sun_observer.z=sun_earth.z+earth_observer.z;//The dot-product of the 'earth_observer' vector and 'sun_observer' vectorallows us to calculate the angle dp=dot_product(&sun_observer,&earth_observer); *angle=acos(dp/(vector_length(&sun_observer)*vector_length(&earth_observer))); *angle*=180.0/PI; //Convert radians to degrees return(0);bad_exit: return(1);}//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - -int print_vec(VECTOR *v) { printf("%.1fx %.1fy %.1fz",v->x,v->y,v->z); return(0);}//- - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - -

-======================================================================================================

Reply to
TR Oltrogge

I think the words you are looking for is either 'convoluted' or 'contrived', instead of complicated you repeat the same old falsehoods with no central theme jumping all over the place from Ra/ Dec terns to geocentric ones or using celestial sphere geometry. to justIify heliocentric and axial rotational motions.In lieu of any real astronomical authority in existence to seperate the terms and untangle the contrived mess created in the late 17th century,here are the basic working principles -

Geocentric - The Earth centered solar system (Antiquity to the Copernican discovery)

Many assume an Earth centered Universe however any astronomer worthy of the name would simply look at how the original heliocentric astronomers viewed the precepts of their antecent geocentric counterparts -

"With regard to Venus and Mercury, however, differences of opinion are found. For, these planets do not pass through every elongation from the sun, as the other planets do. Hence Venus and Mercury are located above the sun by some authorities, like Plato's Timaeus [38 D], but below the sun by others, like Ptolemy [Syntaxis, IX, 1] and many of the modems. Al-Bitruji places Venus above the sun, and Mercury below it. " COPERNICUS

formatting link
Heliocentric system (Copernicus to present) - the orbital motion of the Earth between Venus and Mars by using orbital motion to resolve the observed behavior of the other planets ,their periodic times to resolve the placement of the Earth between the 225 day orbital period of Venus and the 687 day period of Mars with axial rotation as a means to resolve the observed daily cycle.

Ra/Dec or celestial sphere approach (Flamsteed to present) - the practice of using celestial sphere geometry to justify axial and orbital motions of the Earth where the stars have an undefined equidistant geometry to the Earth.It was originally created to resolve determination of terrstrial longitudes for marine navigational purposes by Flamsteed but was extended by Newton to heliocentric reasoning.

Many are inclined to use bluster which others mistake for confidence or authority however there is always hope that there are actually genuine people who are not afraid of the unfamiliarity with the difference between geocentric and heliocentric reasoning as it was orginally understood,the actual principles which keep clocks in sync with the daily cycle at 24 hours/360 degrees,the observational convenience of the Ra/Dec system,the actual principles which distinguish timekeeping and structural astronomy from the observational convenience ,the modifications required to bring perceptions up to speed with modern observations and many,many more realistic approaches to honor human reasoning.

but I thought I would brush up on my vector algebra and solve

=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=AD=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D

=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _ =A0 _

Reply to
oriel36

Could you post it again without losing all the line breaks in the second half?

Thanks

Reply to
Andrew Gabriel

In the almost three weeks since I posted this 'C' program I cleaned it up a little but kept the same logic. I moved some code into subroutines since it was used several times from different places. Also, my first posting didn't include the source for the cross-product and dot-product of vectors since these were pulled from a VCTRSUPPORT.h file I had previously developed. I am still at a loss to figure out why I've got about 6 minutes of error (seems to vary from about 5 to 7 depending on the time of year) but am not willing "fudge" any of my geometrically accurate analysis to eliminate it.

Anyway, I've moved the tidied source code for the SUNSET program and its support VCTRSUPPORT header to my web site where you can download them. I wish I had a nice drawing to show the geometry that I'm solving, but it's all in my head! If you have questions I can give references to the web sites that showed me how to solve Kepler's ellipitical orbit calculation. My source files are located at...

mysite.verizon.net/troltrogge/sunset.c

and

mysite.verizon.net/troltrogge/VCTRSUPPORT.h

Reply to
TR Oltrogge

Or, posted right here, hopefully with decent line breaks...

/* SUNSET.C a program to calculate the time of sunset for any given latitude and longitude, year, and day of the year. It uses vector algebra as much as possible.

Sunset occurs when a line from the sun (considered a point source) runs tangent to the earth and touches earth exactly at the point of the observer's latitude and longitude. Consider this to basically be a problem in 3D coordinate space using rectangular coordinates (although polar coordinates are used initially to compute the earth's orbit).

The 3D coordinate space has the sun at the origin. The earth's orbit is an ellipse in the X-Y plane with the sun at one of the foci. When the earth is at perihelion, or closest to the sun, it is on the positive X-axis with Y=0.

We need two equations. One will determine the earth's center as a vector from the sun as a function of time. Call it the 'sun-earth' vector. The second will determine the distance and direction from the center of the earth to the observer's position on the surface as a function of time. Call it the 'earth-observer' vector. The sum of these two vectors will be a vector that goes from the sun to the observer's position on the surface of the earth. This third, computed, vector is called the 'sun-observer' vector. When the 'sun-observer' vector is perpendicular to the 'earth-observer' vector then the sun is sitting on the horizon and it's either sunrise or sunset. Perpendicular is tested for by taking the dot-product of the two vectors 'earth-observer' and 'sun-observer' and testing for zero.

CALCULATING THE sun-earth VECTOR AS A FUNCTION OF TIME t

Using polar coordinates with the sun at the origin and zero degrees being perihelion Kepler devised the following two equations in solving the mathematics of elliptical orbits...

(1) M = 2 * PI * t / T

where 'M' is the "mean" anomaly, 't' is the time since perihelion, and 'T' is the orbital period

(2) M = E - e * sin (E)

where 'E' is the "eccentric" anomaly, and 'e' is the eccentricity of the ellipse

The goal here is to first calculate 'M' from 't'. 'T' is 365 days 6 hours

13 minutes and 53 seconds, the time in an "anomalistic" orbit (perihelion to perihelion). This is 31558433 seconds. Then we use the second equation to solve for 'E' since we now know 'M' and 'e'. Unfortunately, equation (2) cannot be solved via algebra. But a formula involving an infinite series will get it accurately enough with about three terms...

E = M + (e - 1/8 * e^3) * sin (M) + 1/2 * e^2 * sin (2 * M) + 3/8 * e^3 * sin(3 * M) + ...

Once we get 'E' we substitute this value into the following equation to get 'v' (angle of the earth from the sun, where v=0 at perihelion)...

(3) v = arctan ( sqrt ((1 + e) / (1 - e)) * tan ( E / 2) ) * 2

where 'v' is the polar coordinate angle (from the sun) for the earth at time 't'

Then we simply plug 'v' into the equation for an ellipse (in polar coordinates) to get 'r' (the distance from the sun)...

(4) r = a * (1 - e^2) / (1 + e * cos (v) )

where 'a' is the semi-major axis

A final conversion of 'v' and 'r' into cartesian coordinates is made by

x = r * cos ( v ) y = r * sin ( v )

The 'sun-earth' X coordinate and Y coordinate are now known as a function of time 't'. The Z coordinate is always zero since the orbit is in the X-Y plane.

VISUALIZING THE EARTH'S SPHERICAL SHAPE, AXIS OF ROTATION, AND SEASON-CREATING TILT OF THE AXIS

Next we have to compute the vector 'earth_observer' based on any time 't'. This vector represents the direction and distance of travel from the center of the earth to the observer's location on the surface. This 'earth_observer' vector, expressed in the basis unit vectors of our earth-orbit coordinate system, is computed based on three new factors...

(1) Latitude and Longitude

The observer is on the surface of a sphere with an approximately 4000 mile radius. Using the observer's latitude and longitude will allow us to compute the surface coordinates and, thus, a basic 'observer' vector from the earth's center to the observer. To simplify this calculation we will invoke a new, secondary, coordinate system aligned with the latitude and longitude grid. The X-axis of this new system starts from the earth's center and leaves the earth where the prime meridian intersects with the equator. The Z-axis of this new system is the axis of rotation of the earth and starts at the earth's center and leaves the earth through the north pole. The Y-axis of this new system is now determined by the other two axes. It starts at the earth's center and leaves the earth where the

90 degree east longitude meridian intersects the equator. Using this secondary coordinate system we compute the observer's location with the three equations...

Z= 4000*sin(lat) Y=(4000*cos(lat))*sin(lon) X=(4000*cos(lat))*cos(lon)

Notice that the familiar notation of east and west longitude designation has been replaced with the mathematical requirement that east longitude is used as-is (a positive number from 0 to 180) but west longitude is negated in sign (so 0 to 180 west longitude becomes 0 to -180 degrees).

(2) Elapsed Time

The earth rotates on its own axis approximately every 23 hours 56 minutes 4 seconds (a sidereal day). So even though the observer is fixed in geographic latitude and longitude his effective longitude increases by 360 degrees every 23h56m4s. Therefore, an incremented longitude computed from the time 't' is added to the basic longitude specified in factor (1) to give a correct computation of the 'earth_observer' vector including both factors (1) and (2). At this point the 'earth_observer' vector is still relative to the secondary coordinate system. The equations now become...

Z= 4000*sin(lat) Y=(4000*cos(lat))*sin(lon+t/SIDEREAL_DAY*360) X=(4000*cos(lat))*cos(lon+t/SIDEREAL_DAY*360)

(3) Coordinate Conversion from Secondary to Primary Coordinate System for Earth's Axial Tilt

Because the earth's axis of rotation is "tilted" at an angle of 23.5 degrees with respect to the orbital plane the basic calculation of the 'earth_observer' vector done by using factors (1) and (2) with respect to a secondary coordinate system based on latitude and longitude will have to be converted to a vector based on the original coordinate system that was used to describe the earth's elliptical orbit before we can use it in an algebraic solution for the time of sunset.

Returning to the original coordinate system the earth's north pole is above the X-Y plane, always in positive Z space and "roughly" near the top of the sphere that is the earth. Due to the earth's tilt a line from the earth's center through the north pole will always make an angle of 23.5 degrees with the Z-axis of the original coordinate system. The direction of this line in the original coordinate system is called the 'tilt' vector. It is a constant vector and does not change over time. It never changes its direction. Its specific X, Y, and Z components can only be determined by researching astronomers' analysis of earth's orbit. This 'tilt' vector the astronomers give us is the Z-axis of our secondary coordinate system. What are the X and Y axes? Astronomers will also have to give us those since they determine the orientation of the prime meridian to the original coordinate system at time t=0. Once we have all three of these secondary coordinate system axes defined *as components of the original coordinate system* then we use the transformation that follows for any coordinate system conversion. Thus, the astronomers give us three vectors aligned with the axes of our secondary coordinate system but using the original coordinate system's basis vectors. We call them 'tilt_x', 'tilt_y', and 'tilt_z'.

'tilt_z' is calulated by examining the earth's axis of rotation at the time of summer solstice. 'tilt_x' is also computed by noting where the prime meridian is positioned at the time of summer solstice and then "unwinding" it around the earth's axis of rotation back to the time t=0. 'tilt_y' is simply the cross product of 'tilt_z' and 'tilt_x'. The rotation of the initial 'tilt_x' vector constructed from the geometry at summer solstice back to time t=0 is done using Rodrigues' solution for rotating vector X around vector U by angle theta... _ _ _ _ _ _ _ X' = X * cos(theta) + U * (U . X)(1 - cos(theta)) + (U x X) * sin(theta)

CONVERSION OF COORDINATES BETWEEN TWO COORDINATE SYSTEMS

The coordinate conversion from the secondary system to the original system is accomplished via the general-purpose linear transformation algebraic solution found on the top of page 534 of my VNR book. This solution handles the general case of both a translation of the origin and a rotation of the axes for two distinct coordinate systems. It requires the calculation of "direction cosines" for each of nine angles. Given any vectors aligned with the three axes for the two coordinate systems these nine direction cosines can be computed by using the dot-product relationship of two vectors. Because the dot-product of two vectors is the product of the magnitudes of both vectors multiplied by the cosine of the angle between them the cosine itself is the dot-product divided by the product of the magnitudes of both vectors. The calculation of the nine direction cosines follows:

a11 = dot-product(unit_x,tilt_x) / ( |unit_x|*|tilt_x| ) a12 = dot-product(unit_x,tilt_y) / ( |unit_x|*|tilt_y| ) a13 = dot-product(unit_x,tilt_z) / ( |unit_x|*|tilt_z| ) a21 = dot-product(unit_y,tilt_x) / ( |unit_y|*|tilt_x| ) a22 = dot-product(unit_y,tilt_y) / ( |unit_y|*|tilt_y| ) a23 = dot-product(unit_y,tilt_z) / ( |unit_y|*|tilt_z| ) a31 = dot-product(unit_z,tilt_x) / ( |unit_z|*|tilt_x| ) a32 = dot-product(unit_z,tilt_y) / ( |unit_z|*|tilt_y| ) a33 = dot-product(unit_z,tilt_z) / ( |unit_z|*|tilt_z| )

The direction cosines are the 'Amn' coefficients in the following equations. The translational distance between the origins of the two coordinate systems are the 'An' constants.

The equations for converting a coordinate in the 'prime' coordinate system to a coordinate in the 'non-prime' coordinate system are on the left-hand side. The equations for converting a coordinate in the 'non-prime' coordinate system to a coordinate in the 'prime' coordinate system are on the right-hand side.

x = a1 + a11 * x' + a12 * y' + a13 * z' | x' = a11 * (x - a1) + a21 * (y - a2) + a31 * (z - a3) y = a2 + a21 * x' + a22 * y' + a23 * z' | y' = a12 * (x - a1) + a22 * (y - a3) + a32 * (z - a3) z = a3 + a31 * x' + a32 * y' + a33 * z' | z' = a13 * (x - a1) + a23 * (y - a3) + a33 * (z - a3)

In both sets of equations a1, a2, and a3 are the coordinates (x, y, and z) of the origin of the 'prime' system with respect to the 'non-prime' system. That is, the origin point {0', 0', 0'} of the 'prime' system is at location {a1, a2, a3} in the 'non-prime' system.

In both sets of equations the 'Amn' direction cosines refer to the cosine of the angle between axis 'm' of the 'non-prime' system and axis 'n' of the 'prime' system (where m=1 is the X-axis of the 'non-prime' system, m=2 is the Y-axis of the 'non-prime' system, and m=3 is the Z-axis of the 'non-prime' system; where n=1 is the X-axis of the 'prime' system, n=2 is the Y-axis of the 'prime' system, and n=3 is the Z-axis of the 'prime' system). Notice that because of the symmetry of the cosine function about the zero angle it makes no difference whether the angle is rotated through according to a right-handed orientation or a left-handed orientation since cos(theta)=cos(360-theta). However, each of the nine angles rotated through must all be determined by choosing representative vectors of the axes of each coordinate system that have the same orientation to those axes with regard to positive (or negative) direction.

Since we are dealing with vectors and not actual coordinates there is no translation of the origin of our two systems to be considered. a1, a2, and a3 are all zero. We will have coordinates in the secondary system that need to be converted to the primary system and the equations on the left will be used.

*/ #include #include #include #include "VCTRSUPPORT.h" //SOME USEFUL CONSTANTS #define PI 3.141592653589793 #define SIDEREAL_DAY (23*3600.0+56*60.0+4) //Number of seconds in a sidereal day #define SUN_RISE_SET_ANGLE 89.167 //Angle between 'sun_earth' & 'earth_observer' at sunrise/sunset #define SEMI_MAJOR_AXIS 92955807.0 //Semi-major axis of earth's orbit ellipse #define SECONDS_PER_ORBIT (365*86400+6*3600+13*60+53) //Number of seconds in a true orbit (anomalistic) of the sun #define ECCENTRICITY 0.0167 //Earth's elliptical orbit eccentricity #define AXIS_TILT 23.45 //Number of degrees of tilt in the earth's axis double obs_lat=40.267; //Observer's latitude (positive is northern hemispere, negative is southern hemisphere) double obs_lon=-80.117; //Observer's longitude (positive is east of Greenwich, negative is west of Greenwich) //double obs_lat=45.0; //Observer's latitude (positive is northern hemispere, negative is southern hemisphere) //double obs_lon=-120.0; //Observer's longitude (positive is east of Greenwich, negative is west of Greenwich) typedef struct { int yr; //2008, 2009, 2010, etc int mo; //1=JAN, 2=FEB, etc int da; //1-31 int hh; //0-23 int mm; //0-59 int ss; //0-59 int tzone; //0=GMT -5=EST, etc } datim; // YYYY MM DD HH MM SS Z datim perihelion={2008, 1, 3, 0, 0, 0,0}; //Date/time (GMT) of perihelion (earth closest to sun, our t=0) datim s_solstice={2008, 6,20,23,59, 0,0}; //Date/time (GMT) of summer solstice (tilt_z leans toward sun) //Other interesting, but unneeded, dates //datim sp_equinox={2008, 3,20, 5,48, 0,0}; //Date/time (GMT) of spring equinox (sun_earth perpend to tilt_z) //datim aphelion ={2008, 7, 4, 8, 0, 0,0}; //Date/time (GMT) of aphelion (earth farthest from sun) //datim fa_equinox={2008, 9,22,15,44, 0,0}; //Date/time (GMT) of fall equinox (sun_earth perpend to tilt_z again) //datim w_solstice={2008,12,21,12, 4, 0,0}; //Date/time (GMT) of winter solstice (tilt_z leans away from sun) //The three vectors for the secondary coord system (described in primary coord system components) VECTOR tilt_z; //Z-axis of secondary coord syst def'd in *primary coords* (earth's rotation axis) VECTOR tilt_x; //X-axis of secondary coord syst def'd in *primary coords* (prime meridian at t=0) VECTOR tilt_y; //Y-axis of secondary coord syst def'd in *primary coords* (90 degrees east longitude) //Three unit vectors described in the primary coord system used for computing direction cosines VECTOR unit_x={1,0,0}; VECTOR unit_y={0,1,0}; VECTOR unit_z={0,0,1}; //The nine direction cosines double a11,a12,a13,a21,a22,a23,a31,a32,a33; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - //Function prototypes int date_to_time (datim *d,long *t); int adjust_date (datim *dt,long time); int calc_angle (datim *dt,double *angle); int calc_sun_earth_vector (datim *dt,VECTOR *se); int print_vec (VECTOR *v); //Debug support //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - /* Convert a date and time-of-day value to the time elapsed since perihelion (t=0)

*/ int days_in_month[12]={31,28,31,30, 31, 30, 31, 31, 30, 31, 30, 31}; int cum_days[12] ={ 0,31,59,90,120,151,181,212,242,273,303,334}; int date_to_time(datim *d,long *t) { int max_days; long cum_d,cum_sec_perihelion,cum_sec_input,cum_sec_intervening,y; if (d->yr < 1901 || d->yr > 2099) goto bad_datim; //Century years not leap unless divisible by 400 if (d->mo < 1 || d->mo > 12) goto bad_datim; //Month in range? max_days=days_in_month[d->mo-1]+((!(d->yr%4) && d->mo==2) ? 1 : 0); //Account for Feb in leap year if (d->da < 1 || d->da > max_days) goto bad_datim; //Day in range? if (d->hh < 0 || d->hh > 23) goto bad_datim; //Hour in range? if (d->mm < 0 || d->mm > 59) goto bad_datim; //Minute in range? if (d->ss < 0 || d->ss > 59) goto bad_datim; //Second in range? if (d->tzone < -12 || d->tzone > 11) goto bad_datim; //Time zone in range? //Calc # seconds perihelion date/time is into its own year cum_d=cum_days[perihelion.mo-1] //Start with # days in prior months +((!(perihelion.yr%4) && perihelion.mo>2) ? 1 : 0); //Add 1 if beyond Feb in leap yr cum_d+=perihelion.da-1; //Add # days into month cum_sec_perihelion=cum_d*86400 //Cum secs total starts w/secs in prior days +(perihelion.hh-perihelion.tzone)*3600 //Plus secs in prior hrs of day (adj for tzone) +perihelion.mm*60 //Plus secs in prior mins +perihelion.ss; //Plus secs into current min //Calc # seconds input date/time is into its own year cum_d=cum_days[d->mo-1] //Start with # days in prior months +((!(d->yr%4) && d->mo>2) ? 1 : 0); //Add 1 if beyond Feb in leap yr cum_d+=d->da-1; //Add # days into month cum_sec_input=cum_d*86400 //Cum secs total starts w/secs in prior days +(d->hh-d->tzone)*3600 //Plus secs in prior hrs of day (adj for tzone) +d->mm*60 //Plus secs in prior mins +d->ss; //Plus secs into current min //Calc # seconds in intervening years starting at beginning of perihelion year for (cum_sec_intervening=0,y=perihelion.yr;yyr;y++) //For all intervening years cum_sec_intervening+=(365*86400)+(!(y%4) ? 86400 : 0); //Add # secs in year, accounting for leap *t=cum_sec_intervening-cum_sec_perihelion+cum_sec_input; //Subt perihelion start fract, add input start fract return(0); bad_datim: printf("date_to_time: invalid datim structure (%d/%d/%d %d:%d:%d tZone:%d\\n)\\n", d->yr,d->mo,d->da,d->hh,d->mm,d->ss,d->tzone); return(1); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - int main() { int it; //Time 't' as an integer double t; //Time in seconds from initial position of earth VECTOR sun_earth; //Vector from sun to earth's center VECTOR normal; //Computed vector normal to orbital plane through the sun datim current_date; //Work area for constructing dates to be analyzed double angle; long delta_t; double theta; /* OK, let's get started! First, calculate the constant 'tilt_z' vector (the earth's axis of rotation) in the primary coordinate system based on the fact that at summer solstice the axis of the earth is, at that moment, pointing *directly* at the sun. Another way to put it is that the normal vector to the orbital plane, passing through the sun, will intersect the earth's extended axis at some point in the positive Z direction. Thus, a right triangle is formed by the earth, the sun, and that point of intersection in positive Z space. (This condition also happens at winter solstice, but then the intersection is in negative Z space.) The 'sun_earth' vector at time of summer_solstice is the short leg of this right triangle. The normal vector to the earth's orbital plane and passing through the sun is the long leg of this triangle. The hypotenuse is the 'tilt_z' vector for the earth. The tangent of 23.45 degrees (0.4337751161) is the ratio of the 'sun_earth' vector's length to the length of the normal vector. So the length of the normal vector is computed by dividing the length of the 'sun_earth' vector by the tangent of 23.45 degrees.

*/ if (calc_sun_earth_vector(&s_solstice,&sun_earth)) { printf("sunset: cannot use summer solstice date/time\\n"); goto bad_exit; } // printf("sun_earth="); print_vec(&sun_earth); printf("\\n"); //Debug //OK, now have 'sun_earth' vector. Solve for normal vector's length from right triangle relationship. normal.x=0.0; //Normal vector has no X component normal.y=0.0; //Normal vector has no Y component normal.z=vector_length(&sun_earth)/tan(AXIS_TILT*PI/180); //Z length is 'sun_earth' length divided by tan(23.45) // printf("normal="); print_vec(&normal); printf("\\n"); //Debug //The 'tilt_z' vector is the sum of the 'sun_earth' vector (negated) and the 'normal' vector tilt_z.x=-sun_earth.x+normal.x; //Add 'em tilt_z.y=-sun_earth.y+normal.y; // " tilt_z.z=-sun_earth.z+normal.z; // " // printf("tilt_z="); print_vec(&tilt_z); printf("\\n"); //Debug /* OK, now have 'tilt_z' vector described in primary coordinates. We now need the 'tilt_x' and 'tilt_y' vectors. These are both perpendicular to the 'tilt_z' vector and the 3 of them are the axes for the secondary coordinate system. 'tilt_x' points from the earth's center through the earth's equator at the longitude of the prime meridian at time t=0. It will also be constant, not changing as the earth orbits or rotates. 'tilt_x' will tell us the exact orientation of the earth in its rotation about its axis at t=0. 'tilt_y' is nothing more than the vector from the earth's center through the equator at longitude east 90 degrees. It will be calculated via a cross product of 'tilt_z' and 'tilt_x' once 'tilt_x' has been ascertained.

Working with the summer solstice again, this time let's make a right triangle using the 'sun_earth' vector as the long leg. The normal vector to the earth's orbital plane going through the sun and going in the *negative* Z direction is the short leg. The hypotenuse is related to the 'tilt_x' vector since it goes from the earth's center out the equator but at a longitude that is *not* the prime meridian. *If* the prime meridian were aligned exactly with this hypotenuse at the time of summer solstice then it would be exactly noon GMT at the summer solstice. But, instead, it is some other time (GMT) at summer solstice (23:59 for 2008, not 12:00) and so we know the prime meridian is rotated (on the 'tilt_z' axis) an angle of (23:59-12:00)/SIDEREAL_DAY*360) degrees from the orientation of the hypotenuse of our right triangle. But this is all we need to know in order to "back out" all the rotations of the prime meridian (which is the 'tilt_x' vector) that have happened since the date/time of perihelion. Then we will have the correct 'tilt_x' vector value we want. Back to our second right triangle. The tangent of 23.45 degrees is the ratio of the length of the negative-Z-going normal vector to the 'sun_earth' vector's length. So the length of this negative-Z-going normal vector is the product of the 'sun_earth' vector's length and the tangent of 23.45 degrees.

*/ normal.x=0.0; //There's no X component of any vector normal to the orbital plane normal.y=0.0; //Also, no Y component normal.z=-vector_length(&sun_earth)*tan(AXIS_TILT*PI/180); //Z component from right triangle relationship // printf("normal(-Z)="); print_vec(&normal); printf("\\n"); //The 'tilt_x' vector (as a starting point) is the sum of the 'sun_earth' vector and the 'normal' vector (negated) tilt_x.x=sun_earth.x-normal.x; tilt_x.y=sun_earth.y-normal.y; tilt_x.z=sun_earth.z-normal.z; // printf("tilt_x(start)="); print_vec(&tilt_x); printf("\\n"); //The prime meridian is (Time at GMT)/SIDEREAL_DAY*360 degrees counterclockwise from the "midnight" vector it=s_solstice.hh*3600+s_solstice.mm*60+s_solstice.ss; t=it; //Convert time to float theta=t/SIDEREAL_DAY*2*PI; vector_rotate(&tilt_x,&tilt_z,theta); //Rotate small amt to get prime meridian at GMT of summer solstice // printf("tilt_x(at GMT)="); print_vec(&tilt_x); printf("\\n"); //Unwind tilt_x vector to its position at perihelion (find the angle of clockwise rotation needed) if (date_to_time(&s_solstice,&it)) { //Convert summer solstice date/time to secs since perihelion printf("sunset: unable to convert summer solstice date/time to seconds\\n"); goto bad_exit; } t=it; //Convert time to float theta=-t/SIDEREAL_DAY*2*PI; //This is angle to unwind *for the specific meridian* aligned with 'sun-earth' vector vector_rotate(&tilt_x,&tilt_z,theta); //Unwind 'tilt_x' vector until t=0 // printf("tilt_x(final, at t=0)="); print_vec(&tilt_x); printf("\\n"); //Take cross product of 'tilt_z' with 'tilt_x' to generate our 'tilt_y' perpendicular to both cross_product(&tilt_z,&tilt_x,&tilt_y); //Compute the nine direction cosines for secondary to primary axes conversion a11=dot_product(&unit_x,&tilt_x)/(vector_length(&unit_x)*vector_length(&tilt_x)); a12=dot_product(&unit_x,&tilt_y)/(vector_length(&unit_x)*vector_length(&tilt_y)); a13=dot_product(&unit_x,&tilt_z)/(vector_length(&unit_x)*vector_length(&tilt_z)); a21=dot_product(&unit_y,&tilt_x)/(vector_length(&unit_y)*vector_length(&tilt_x)); a22=dot_product(&unit_y,&tilt_y)/(vector_length(&unit_y)*vector_length(&tilt_y)); a23=dot_product(&unit_y,&tilt_z)/(vector_length(&unit_y)*vector_length(&tilt_z)); a31=dot_product(&unit_z,&tilt_x)/(vector_length(&unit_z)*vector_length(&tilt_x)); a32=dot_product(&unit_z,&tilt_y)/(vector_length(&unit_z)*vector_length(&tilt_y)); a33=dot_product(&unit_z,&tilt_z)/(vector_length(&unit_z)*vector_length(&tilt_z)); /* OK, now test all our logic by choosing some date/time values and then calculating the 'sun_earth' vector, the 'earth_observer' vector, the 'sun_observer' vector, and the angle between the 'sun_observer' and 'earth_observer' vectors. When the angle is close to 90 degrees then we have a sunrise or sunset.

Given a date/time determine the time of sunrise and sunset for that date. Do this by iterating times during the date and check that the angle of the sun is close enough to SUN_RISE_SET_ANGLE.

*/ for (current_date.yr=2008;current_date.yrmm+=adj_mm; dt->hh+=adj_hh; if (dt->ss > 59) { dt->ss-=60; dt->mm++; } if (dt->ss < 0) { dt->ss+=60; dt->mm--; } if (dt->mm > 59) { dt->mm-=60; dt->hh++; } if (dt->mm < 0) { dt->mm+=60; dt->hh--; } if (dt->hh > 23) { dt->hh=23; dt->mm=59; dt->ss=59; goto bad_exit; } if (dt->hh < 0) { dt->hh=0; dt->mm=0; dt->ss=0; goto bad_exit; } return(0); bad_exit: return(1); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - int calc_angle(datim *dt,double *angle) { /* Given a date/time in 'dt' and using the observer's position ('obs_lat' and 'obs_lon') compute the angle between the 'earth_observer' vector and the 'sun_observer' vector. Return its value in degrees. Return 0 if no error. Return 1 if an error is found in the input date/time 'dt'.

*/ long it; double t,lon,lat,dp; VECTOR sun_earth; //Vector from sun to earth's center VECTOR earth_observer; //Vector from earth's center to the observer at given latitude, longitude, and time VECTOR obs; //Vector from earth's center to observer's point (after time rotation) in *secondary coords* VECTOR sun_observer; //Vector from sun to the observer's tangent point if (calc_sun_earth_vector(dt,&sun_earth)) { printf("calc_angle: unable to calculate sun_earth vector\\n"); goto bad_exit; } if (date_to_time(dt,&it)) { //Convert date/time structure to a time 't' in seconds since perihelion printf("calc_angle: cannot use input date/time\\n"); goto bad_exit; } t=it; //Convert time to float //Second calculate observer's latitude/longitude in secondary coords with elapsed time increasing longitude lon=obs_lon+t/SIDEREAL_DAY*360.0; //Compute longitude with time rotation done lat=obs_lat ; //Latitude always stays the same obs.z= 4000.0*sin(lat*PI/180.0); //Observer's vector from earth's center (secondary coords) obs.y=(4000.0*cos(lat*PI/180.0))*sin(lon*PI/180.0); // " obs.x=(4000.0*cos(lat*PI/180.0))*cos(lon*PI/180.0); // " //Change vector in secondary system to vector in primary system coords earth_observer.x=a11*obs.x+a12*obs.y+a13*obs.z; //Now transform to primary system earth_observer.y=a21*obs.x+a22*obs.y+a23*obs.z; // " earth_observer.z=a31*obs.x+a32*obs.y+a33*obs.z; // " //Vector from sun to observer is sum of vector from sun to earth plus observer's vector from earth center sun_observer.x=sun_earth.x+earth_observer.x; sun_observer.y=sun_earth.y+earth_observer.y; sun_observer.z=sun_earth.z+earth_observer.z; //The dot-product of the 'earth_observer' vector and 'sun_observer' vector allows us to calculate the angle dp=dot_product(&sun_observer,&earth_observer); *angle=acos(dp/(vector_length(&sun_observer)*vector_length(&earth_observer))); *angle*=180.0/PI; //Convert radians to degrees return(0); bad_exit: return(1); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - int calc_sun_earth_vector(datim *dt,VECTOR *se) { long it; //Time in seconds past perihelion (integer) double t; //Time in seconds past perihelion (floating point) double M; //The "mean" anomaly angle double E; //The "eccentric" anomaly angle double v; //Angle, in radians, of earth in its orbit from its initial position double r; //Polar coordinate 'radius' of earth int i; if (date_to_time(dt,&it)) { //Convert date/time structure to a time 't' in seconds since perihelion printf("calc_sun_earth_vector: cannot use input date/time\\n"); goto bad_exit; } t=it; //Convert time to float M=2*PI*t/SECONDS_PER_ORBIT; //Calculate Kepler's "Mean" anomaly /* The following equation solves for E, given M, using the first 3 terms of an infinite series. The equation that it's solving is Kepler's relationship between the Mean anomaly M and the Eccentric anomaly E...

E = M - e * sin E

*/ E=M //Compute Kepler's "Eccentric" anomaly (approximately) +(ECCENTRICITY-1.0/8.0*pow(ECCENTRICITY,3.0))*sin(1.0*M) //using the first three terms of an infinite series + 1.0/2.0*pow(ECCENTRICITY,2.0) *sin(2.0*M) // " + 3.0/8.0*pow(ECCENTRICITY,3.0) *sin(3.0*M); // " /* Another way to solve for E is iteratively using the conditions...

E[0] = M E[i+1] = M + e * sin( E[i] )

E=M; for (i=0;ix=r*cos(v); //Convert polar to cartesian se->y=r*sin(v); // " se->z=0.0; //There's never any Z component for earth's center return(0); bad_exit: return(1); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - int print_vec(VECTOR *v) { printf("%.1fx %.1fy %.1fz",v->x,v->y,v->z); return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - -

// VCTRSUPPORT.h //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = //= = = = = = = = = = = = = = = 3D VECTOR AND GEOMETRIC PROCESSING ROUTINES = = = = = = = = = = = = = = = = = = = = //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = /* Minimum angle to call lines parallel or perpendicular is arbitrarily set to 0.1 degrees. The sine of 0.1 degrees is 0.001745.

*/ #define MIN_SINE 0.001745 #define MIN_COSINE 0.001745 //- - - - - - - - - DEFINE GEOMETRIC TYPES (POINT, VECTOR, LINE, PLANE, TRIANGLE, etc) - - - - - - - - - - - - - -

typedef struct { //A POINT has a single definition and is defined by... double x,y,z; //Its 3 orthogonal (x,y,z) coords rel to the origin } POINT;

typedef struct { //A VECTOR has a single definition and is defined by... double x,y,z; //Its 3 orthogonal (x,y,z) composite vector lengths } VECTOR;

typedef struct { //A LINE's preferred definition is... POINT lp; //Any arbitrary POINT on the line VECTOR lv; //And a direction VECTOR } LINE; /* Note: In geometry a line has no inherit "direction" but in our "vector" description the VECTOR will allow us to consider the LINE to be "flowing" in one of two directions as well as to distinguish between the 'left' and 'right' sides of the LINE.

*/ typedef struct { //A line may also be defined by... POINT lp1; //Any two points POINT lp2; //On the line } line_2pt;

typedef struct { //A PLANE's preferred definition is in Hessian form... VECTOR pn; //A (possibly unit) VECTOR normal to the plane double d; //And d, the distance of the plane from the origin } PLANE; typedef struct { //Or, in pure scalar (coordinate) terms, as... double A,B,C,D; //The general equation Ax+By+Cz+D=0 } plane_coord; typedef struct { //Or, as... POINT pp; //Any point on the plane VECTOR pv1,pv2; //And 2 non-parallel direction vectors } plane_pt2v;

typedef struct { //A TRIANGLE is defined preferably by... POINT tp; //A POINT VECTOR tv1,tv2; //And 2 non-parallel VECTORs which define the other two corners } TRIANGLE; typedef struct { //A TRIANGLE may also be defined by... POINT tp1,tp2,tp3; //Three distinct non-colinear POINTs } triangle_3pt;

//- - - - - - - - - - - - FUNCTION PROTOTYPES OF BASIC GEOMETRIC OPERATIONS - - - - - - - - - - - int vector_1_point (POINT *p,VECTOR *v); int vector_2_points (POINT *p1,POINT *p2,VECTOR *v); double distance_2_points (POINT *p1,POINT *p2); double vector_length (VECTOR *v); int vector_add (VECTOR *v1,VECTOR *v2,VECTOR *v3); int vector_sub (VECTOR *v1,VECTOR *v2,VECTOR *v3); int vector_mpy (VECTOR *v,double scale); int vector_resize (VECTOR *v,double vector_len); double dot_product (VECTOR *v1,VECTOR *v2); int cross_product (VECTOR *v1,VECTOR *v2,VECTOR *v3); int plane_3_points (POINT *a,POINT *b,POINT *c,PLANE *pl); int intersect_line_plane (LINE *l,PLANE *pl,POINT *p); int plane_from_pt_2v (POINT *p,VECTOR *v1,VECTOR *v2,PLANE *pl); int lines_2_closest_points (LINE *l1,LINE *l2,POINT *pa,POINT *pb); double distance_2_lines (LINE *l1,LINE *l2,LINE *l3); int intersect_2_planes (PLANE *pl1,PLANE *pl2,LINE *l); int triangle_from_3pt (POINT *p1,POINT *p2,POINT *p3,TRIANGLE *t); int line_2_points (POINT *p1,POINT *p2,LINE *l); int vector_rotate (VECTOR *X,VECTOR *U,double theta); //- - - - - - - - - - - - - FUNCTION CODE FOR BASIC GEOMETRIC OPERATIONS - - - - - - - - - - - /* A VECTOR is constructed from the origin to a POINT. If the point is the origin itself a null vector will result, but this is not a problem. Zero is always returned.

*/ int vector_1_point(POINT *p,VECTOR *v) { v->x=p->x; v->y=p->y; v->z=p->z; return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - /* A VECTOR is constructed from two POINTs. If the two points are the same then a null vector will result, but this is not a problem. Zero is always returned.

*/ int vector_2_points(POINT *p1,POINT *p2,VECTOR *v) { v->x=p2->x-p1->x; v->y=p2->y-p1->y; v->z=p2->z-p1->z; return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - /* The distance between two points is calculated via the Pythagorean theorem. If the points are identical the value 0.0 will be returned so no check is needed.

*/ double distance_2_points(POINT *p1,POINT *p2) { return sqrt((p2->x - p1->x) * (p2->x - p1->x) + (p2->y - p1->y) * (p2->y - p1->y) + (p2->z - p1->z) * (p2->z - p1->z)); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - /* The length of a VECTOR is returned. No check for a null vector is needed.

*/ double vector_length(VECTOR *v) { return sqrt(v->x * v->x + v->y * v->y + v->z * v->z); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - /* Add two VECTORs v1 and v2, giving a third VECTOR v3. No check for null vectors is needed. Zero is always returned.

*/ int vector_add(VECTOR *v1,VECTOR *v2,VECTOR *v3) { v3->x=v1->x+v2->x; v3->y=v1->y+v2->y; v3->z=v1->z+v2->z; return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - /* Subtract VECTOR v2 from v1, giving a third VECTOR v3. No check for null vectors is needed. Zero is always returned.

*/ int vector_sub(VECTOR *v1,VECTOR *v2,VECTOR *v3) { v3->x=v1->x-v2->x; v3->y=v1->y-v2->y; v3->z=v1->z-v2->z; return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - /* Multiply the lengths of each of the three components of a VECTOR 'v' by 'scale'. The vector 'v' is modified as a result. 'scale' may be 0.0 resulting in 'v' becoming the null vector. This is not a problem. Zero is always returned.

*/ int vector_mpy(VECTOR *v,double scale) { v->x*=scale; v->y*=scale; v->z*=scale; return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - /* Resize the VECTOR 'v' so its length is 'vector_len'. The vector 'v' is modified as a result. 'vector_len' may be 0.0 resulting in 'v' becoming the null vector. This is not a problem. If 'vector_len' is less than zero then the direction of 'v' will be reversed because the sign of each of its component vectors will be reversed. In this case the length of 'v' will be set to the absolute value of 'vector_len'. Again, this is not a problem.

*/ int vector_resize(VECTOR *v,double vector_len) { double factor; /* if (vector_lenx*=factor; v->y*=factor; v->z*=factor; //And apply it to all the components } return(0); //Return success } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - /* The dot product of the two vectors v1 and v2 is returned. If the two vectors are *essentially* perpendicular return 0.0.

*/ double dot_product(VECTOR *v1,VECTOR *v2) { double temp,dp; if (v1->x==0.0 && v1->y==0.0 && v1->z==0.0 || //If either v2->x==0.0 && v2->y==0.0 && v2->z==0.0) { //vector is NULL /* printf("v=null "); */ return(0.0); //Pretend they are perpendicular and return 0.0 } dp=v1->x * v2->x + v1->y * v2->y + v1->z * v2->z; //Compute dot product if (dp==0.0) { //If dot product has gone to zero /* printf("dp=0.0 "); */ return(0.0); //Vectors must be perpendicular so return 0.0 } /* Since the magnitude of the dot product of two vectors is the product of their magnitudes times the cosine of the angle between them I can solve for the cosine of the angle between them by dividing the already computed dot product's magnitude by the product of their individual magnitudes. If this cosine is close to zero then the angle must be close to 90 degrees. If so, the vectors are 'essentially' perpendicular. */ temp=vector_length(v1)*vector_length(v2); //Get product of vector lengths if (temp==0.0) { //If product of vector lengths has gone to zero /* printf("v*v=null "); */ return(0.0); //Vectors must be perpendicular } if (fabs(dp)/tempx==0.0 && v1->y==0.0 && v1->z==0.0 || //If either v2->x==0.0 && v2->y==0.0 && v2->z==0.0) { //vector is null v3->x=0.0; //Set the output vector to null v3->y=0.0; v3->z=0.0; return(1); //Pretend parallel and return 1 } v3->x=v1->y * v2->z - v2->y * v1->z; v3->y=v1->z * v2->x - v2->z * v1->x; v3->z=v1->x * v2->y - v2->x * v1->y; if (v3->x==0.0 && v3->y==0.0 && v3->z==0.0) { //If computed vector is null return(1); //The inputs must have been parallel, return 1 } /* THIS OPTIONAL CODE IMPLEMENTS THE "ESSENTIALLY PARALLEL" LOGIC IF YOU WANT IT

Since the magnitude of the cross product of two vectors is the product of their magnitudes times the sine of the angle between them I can solve for the sine of the angle between them by dividing the already computed cross product's magnitude by the product of their individual magnitudes. If this sine is close to zero then the angle must be close to zero degrees. If so, the vectors are 'essentially' parallel.

divisor=vector_length(v1)*vector_length(v2); //Calc product of input magnitudes if (divisor==0.0) //If divisor has gone to zero input vectors too small to work with return(1); //Return the result as if parallel if ((vector_length(v3)/divisor)x - a->x; //Create a VECTOR from POINT 'a' to POINT 'b' temp_v1.y = b->y - a->y; temp_v1.z = b->z - a->z; temp_v2.x = c->x - a->x; //Create a VECTOR from POINT 'a' to POINT 'c' temp_v2.y = c->y - a->y; temp_v2.z = c->z - a->z; if (plane_from_pt_2v(a,&temp_v1,&temp_v2,pl)==0) //Transform to a PLANE return(0); //If subcontract went OK return 0 else //Otherwise, something's wrong with our input (two points same?) return(1); //So return error } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - /* To calculate the intersection of a line and a plane use the vector definitions of each... _ _ n . p = d (normal vector and distance def. of PLANE) _ _ _ p = p1 + t * l (point and vector def. of LINE) _ _ _ _ _ _ n . (p1 + t * l) = d (subst p1 + t * l for p in PLANE equation) _ _ _ _ n . p1 + t * n . l = d (using distributive law) _ _ _ _ t = (d - n . p1) / (n . l) (solve for t) _ Plug value for t into LINE eq. to get p

*/ int intersect_line_plane(LINE *l,PLANE *pl,POINT *p) { double t,temp; VECTOR p1v; if ((temp=dot_product(&l->lv,&pl->pn))==0.0) //If line and plane parallel return(1); //Return bad status 1 p1v.x=l->lp.x; p1v.y=l->lp.y; p1v.z=l->lp.z; t=(pl->d-dot_product(&pl->pn,&p1v))/temp; p->x = l->lp.x + t * l->lv.x; p->y = l->lp.y + t * l->lv.y; p->z = l->lp.z + t * l->lv.z; return(0); //Return normal status 0 } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - /* Compute the Hessian form of the plane from the 2-vectors-and-point form. _ _ n . p = d (normal vector and distance def. of PLANE) _ _ _ _ _ n = (v1 X v2) / |v1 X v2| (def. of unit normal vector to PLANE) _ _ _ _ _ d = ((v1 X v2) / |v1 X v2|) . p (solve for d)

*/ int plane_from_pt_2v(POINT *p,VECTOR *v1,VECTOR *v2,PLANE *pl) { double temp; VECTOR tempv; if (cross_product(v1,v2,&pl->pn)==1) //If 2 vectors parallel return(1); //Return error temp=vector_length(&pl->pn); pl->pn.x/=temp; pl->pn.y/=temp; pl->pn.z/=temp; //Make norm vector unit tempv.x=p->x; tempv.y=p->y; tempv.z=p->z; pl->d=dot_product(&pl->pn,&tempv); return(0); //Normal return } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - /* Given two lines 'l1' and 'l2' determine the point 'pa' on line 'l1' and the point 'pb' on line 'l2' that are the two closest points between the lines. The normal return is the value 0. This occurs even when the two lines intersect and, therefore, 'pa' and 'pb' are the same point. This is not an error. If the given lines are parallel then there are no unique points you can call 'closest'. In that case a 1 is returned as an error indication and the resulting values for 'pa' and 'pb' are undefined.

*/ int lines_2_closest_points(LINE *l1,LINE *l2,POINT *pa,POINT *pb) { VECTOR c; PLANE pl1,pl2; if (cross_product(&l1->lv,&l2->lv,&c)==1) { /* If lines are parallel */ return(1); } /* In the next 4 function calls (plane_from_pt_2v and intersect_line_plane) the check for an error '1' return is for safety only. Because the two input lines have already been shown to be non-parallel these calculations *should* return a normal result. If, though, for any reason these functions *do* return an error result it might be because of precision problems and so we must then return error '1' just as if our first test for parallel lines had been true. It should never happen if we get this far. */ /* form plane containing line l1, parallel to vector c */ if (plane_from_pt_2v(&l1->lp,&l1->lv,&c,&pl1)==1) return(1); /* form plane containing line l2, parallel to vector c */ if (plane_from_pt_2v(&l2->lp,&l2->lv,&c,&pl2)==1) return(1); /* closest point on l1 is where l1 intersects pl2 */ if (intersect_line_plane(l1,&pl2,pa)==1) return(1); /* closest point on l2 is where l2 intersects pl1 */ if (intersect_line_plane(l2,&pl1,pb)==1) return(1); /* Interesting, but not necessary, code for distinguishing between intersecting or skew lines. if (distance_2_points(pa,pb)==0.0) //Intersecting else //Skew */ return(0); //Normal return } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - double distance_2_lines(LINE *l1,LINE *l2,LINE *l3) { POINT p1,p2; if (lines_2_closest_points(l1,l2,&p1,&p2)==1) //If the lines are parallel return(-1.0); //Return an error (in future could return singular value) l3->lp.x=p1.x; l3->lp.y=p1.y; l3->lp.z=p1.z; l3->lv.x=p2.x-p1.x; l3->lv.y=p2.y-p1.y; l3->lv.z=p2.z-p1.z; return(distance_2_points(&p1,&p2)); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - /* The intersection of the two planes pl1 and pl2 is returned as a line in point-vector format in 'l'. If the planes are parallel 1 is returned. Otherwise, 0.

*/ int intersect_2_planes(PLANE *pl1,PLANE *pl2,LINE *l) { VECTOR *v1,*v2; double determinant,c1,c2,d1,d2; if (cross_product(&pl1->pn,&pl2->pn,&l->lv)==1) return(1); /* OK, they're not parallel, use Paul Bourke algorithm. */ v1=&pl1->pn; d1=pl1->d; v2=&pl2->pn; d2=pl2->d; determinant=dot_product(v1,v1)*dot_product(v2,v2) -dot_product(v1,v2)*dot_product(v1,v2); /* Next check catches boundary condition where cross product not zero but determinant is, so must still call the planes parallel. */ if (determinant==0.0) return(1); c1=(d1*dot_product(v2,v2)-d2*dot_product(v1,v2))/determinant; c2=(d2*dot_product(v1,v1)-d1*dot_product(v1,v2))/determinant; l->lp.x=c1*v1->x+c2*v2->x; l->lp.y=c1*v1->x+c2*v2->y; l->lp.z=c1*v1->x+c2*v2->z; return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - int triangle_from_3pt(POINT *p1,POINT *p2,POINT *p3,TRIANGLE *t) { VECTOR temp; t->tp.x = p1->x; t->tp.y = p1->y; t->tp.z = p1->z; t->tv1.x = p2->x - p1->x; t->tv1.y = p2->y - p1->y; t->tv1.z = p2->z - p1->z; t->tv2.x = p3->x - p1->x; t->tv2.y = p3->y - p1->y; t->tv2.z = p3->z - p1->z; if (cross_product(&t->tv1,&t->tv2,&temp)==1) //If vector from p1->p2 parallel to vector from p1->p3 return(1); //Return error, points are colinear or two points are the same else //Otherwise return(0); //Normal return } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - /* Return the preferred 'point-vector' definition of a line given two points on the line. This routine always returns POINT p1 as the 'point' and the directed vector going from POINT p1 to POINT p2 as the 'vector'. A check is made, however, to ensure that both p1 and p2 are not the same POINT.

*/ int line_2_points(POINT *p1,POINT *p2,LINE *l) { l->lp.x=p1->x; l->lp.y=p1->y; l->lp.z=p1->z; //LINE 'l' defined by POINT 'p1' l->lv.x=p2->x-p1->x; l->lv.y=p2->y-p1->y; l->lv.z=p2->z-p1->z; //And vector p1->p2 if (l->lv.x==0.0 && l->lv.y==0.0 && l->lv.z==0.0) //If the vector is null, points were the same return(1); //So return bad status else //Otherwise, points were not the same return(0); //Return normal status } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - /* Rotate the VECTOR X about the VECTOR U through an angle of 'theta' radians. Note that if X and U are pointing in the same direction their cross product returns the null vector. This is not a problem because it simply causes X to be returned unchanged as it ought to.

*/ int vector_rotate(VECTOR *X,VECTOR *U,double theta) { VECTOR temp_u,temp_x,temp_v1,temp_v2; double temp; temp_u=*U; //Make a copy of U so we can resize it vector_resize(&temp_u,1.0); //Make it a unit vector for the following calculations temp_x=*X; //Make a copy of X to work with vector_mpy(&temp_x,cos(theta)); //Now have X*cos(theta) [in temp_x] temp=dot_product(&temp_u,&temp_x)*(1.0-cos(theta)); //Now have (U.X)(1-cos(theta)) [in temp] temp_v1=temp_u; //Prepare to multiply U by result of previous line vector_mpy(&temp_v1,temp); //Now have U*(U.X)(1-cos(theta)) [in temp_v1] cross_product(&temp_u,X,&temp_v2); //Now have (UxX) vector_mpy(&temp_v2,sin(theta)); //Now have (UxX)*sin(theta) [in temp_v2] vector_add(&temp_v1,&temp_v2,X); //Now have U*(U.X)(1-cos(theta))+(UxX)*sin(theta) [in X] vector_add(&temp_x,X,X); //Now have X*cos(theta)+U*(U.X)(1-cos(theta))+(UxX)*sin(theta) [in X] return(0); } //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Reply to
TR Oltrogge

Hey, let's try this one more time now that I've set line-wrap to 132 columns...

/* SUNSET.C a program to calculate the time of sunset for any given latitude and longitude, year, and day of the year. It uses vector algebra as much as possible.

Sunset occurs when a line from the sun (considered a point source) runs tangent to the earth and touches earth exactly at the point of the observer's latitude and longitude. Consider this to basically be a problem in 3D coordinate space using rectangular coordinates (although polar coordinates are used initially to compute the earth's orbit).

The 3D coordinate space has the sun at the origin. The earth's orbit is an ellipse in the X-Y plane with the sun at one of the foci. When the earth is at perihelion, or closest to the sun, it is on the positive X-axis with Y=0.

We need two equations. One will determine the earth's center as a vector from the sun as a function of time. Call it the 'sun-earth' vector. The second will determine the distance and direction from the center of the earth to the observer's position on the surface as a function of time. Call it the 'earth-observer' vector. The sum of these two vectors will be a vector that goes from the sun to the observer's position on the surface of the earth. This third, computed, vector is called the 'sun-observer' vector. When the 'sun-observer' vector is perpendicular to the 'earth-observer' vector then the sun is sitting on the horizon and it's either sunrise or sunset. Perpendicular is tested for by taking the dot-product of the two vectors 'earth-observer' and 'sun-observer' and testing for zero.

CALCULATING THE sun-earth VECTOR AS A FUNCTION OF TIME t

Using polar coordinates with the sun at the origin and zero degrees being perihelion Kepler devised the following two equations in solving the mathematics of elliptical orbits...

(1) M = 2 * PI * t / T

where 'M' is the "mean" anomaly, 't' is the time since perihelion, and 'T' is the orbital period

(2) M = E - e * sin (E)

where 'E' is the "eccentric" anomaly, and 'e' is the eccentricity of the ellipse

The goal here is to first calculate 'M' from 't'. 'T' is 365 days 6 hours 13 minutes and 53 seconds, the time in an "anomalistic" orbit (perihelion to perihelion). This is 31558433 seconds. Then we use the second equation to solve for 'E' since we now know 'M' and 'e'. Unfortunately, equation (2) cannot be solved via algebra. But a formula involving an infinite series will get it accurately enough with about three terms...

E = M + (e - 1/8 * e^3) * sin (M) + 1/2 * e^2 * sin (2 * M) + 3/8 * e^3 * sin(3 * M) + ...

Once we get 'E' we substitute this value into the following equation to get 'v' (angle of the earth from the sun, where v=0 at perihelion)...

(3) v = arctan ( sqrt ((1 + e) / (1 - e)) * tan ( E / 2) ) * 2

where 'v' is the polar coordinate angle (from the sun) for the earth at time 't'

Then we simply plug 'v' into the equation for an ellipse (in polar coordinates) to get 'r' (the distance from the sun)...

(4) r = a * (1 - e^2) / (1 + e * cos (v) )

where 'a' is the semi-major axis

A final conversion of 'v' and 'r' into cartesian coordinates is made by

x = r * cos ( v ) y = r * sin ( v )

The 'sun-earth' X coordinate and Y coordinate are now known as a function of time 't'. The Z coordinate is always zero since the orbit is in the X-Y plane.

VISUALIZING THE EARTH'S SPHERICAL SHAPE, AXIS OF ROTATION, AND SEASON-CREATING TILT OF THE AXIS

Next we have to compute the vector 'earth_observer' based on any time 't'. This vector represents the direction and distance of travel from the center of the earth to the observer's location on the surface. This 'earth_observer' vector, expressed in the basis unit vectors of our earth-orbit coordinate system, is computed based on three new factors...

(1) Latitude and Longitude

The observer is on the surface of a sphere with an approximately 4000 mile radius. Using the observer's latitude and longitude will allow us to compute the surface coordinates and, thus, a basic 'observer' vector from the earth's center to the observer. To simplify this calculation we will invoke a new, secondary, coordinate system aligned with the latitude and longitude grid. The X-axis of this new system starts from the earth's center and leaves the earth where the prime meridian intersects with the equator. The Z-axis of this new system is the axis of rotation of the earth and starts at the earth's center and leaves the earth through the north pole. The Y-axis of this new system is now determined by the other two axes. It starts at the earth's center and leaves the earth where the 90 degree east longitude meridian intersects the equator. Using this secondary coordinate system we compute the observer's location with the three equations...

Z= 4000*sin(lat) Y=(4000*cos(lat))*sin(lon) X=(4000*cos(lat))*cos(lon)

Notice that the familiar notation of east and west longitude designation has been replaced with the mathematical requirement that east longitude is used as-is (a positive number from 0 to 180) but west longitude is negated in sign (so 0 to 180 west longitude becomes 0 to

-180 degrees).

(2) Elapsed Time

The earth rotates on its own axis approximately every 23 hours 56 minutes

4 seconds (a sidereal day). So even though the observer is fixed in geographic latitude and longitude his effective longitude increases by 360 degrees every 23h56m4s. Therefore, an incremented longitude computed from the time 't' is added to the basic longitude specified in factor (1) to give a correct computation of the 'earth_observer' vector including both factors (1) and (2). At this point the 'earth_observer' vector is still relative to the secondary coordinate system. The equations now become...

Z= 4000*sin(lat) Y=(4000*cos(lat))*sin(lon+t/SIDEREAL_DAY*360) X=(4000*cos(lat))*cos(lon+t/SIDEREAL_DAY*360)

(3) Coordinate Conversion from Secondary to Primary Coordinate System for Earth's Axial Tilt

Because the earth's axis of rotation is "tilted" at an angle of 23.5 degrees with respect to the orbital plane the basic calculation of the 'earth_observer' vector done by using factors (1) and (2) with respect to a secondary coordinate system based on latitude and longitude will have to be converted to a vector based on the original coordinate system that was used to describe the earth's elliptical orbit before we can use it in an algebraic solution for the time of sunset.

Returning to the original coordinate system the earth's north pole is above the X-Y plane, always in positive Z space and "roughly" near the top of the sphere that is the earth. Due to the earth's tilt a line from the earth's center through the north pole will always make an angle of 23.5 degrees with the Z-axis of the original coordinate system. The direction of this line in the original coordinate system is called the 'tilt' vector. It is a constant vector and does not change over time. It never changes its direction. Its specific X, Y, and Z components can only be determined by researching astronomers' analysis of earth's orbit. This 'tilt' vector the astronomers give us is the Z-axis of our secondary coordinate system. What are the X and Y axes? Astronomers will also have to give us those since they determine the orientation of the prime meridian to the original coordinate system at time t=0. Once we have all three of these secondary coordinate system axes defined *as components of the original coordinate system* then we use the transformation that follows for any coordinate system conversion. Thus, the astronomers give us three vectors aligned with the axes of our secondary coordinate system but using the original coordinate system's basis vectors. We call them 'tilt_x', 'tilt_y', and 'tilt_z'.

'tilt_z' is calulated by examining the earth's axis of rotation at the time of summer solstice. 'tilt_x' is also computed by noting where the prime meridian is positioned at the time of summer solstice and then "unwinding" it around the earth's axis of rotation back to the time t=0. 'tilt_y' is simply the cross product of 'tilt_z' and 'tilt_x'. The rotation of the initial 'tilt_x' vector constructed from the geometry at summer solstice back to time t=0 is done using Rodrigues' solution for rotating vector X around vector U by angle theta... _ _ _ _ _ _ _ X' = X * cos(theta) + U * (U . X)(1 - cos(theta)) + (U x X) * sin(theta)

CONVERSION OF COORDINATES BETWEEN TWO COORDINATE SYSTEMS

The coordinate conversion from the secondary system to the original system is accomplished via the general-purpose linear transformation algebraic solution found on the top of page 534 of my VNR book. This solution handles the general case of both a translation of the origin and a rotation of the axes for two distinct coordinate systems. It requires the calculation of "direction cosines" for each of nine angles. Given any vectors aligned with the three axes for the two coordinate systems these nine direction cosines can be computed by using the dot-product relationship of two vectors. Because the dot-product of two vectors is the product of the magnitudes of both vectors multiplied by the cosine of the angle between them the cosine itself is the dot-product divided by the product of the magnitudes of both vectors. The calculation of the nine direction cosines follows:

a11 = dot-product(unit_x,tilt_x) / ( |unit_x|*|tilt_x| ) a12 = dot-product(unit_x,tilt_y) / ( |unit_x|*|tilt_y| ) a13 = dot-product(unit_x,tilt_z) / ( |unit_x|*|tilt_z| ) a21 = dot-product(unit_y,tilt_x) / ( |unit_y|*|tilt_x| ) a22 = dot-product(unit_y,tilt_y) / ( |unit_y|*|tilt_y| ) a23 = dot-product(unit_y,tilt_z) / ( |unit_y|*|tilt_z| ) a31 = dot-product(unit_z,tilt_x) / ( |unit_z|*|tilt_x| ) a32 = dot-product(unit_z,tilt_y) / ( |unit_z|*|tilt_y| ) a33 = dot-product(unit_z,tilt_z) / ( |unit_z|*|tilt_z| )

The direction cosines are the 'Amn' coefficients in the following equations. The translational distance between the origins of the two coordinate systems are the 'An' constants.

The equations for converting a coordinate in the 'prime' coordinate system to a coordinate in the 'non-prime' coordinate system are on the left-hand side. The equations for converting a coordinate in the 'non-prime' coordinate system to a coordinate in the 'prime' coordinate system are on the right-hand side.

x = a1 + a11 * x' + a12 * y' + a13 * z' | x' = a11 * (x - a1) + a21

  • (y - a2) + a31 * (z - a3) y = a2 + a21 * x' + a22 * y' + a23 * z' | y' = a12 * (x - a1) + a22
  • (y - a3) + a32 * (z - a3) z = a3 + a31 * x' + a32 * y' + a33 * z' | z' = a13 * (x - a1) + a23
  • (y - a3) + a33 * (z - a3)

In both sets of equations a1, a2, and a3 are the coordinates (x, y, and z) of the origin of the 'prime' system with respect to the 'non-prime' system. That is, the origin point {0', 0', 0'} of the 'prime' system is at location {a1, a2, a3} in the 'non-prime' system.

In both sets of equations the 'Amn' direction cosines refer to the cosine of the angle between axis 'm' of the 'non-prime' system and axis 'n' of the 'prime' system (where m=1 is the X-axis of the 'non-prime' system, m=2 is the Y-axis of the 'non-prime' system, and m=3 is the Z-axis of the 'non-prime' system; where n=1 is the X-axis of the 'prime' system, n=2 is the Y-axis of the 'prime' system, and n=3 is the Z-axis of the 'prime' system). Notice that because of the symmetry of the cosine function about the zero angle it makes no difference whether the angle is rotated through according to a right-handed orientation or a left-handed orientation since cos(theta)=cos(360-theta). However, each of the nine angles rotated through must all be determined by choosing representative vectors of the axes of each coordinate system that have the same orientation to those axes with regard to positive (or negative) direction.

Since we are dealing with vectors and not actual coordinates there is no translation of the origin of our two systems to be considered. a1, a2, and a3 are all zero. We will have coordinates in the secondary system that need to be converted to the primary system and the equations on the left will be used.

*/ #include #include #include #include "VCTRSUPPORT.h" //SOME USEFUL CONSTANTS #define PI 3.141592653589793 #define SIDEREAL_DAY (23*3600.0+56*60.0+4) //Number of seconds in a sidereal day #define SUN_RISE_SET_ANGLE 89.167 //Angle between 'sun_earth' & 'earth_observer' at sunrise/sunset #define SEMI_MAJOR_AXIS 92955807.0 //Semi-major axis of earth's orbit ellipse #define SECONDS_PER_ORBIT (365*86400+6*3600+13*60+53) //Number of seconds in a true orbit (anomalistic) of the sun #define ECCENTRICITY 0.0167 //Earth's elliptical orbit eccentricity #define AXIS_TILT 23.45 //Number of degrees of tilt in the earth's axis double obs_lat=40.267; //Observer's latitude (positive is northern hemispere, negative is southern hemisphere) double obs_lon=-80.117; //Observer's longitude (positive is east of Greenwich, negative is west of Greenwich) //double obs_lat=45.0; //Observer's latitude (positive is northern hemispere, negative is southern hemisphere) //double obs_lon=-120.0; //Observer's longitude (positive is east of Greenwich, negative is west of Greenwich) typedef struct { int yr; //2008, 2009, 2010, etc int mo; //1=JAN, 2=FEB, etc int da; //1-31 int hh; //0-23 int mm; //0-59 int ss; //0-59 int tzone; //0=GMT -5=EST, etc } datim; // YYYY MM DD HH MM SS Z datim perihelion={2008, 1, 3, 0, 0, 0,0}; //Date/time (GMT) of perihelion (earth closest to sun, our t=0) datim s_solstice={2008, 6,20,23,59, 0,0}; //Date/time (GMT) of summer solstice (tilt_z leans toward sun) //Other interesting, but unneeded, dates //datim sp_equinox={2008, 3,20, 5,48, 0,0}; //Date/time (GMT) of spring equinox (sun_earth perpend to tilt_z) //datim aphelion ={2008, 7, 4, 8, 0, 0,0}; //Date/time (GMT) of aphelion (earth farthest from sun) //datim fa_equinox={2008, 9,22,15,44, 0,0}; //Date/time (GMT) of fall equinox (sun_earth perpend to tilt_z again) //datim w_solstice={2008,12,21,12, 4, 0,0}; //Date/time (GMT) of winter solstice (tilt_z leans away from sun) //The three vectors for the secondary coord system (described in primary coord system components) VECTOR tilt_z; //Z-axis of secondary coord syst def'd in *primary coords* (earth's rotation axis) VECTOR tilt_x; //X-axis of secondary coord syst def'd in *primary coords* (prime meridian at t=0) VECTOR tilt_y; //Y-axis of secondary coord syst def'd in *primary coords* (90 degrees east longitude) //Three unit vectors described in the primary coord system used for computing direction cosines VECTOR unit_x={1,0,0}; VECTOR unit_y={0,1,0}; VECTOR unit_z={0,0,1}; //The nine direction cosines double a11,a12,a13,a21,a22,a23,a31,a32,a33; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - //Function prototypes int date_to_time (datim *d,long *t); int adjust_date (datim *dt,long time); int calc_angle (datim *dt,double *angle); int calc_sun_earth_vector (datim *dt,VECTOR *se); int print_vec (VECTOR *v); //Debug support //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - /* Convert a date and time-of-day value to the time elapsed since perihelion (t=0)

*/ int days_in_month[12]={31,28,31,30, 31, 30, 31, 31, 30, 31, 30, 31}; int cum_days[12] ={ 0,31,59,90,120,151,181,212,242,273,303,334}; int date_to_time(datim *d,long *t) { int max_days; long cum_d,cum_sec_perihelion,cum_sec_input,cum_sec_intervening,y; if (d->yr < 1901 || d->yr > 2099) goto bad_datim; //Century years not leap unless divisible by 400 if (d->mo < 1 || d->mo > 12) goto bad_datim; //Month in range? max_days=days_in_month[d->mo-1]+((!(d->yr%4) && d->mo==2) ? 1 : 0); //Account for Feb in leap year if (d->da < 1 || d->da > max_days) goto bad_datim; //Day in range? if (d->hh < 0 || d->hh > 23) goto bad_datim; //Hour in range? if (d->mm < 0 || d->mm > 59) goto bad_datim; //Minute in range? if (d->ss < 0 || d->ss > 59) goto bad_datim; //Second in range? if (d->tzone < -12 || d->tzone > 11) goto bad_datim; //Time zone in range? //Calc # seconds perihelion date/time is into its own year cum_d=cum_days[perihelion.mo-1] //Start with # days in prior months +((!(perihelion.yr%4) && perihelion.mo>2) ? 1 : 0); //Add 1 if beyond Feb in leap yr cum_d+=perihelion.da-1; //Add # days into month cum_sec_perihelion=cum_d*86400 //Cum secs total starts w/secs in prior days +(perihelion.hh-perihelion.tzone)*3600 //Plus secs in prior hrs of day (adj for tzone) +perihelion.mm*60 //Plus secs in prior mins +perihelion.ss; //Plus secs into current min //Calc # seconds input date/time is into its own year cum_d=cum_days[d->mo-1] //Start with # days in prior months +((!(d->yr%4) && d->mo>2) ? 1 : 0); //Add 1 if beyond Feb in leap yr cum_d+=d->da-1; //Add # days into month cum_sec_input=cum_d*86400 //Cum secs total starts w/secs in prior days +(d->hh-d->tzone)*3600 //Plus secs in prior hrs of day (adj for tzone) +d->mm*60 //Plus secs in prior mins +d->ss; //Plus secs into current min //Calc # seconds in intervening years starting at beginning of perihelion year for (cum_sec_intervening=0,y=perihelion.yr;yyr;y++) //For all intervening years cum_sec_intervening+=(365*86400)+(!(y%4) ? 86400 : 0); //Add # secs in year, accounting for leap *t=cum_sec_intervening-cum_sec_perihelion+cum_sec_input; //Subt perihelion start fract, add input start fract return(0); bad_datim: printf("date_to_time: invalid datim structure (%d/%d/%d %d:%d:%d tZone:%d\\n)\\n", d->yr,d->mo,d->da,d->hh,d->mm,d->ss,d->tzone); return(1); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - int main() { int it; //Time 't' as an integer double t; //Time in seconds from initial position of earth VECTOR sun_earth; //Vector from sun to earth's center VECTOR normal; //Computed vector normal to orbital plane through the sun datim current_date; //Work area for constructing dates to be analyzed double angle; long delta_t; double theta; /* OK, let's get started! First, calculate the constant 'tilt_z' vector (the earth's axis of rotation) in the primary coordinate system based on the fact that at summer solstice the axis of the earth is, at that moment, pointing *directly* at the sun. Another way to put it is that the normal vector to the orbital plane, passing through the sun, will intersect the earth's extended axis at some point in the positive Z direction. Thus, a right triangle is formed by the earth, the sun, and that point of intersection in positive Z space. (This condition also happens at winter solstice, but then the intersection is in negative Z space.) The 'sun_earth' vector at time of summer_solstice is the short leg of this right triangle. The normal vector to the earth's orbital plane and passing through the sun is the long leg of this triangle. The hypotenuse is the 'tilt_z' vector for the earth. The tangent of 23.45 degrees (0.4337751161) is the ratio of the 'sun_earth' vector's length to the length of the normal vector. So the length of the normal vector is computed by dividing the length of the 'sun_earth' vector by the tangent of 23.45 degrees.

*/ if (calc_sun_earth_vector(&s_solstice,&sun_earth)) { printf("sunset: cannot use summer solstice date/time\\n"); goto bad_exit; } // printf("sun_earth="); print_vec(&sun_earth); printf("\\n"); //Debug //OK, now have 'sun_earth' vector. Solve for normal vector's length from right triangle relationship. normal.x=0.0; //Normal vector has no X component normal.y=0.0; //Normal vector has no Y component normal.z=vector_length(&sun_earth)/tan(AXIS_TILT*PI/180); //Z length is 'sun_earth' length divided by tan(23.45) // printf("normal="); print_vec(&normal); printf("\\n"); //Debug //The 'tilt_z' vector is the sum of the 'sun_earth' vector (negated) and the 'normal' vector tilt_z.x=-sun_earth.x+normal.x; //Add 'em tilt_z.y=-sun_earth.y+normal.y; // " tilt_z.z=-sun_earth.z+normal.z; // " // printf("tilt_z="); print_vec(&tilt_z); printf("\\n"); //Debug /* OK, now have 'tilt_z' vector described in primary coordinates. We now need the 'tilt_x' and 'tilt_y' vectors. These are both perpendicular to the 'tilt_z' vector and the 3 of them are the axes for the secondary coordinate system. 'tilt_x' points from the earth's center through the earth's equator at the longitude of the prime meridian at time t=0. It will also be constant, not changing as the earth orbits or rotates. 'tilt_x' will tell us the exact orientation of the earth in its rotation about its axis at t=0. 'tilt_y' is nothing more than the vector from the earth's center through the equator at longitude east 90 degrees. It will be calculated via a cross product of 'tilt_z' and 'tilt_x' once 'tilt_x' has been ascertained.

Working with the summer solstice again, this time let's make a right triangle using the 'sun_earth' vector as the long leg. The normal vector to the earth's orbital plane going through the sun and going in the *negative* Z direction is the short leg. The hypotenuse is related to the 'tilt_x' vector since it goes from the earth's center out the equator but at a longitude that is *not* the prime meridian.

*If* the prime meridian were aligned exactly with this hypotenuse at the time of summer solstice then it would be exactly noon GMT at the summer solstice. But, instead, it is some other time (GMT) at summer solstice (23:59 for 2008, not 12:00) and so we know the prime meridian is rotated (on the 'tilt_z' axis) an angle of (23:59-12:00)/SIDEREAL_DAY*360) degrees from the orientation of the hypotenuse of our right triangle. But this is all we need to know in order to "back out" all the rotations of the prime meridian (which is the 'tilt_x' vector) that have happened since the date/time of perihelion. Then we will have the correct 'tilt_x' vector value we want. Back to our second right triangle. The tangent of 23.45 degrees is the ratio of the length of the negative-Z-going normal vector to the 'sun_earth' vector's length. So the length of this negative-Z-going normal vector is the product of the 'sun_earth' vector's length and the tangent of 23.45 degrees. */ normal.x=0.0; //There's no X component of any vector normal to the orbital plane normal.y=0.0; //Also, no Y component normal.z=-vector_length(&sun_earth)*tan(AXIS_TILT*PI/180); //Z component from right triangle relationship // printf("normal(-Z)="); print_vec(&normal); printf("\\n"); //The 'tilt_x' vector (as a starting point) is the sum of the 'sun_earth' vector and the 'normal' vector (negated) tilt_x.x=sun_earth.x-normal.x; tilt_x.y=sun_earth.y-normal.y; tilt_x.z=sun_earth.z-normal.z; // printf("tilt_x(start)="); print_vec(&tilt_x); printf("\\n"); //The prime meridian is (Time at GMT)/SIDEREAL_DAY*360 degrees counterclockwise from the "midnight" vector it=s_solstice.hh*3600+s_solstice.mm*60+s_solstice.ss; t=it; //Convert time to float theta=t/SIDEREAL_DAY*2*PI; vector_rotate(&tilt_x,&tilt_z,theta); //Rotate small amt to get prime meridian at GMT of summer solstice // printf("tilt_x(at GMT)="); print_vec(&tilt_x); printf("\\n"); //Unwind tilt_x vector to its position at perihelion (find the angle of clockwise rotation needed) if (date_to_time(&s_solstice,&it)) { //Convert summer solstice date/time to secs since perihelion printf("sunset: unable to convert summer solstice date/time to seconds\\n"); goto bad_exit; } t=it; //Convert time to float theta=-t/SIDEREAL_DAY*2*PI; //This is angle to unwind *for the specific meridian* aligned with 'sun-earth' vector vector_rotate(&tilt_x,&tilt_z,theta); //Unwind 'tilt_x' vector until t=0 // printf("tilt_x(final, at t=0)="); print_vec(&tilt_x); printf("\\n"); //Take cross product of 'tilt_z' with 'tilt_x' to generate our 'tilt_y' perpendicular to both cross_product(&tilt_z,&tilt_x,&tilt_y); //Compute the nine direction cosines for secondary to primary axes conversion a11=dot_product(&unit_x,&tilt_x)/(vector_length(&unit_x)*vector_length(&tilt_x)); a12=dot_product(&unit_x,&tilt_y)/(vector_length(&unit_x)*vector_length(&tilt_y)); a13=dot_product(&unit_x,&tilt_z)/(vector_length(&unit_x)*vector_length(&tilt_z)); a21=dot_product(&unit_y,&tilt_x)/(vector_length(&unit_y)*vector_length(&tilt_x)); a22=dot_product(&unit_y,&tilt_y)/(vector_length(&unit_y)*vector_length(&tilt_y)); a23=dot_product(&unit_y,&tilt_z)/(vector_length(&unit_y)*vector_length(&tilt_z)); a31=dot_product(&unit_z,&tilt_x)/(vector_length(&unit_z)*vector_length(&tilt_x)); a32=dot_product(&unit_z,&tilt_y)/(vector_length(&unit_z)*vector_length(&tilt_y)); a33=dot_product(&unit_z,&tilt_z)/(vector_length(&unit_z)*vector_length(&tilt_z)); /* OK, now test all our logic by choosing some date/time values and then calculating the 'sun_earth' vector, the 'earth_observer' vector, the 'sun_observer' vector, and the angle between the 'sun_observer' and 'earth_observer' vectors. When the angle is close to 90 degrees then we have a sunrise or sunset.

Given a date/time determine the time of sunrise and sunset for that date. Do this by iterating times during the date and check that the angle of the sun is close enough to SUN_RISE_SET_ANGLE.

*/ for (current_date.yr=2008;current_date.yrmm+=adj_mm; dt->hh+=adj_hh; if (dt->ss > 59) { dt->ss-=60; dt->mm++; } if (dt->ss < 0) { dt->ss+=60; dt->mm--; } if (dt->mm > 59) { dt->mm-=60; dt->hh++; } if (dt->mm < 0) { dt->mm+=60; dt->hh--; } if (dt->hh > 23) { dt->hh=23; dt->mm=59; dt->ss=59; goto bad_exit; } if (dt->hh < 0) { dt->hh=0; dt->mm=0; dt->ss=0; goto bad_exit; } return(0); bad_exit: return(1); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - int calc_angle(datim *dt,double *angle) { /* Given a date/time in 'dt' and using the observer's position ('obs_lat' and 'obs_lon') compute the angle between the 'earth_observer' vector and the 'sun_observer' vector. Return its value in degrees. Return 0 if no error. Return 1 if an error is found in the input date/time 'dt'.

*/ long it; double t,lon,lat,dp; VECTOR sun_earth; //Vector from sun to earth's center VECTOR earth_observer; //Vector from earth's center to the observer at given latitude, longitude, and time VECTOR obs; //Vector from earth's center to observer's point (after time rotation) in *secondary coords* VECTOR sun_observer; //Vector from sun to the observer's tangent point if (calc_sun_earth_vector(dt,&sun_earth)) { printf("calc_angle: unable to calculate sun_earth vector\\n"); goto bad_exit; } if (date_to_time(dt,&it)) { //Convert date/time structure to a time 't' in seconds since perihelion printf("calc_angle: cannot use input date/time\\n"); goto bad_exit; } t=it; //Convert time to float //Second calculate observer's latitude/longitude in secondary coords with elapsed time increasing longitude lon=obs_lon+t/SIDEREAL_DAY*360.0; //Compute longitude with time rotation done lat=obs_lat ; //Latitude always stays the same obs.z= 4000.0*sin(lat*PI/180.0); //Observer's vector from earth's center (secondary coords) obs.y=(4000.0*cos(lat*PI/180.0))*sin(lon*PI/180.0); // " obs.x=(4000.0*cos(lat*PI/180.0))*cos(lon*PI/180.0); // " //Change vector in secondary system to vector in primary system coords earth_observer.x=a11*obs.x+a12*obs.y+a13*obs.z; //Now transform to primary system earth_observer.y=a21*obs.x+a22*obs.y+a23*obs.z; // " earth_observer.z=a31*obs.x+a32*obs.y+a33*obs.z; // " //Vector from sun to observer is sum of vector from sun to earth plus observer's vector from earth center sun_observer.x=sun_earth.x+earth_observer.x; sun_observer.y=sun_earth.y+earth_observer.y; sun_observer.z=sun_earth.z+earth_observer.z; //The dot-product of the 'earth_observer' vector and 'sun_observer' vector allows us to calculate the angle dp=dot_product(&sun_observer,&earth_observer); *angle=acos(dp/(vector_length(&sun_observer)*vector_length(&earth_observer))); *angle*=180.0/PI; //Convert radians to degrees return(0); bad_exit: return(1); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - int calc_sun_earth_vector(datim *dt,VECTOR *se) { long it; //Time in seconds past perihelion (integer) double t; //Time in seconds past perihelion (floating point) double M; //The "mean" anomaly angle double E; //The "eccentric" anomaly angle double v; //Angle, in radians, of earth in its orbit from its initial position double r; //Polar coordinate 'radius' of earth int i; if (date_to_time(dt,&it)) { //Convert date/time structure to a time 't' in seconds since perihelion printf("calc_sun_earth_vector: cannot use input date/time\\n"); goto bad_exit; } t=it; //Convert time to float M=2*PI*t/SECONDS_PER_ORBIT; //Calculate Kepler's "Mean" anomaly /* The following equation solves for E, given M, using the first 3 terms of an infinite series. The equation that it's solving is Kepler's relationship between the Mean anomaly M and the Eccentric anomaly E...

E = M - e * sin E

*/ E=M //Compute Kepler's "Eccentric" anomaly (approximately) +(ECCENTRICITY-1.0/8.0*pow(ECCENTRICITY,3.0))*sin(1.0*M) //using the first three terms of an infinite series + 1.0/2.0*pow(ECCENTRICITY,2.0) *sin(2.0*M) // " + 3.0/8.0*pow(ECCENTRICITY,3.0) *sin(3.0*M); // " /* Another way to solve for E is iteratively using the conditions...

E[0] = M E[i+1] = M + e * sin( E[i] )

E=M; for (i=0;ix=r*cos(v); //Convert polar to cartesian se->y=r*sin(v); // " se->z=0.0; //There's never any Z component for earth's center return(0); bad_exit: return(1); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - int print_vec(VECTOR *v) { printf("%.1fx %.1fy %.1fz",v->x,v->y,v->z); return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - -

// VCTRSUPPORT.h //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = //= = = = = = = = = = = = = = = 3D VECTOR AND GEOMETRIC PROCESSING ROUTINES = = = = = = = = = = = = = = = = = = = = //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = /* Minimum angle to call lines parallel or perpendicular is arbitrarily set to 0.1 degrees. The sine of 0.1 degrees is 0.001745.

*/ #define MIN_SINE 0.001745 #define MIN_COSINE 0.001745 //- - - - - - - - - DEFINE GEOMETRIC TYPES (POINT, VECTOR, LINE, PLANE, TRIANGLE, etc) - - - - - - - - - - - - - -

typedef struct { //A POINT has a single definition and is defined by... double x,y,z; //Its 3 orthogonal (x,y,z) coords rel to the origin } POINT;

typedef struct { //A VECTOR has a single definition and is defined by... double x,y,z; //Its 3 orthogonal (x,y,z) composite vector lengths } VECTOR;

typedef struct { //A LINE's preferred definition is... POINT lp; //Any arbitrary POINT on the line VECTOR lv; //And a direction VECTOR } LINE; /* Note: In geometry a line has no inherit "direction" but in our "vector" description the VECTOR will allow us to consider the LINE to be "flowing" in one of two directions as well as to distinguish between the 'left' and 'right' sides of the LINE.

*/ typedef struct { //A line may also be defined by... POINT lp1; //Any two points POINT lp2; //On the line } line_2pt;

typedef struct { //A PLANE's preferred definition is in Hessian form... VECTOR pn; //A (possibly unit) VECTOR normal to the plane double d; //And d, the distance of the plane from the origin } PLANE; typedef struct { //Or, in pure scalar (coordinate) terms, as... double A,B,C,D; //The general equation Ax+By+Cz+D=0 } plane_coord; typedef struct { //Or, as... POINT pp; //Any point on the plane VECTOR pv1,pv2; //And 2 non-parallel direction vectors } plane_pt2v;

typedef struct { //A TRIANGLE is defined preferably by... POINT tp; //A POINT VECTOR tv1,tv2; //And 2 non-parallel VECTORs which define the other two corners } TRIANGLE; typedef struct { //A TRIANGLE may also be defined by... POINT tp1,tp2,tp3; //Three distinct non-colinear POINTs } triangle_3pt;

//- - - - - - - - - - - - FUNCTION PROTOTYPES OF BASIC GEOMETRIC OPERATIONS - -

- - - - - - - - - int vector_1_point (POINT *p,VECTOR *v); int vector_2_points (POINT *p1,POINT *p2,VECTOR *v); double distance_2_points (POINT *p1,POINT *p2); double vector_length (VECTOR *v); int vector_add (VECTOR *v1,VECTOR *v2,VECTOR *v3); int vector_sub (VECTOR *v1,VECTOR *v2,VECTOR *v3); int vector_mpy (VECTOR *v,double scale); int vector_resize (VECTOR *v,double vector_len); double dot_product (VECTOR *v1,VECTOR *v2); int cross_product (VECTOR *v1,VECTOR *v2,VECTOR *v3); int plane_3_points (POINT *a,POINT *b,POINT *c,PLANE *pl); int intersect_line_plane (LINE *l,PLANE *pl,POINT *p); int plane_from_pt_2v (POINT *p,VECTOR *v1,VECTOR *v2,PLANE *pl); int lines_2_closest_points (LINE *l1,LINE *l2,POINT *pa,POINT *pb); double distance_2_lines (LINE *l1,LINE *l2,LINE *l3); int intersect_2_planes (PLANE *pl1,PLANE *pl2,LINE *l); int triangle_from_3pt (POINT *p1,POINT *p2,POINT *p3,TRIANGLE *t); int line_2_points (POINT *p1,POINT *p2,LINE *l); int vector_rotate (VECTOR *X,VECTOR *U,double theta); //- - - - - - - - - - - - - FUNCTION CODE FOR BASIC GEOMETRIC OPERATIONS - - - -

- - - - - - - /* A VECTOR is constructed from the origin to a POINT. If the point is the origin itself a null vector will result, but this is not a problem. Zero is always returned.

*/ int vector_1_point(POINT *p,VECTOR *v) { v->x=p->x; v->y=p->y; v->z=p->z; return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - /* A VECTOR is constructed from two POINTs. If the two points are the same then a null vector will result, but this is not a problem. Zero is always returned.

*/ int vector_2_points(POINT *p1,POINT *p2,VECTOR *v) { v->x=p2->x-p1->x; v->y=p2->y-p1->y; v->z=p2->z-p1->z; return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - /* The distance between two points is calculated via the Pythagorean theorem. If the points are identical the value 0.0 will be returned so no check is needed.

*/ double distance_2_points(POINT *p1,POINT *p2) { return sqrt((p2->x - p1->x) * (p2->x - p1->x) + (p2->y - p1->y) * (p2->y - p1->y) + (p2->z - p1->z) * (p2->z - p1->z)); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - /* The length of a VECTOR is returned. No check for a null vector is needed.

*/ double vector_length(VECTOR *v) { return sqrt(v->x * v->x + v->y * v->y + v->z * v->z); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - /* Add two VECTORs v1 and v2, giving a third VECTOR v3. No check for null vectors is needed. Zero is always returned.

*/ int vector_add(VECTOR *v1,VECTOR *v2,VECTOR *v3) { v3->x=v1->x+v2->x; v3->y=v1->y+v2->y; v3->z=v1->z+v2->z; return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - /* Subtract VECTOR v2 from v1, giving a third VECTOR v3. No check for null vectors is needed. Zero is always returned.

*/ int vector_sub(VECTOR *v1,VECTOR *v2,VECTOR *v3) { v3->x=v1->x-v2->x; v3->y=v1->y-v2->y; v3->z=v1->z-v2->z; return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - /* Multiply the lengths of each of the three components of a VECTOR 'v' by 'scale'. The vector 'v' is modified as a result. 'scale' may be 0.0 resulting in 'v' becoming the null vector. This is not a problem. Zero is always returned.

*/ int vector_mpy(VECTOR *v,double scale) { v->x*=scale; v->y*=scale; v->z*=scale; return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - /* Resize the VECTOR 'v' so its length is 'vector_len'. The vector 'v' is modified as a result. 'vector_len' may be 0.0 resulting in 'v' becoming the null vector. This is not a problem. If 'vector_len' is less than zero then the direction of 'v' will be reversed because the sign of each of its component vectors will be reversed. In this case the length of 'v' will be set to the absolute value of 'vector_len'. Again, this is not a problem.

*/ int vector_resize(VECTOR *v,double vector_len) { double factor; /* if (vector_lenx*=factor; v->y*=factor; v->z*=factor; //And apply it to all the components } return(0); //Return success } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - /* The dot product of the two vectors v1 and v2 is returned. If the two vectors are *essentially* perpendicular return 0.0.

*/ double dot_product(VECTOR *v1,VECTOR *v2) { double temp,dp; if (v1->x==0.0 && v1->y==0.0 && v1->z==0.0 || //If either v2->x==0.0 && v2->y==0.0 && v2->z==0.0) { //vector is NULL /* printf("v=null "); */ return(0.0); //Pretend they are perpendicular and return 0.0 } dp=v1->x * v2->x + v1->y * v2->y + v1->z * v2->z; //Compute dot product if (dp==0.0) { //If dot product has gone to zero /* printf("dp=0.0 "); */ return(0.0); //Vectors must be perpendicular so return 0.0 } /* Since the magnitude of the dot product of two vectors is the product of their magnitudes times the cosine of the angle between them I can solve for the cosine of the angle between them by dividing the already computed dot product's magnitude by the product of their individual magnitudes. If this cosine is close to zero then the angle must be close to 90 degrees. If so, the vectors are 'essentially' perpendicular. */ temp=vector_length(v1)*vector_length(v2); //Get product of vector lengths if (temp==0.0) { //If product of vector lengths has gone to zero /* printf("v*v=null "); */ return(0.0); //Vectors must be perpendicular } if (fabs(dp)/tempx==0.0 && v1->y==0.0 && v1->z==0.0 || //If either v2->x==0.0 && v2->y==0.0 && v2->z==0.0) { //vector is null v3->x=0.0; //Set the output vector to null v3->y=0.0; v3->z=0.0; return(1); //Pretend parallel and return 1 } v3->x=v1->y * v2->z - v2->y * v1->z; v3->y=v1->z * v2->x - v2->z * v1->x; v3->z=v1->x * v2->y - v2->x * v1->y; if (v3->x==0.0 && v3->y==0.0 && v3->z==0.0) { //If computed vector is null return(1); //The inputs must have been parallel, return 1 } /* THIS OPTIONAL CODE IMPLEMENTS THE "ESSENTIALLY PARALLEL" LOGIC IF YOU WANT IT

Since the magnitude of the cross product of two vectors is the product of their magnitudes times the sine of the angle between them I can solve for the sine of the angle between them by dividing the already computed cross product's magnitude by the product of their individual magnitudes. If this sine is close to zero then the angle must be close to zero degrees. If so, the vectors are 'essentially' parallel.

divisor=vector_length(v1)*vector_length(v2); //Calc product of input magnitudes if (divisor==0.0) //If divisor has gone to zero input vectors too small to work with return(1); //Return the result as if parallel if ((vector_length(v3)/divisor)x - a->x; //Create a VECTOR from POINT 'a' to POINT 'b' temp_v1.y = b->y - a->y; temp_v1.z = b->z - a->z; temp_v2.x = c->x - a->x; //Create a VECTOR from POINT 'a' to POINT 'c' temp_v2.y = c->y - a->y; temp_v2.z = c->z - a->z; if (plane_from_pt_2v(a,&temp_v1,&temp_v2,pl)==0) //Transform to a PLANE return(0); //If subcontract went OK return 0 else //Otherwise, something's wrong with our input (two points same?) return(1); //So return error } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - /* To calculate the intersection of a line and a plane use the vector definitions of each... _ _ n . p = d (normal vector and distance def. of PLANE) _ _ _ p = p1 + t * l (point and vector def. of LINE) _ _ _ _ _ _ n . (p1 + t * l) = d (subst p1 + t * l for p in PLANE equation) _ _ _ _ n . p1 + t * n . l = d (using distributive law) _ _ _ _ t = (d - n . p1) / (n . l) (solve for t) _ Plug value for t into LINE eq. to get p

*/ int intersect_line_plane(LINE *l,PLANE *pl,POINT *p) { double t,temp; VECTOR p1v; if ((temp=dot_product(&l->lv,&pl->pn))==0.0) //If line and plane parallel return(1); //Return bad status 1 p1v.x=l->lp.x; p1v.y=l->lp.y; p1v.z=l->lp.z; t=(pl->d-dot_product(&pl->pn,&p1v))/temp; p->x = l->lp.x + t * l->lv.x; p->y = l->lp.y + t * l->lv.y; p->z = l->lp.z + t * l->lv.z; return(0); //Return normal status 0 } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - /* Compute the Hessian form of the plane from the 2-vectors-and-point form. _ _ n . p = d (normal vector and distance def. of PLANE) _ _ _ _ _ n = (v1 X v2) / |v1 X v2| (def. of unit normal vector to PLANE) _ _ _ _ _ d = ((v1 X v2) / |v1 X v2|) . p (solve for d)

*/ int plane_from_pt_2v(POINT *p,VECTOR *v1,VECTOR *v2,PLANE *pl) { double temp; VECTOR tempv; if (cross_product(v1,v2,&pl->pn)==1) //If 2 vectors parallel return(1); //Return error temp=vector_length(&pl->pn); pl->pn.x/=temp; pl->pn.y/=temp; pl->pn.z/=temp; //Make norm vector unit tempv.x=p->x; tempv.y=p->y; tempv.z=p->z; pl->d=dot_product(&pl->pn,&tempv); return(0); //Normal return } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - /* Given two lines 'l1' and 'l2' determine the point 'pa' on line 'l1' and the point 'pb' on line 'l2' that are the two closest points between the lines. The normal return is the value 0. This occurs even when the two lines intersect and, therefore, 'pa' and 'pb' are the same point. This is not an error. If the given lines are parallel then there are no unique points you can call 'closest'. In that case a 1 is returned as an error indication and the resulting values for 'pa' and 'pb' are undefined.

*/ int lines_2_closest_points(LINE *l1,LINE *l2,POINT *pa,POINT *pb) { VECTOR c; PLANE pl1,pl2; if (cross_product(&l1->lv,&l2->lv,&c)==1) { /* If lines are parallel */ return(1); } /* In the next 4 function calls (plane_from_pt_2v and intersect_line_plane) the check for an error '1' return is for safety only. Because the two input lines have already been shown to be non-parallel these calculations *should* return a normal result. If, though, for any reason these functions *do* return an error result it might be because of precision problems and so we must then return error '1' just as if our first test for parallel lines had been true. It should never happen if we get this far. */ /* form plane containing line l1, parallel to vector c */ if (plane_from_pt_2v(&l1->lp,&l1->lv,&c,&pl1)==1) return(1); /* form plane containing line l2, parallel to vector c */ if (plane_from_pt_2v(&l2->lp,&l2->lv,&c,&pl2)==1) return(1); /* closest point on l1 is where l1 intersects pl2 */ if (intersect_line_plane(l1,&pl2,pa)==1) return(1); /* closest point on l2 is where l2 intersects pl1 */ if (intersect_line_plane(l2,&pl1,pb)==1) return(1); /* Interesting, but not necessary, code for distinguishing between intersecting or skew lines. if (distance_2_points(pa,pb)==0.0) //Intersecting else //Skew */ return(0); //Normal return } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - double distance_2_lines(LINE *l1,LINE *l2,LINE *l3) { POINT p1,p2; if (lines_2_closest_points(l1,l2,&p1,&p2)==1) //If the lines are parallel return(-1.0); //Return an error (in future could return singular value) l3->lp.x=p1.x; l3->lp.y=p1.y; l3->lp.z=p1.z; l3->lv.x=p2.x-p1.x; l3->lv.y=p2.y-p1.y; l3->lv.z=p2.z-p1.z; return(distance_2_points(&p1,&p2)); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - /* The intersection of the two planes pl1 and pl2 is returned as a line in point-vector format in 'l'. If the planes are parallel 1 is returned. Otherwise, 0.

*/ int intersect_2_planes(PLANE *pl1,PLANE *pl2,LINE *l) { VECTOR *v1,*v2; double determinant,c1,c2,d1,d2; if (cross_product(&pl1->pn,&pl2->pn,&l->lv)==1) return(1); /* OK, they're not parallel, use Paul Bourke algorithm. */ v1=&pl1->pn; d1=pl1->d; v2=&pl2->pn; d2=pl2->d; determinant=dot_product(v1,v1)*dot_product(v2,v2) -dot_product(v1,v2)*dot_product(v1,v2); /* Next check catches boundary condition where cross product not zero but determinant is, so must still call the planes parallel. */ if (determinant==0.0) return(1); c1=(d1*dot_product(v2,v2)-d2*dot_product(v1,v2))/determinant; c2=(d2*dot_product(v1,v1)-d1*dot_product(v1,v2))/determinant; l->lp.x=c1*v1->x+c2*v2->x; l->lp.y=c1*v1->x+c2*v2->y; l->lp.z=c1*v1->x+c2*v2->z; return(0); } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - int triangle_from_3pt(POINT *p1,POINT *p2,POINT *p3,TRIANGLE *t) { VECTOR temp; t->tp.x = p1->x; t->tp.y = p1->y; t->tp.z = p1->z; t->tv1.x = p2->x - p1->x; t->tv1.y = p2->y - p1->y; t->tv1.z = p2->z - p1->z; t->tv2.x = p3->x - p1->x; t->tv2.y = p3->y - p1->y; t->tv2.z = p3->z - p1->z; if (cross_product(&t->tv1,&t->tv2,&temp)==1) //If vector from p1->p2 parallel to vector from p1->p3 return(1); //Return error, points are colinear or two points are the same else //Otherwise return(0); //Normal return } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - /* Return the preferred 'point-vector' definition of a line given two points on the line. This routine always returns POINT p1 as the 'point' and the directed vector going from POINT p1 to POINT p2 as the 'vector'. A check is made, however, to ensure that both p1 and p2 are not the same POINT.

*/ int line_2_points(POINT *p1,POINT *p2,LINE *l) { l->lp.x=p1->x; l->lp.y=p1->y; l->lp.z=p1->z; //LINE 'l' defined by POINT 'p1' l->lv.x=p2->x-p1->x; l->lv.y=p2->y-p1->y; l->lv.z=p2->z-p1->z; //And vector p1->p2 if (l->lv.x==0.0 && l->lv.y==0.0 && l->lv.z==0.0) //If the vector is null, points were the same return(1); //So return bad status else //Otherwise, points were not the same return(0); //Return normal status } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - /* Rotate the VECTOR X about the VECTOR U through an angle of 'theta' radians. Note that if X and U are pointing in the same direction their cross product returns the null vector. This is not a problem because it simply causes X to be returned unchanged as it ought to.

*/ int vector_rotate(VECTOR *X,VECTOR *U,double theta) { VECTOR temp_u,temp_x,temp_v1,temp_v2; double temp; temp_u=*U; //Make a copy of U so we can resize it vector_resize(&temp_u,1.0); //Make it a unit vector for the following calculations temp_x=*X; //Make a copy of X to work with vector_mpy(&temp_x,cos(theta)); //Now have X*cos(theta) [in temp_x] temp=dot_product(&temp_u,&temp_x)*(1.0-cos(theta)); //Now have (U.X)(1-cos(theta)) [in temp] temp_v1=temp_u; //Prepare to multiply U by result of previous line vector_mpy(&temp_v1,temp); //Now have U*(U.X)(1-cos(theta)) [in temp_v1] cross_product(&temp_u,X,&temp_v2); //Now have (UxX) vector_mpy(&temp_v2,sin(theta)); //Now have (UxX)*sin(theta) [in temp_v2] vector_add(&temp_v1,&temp_v2,X); //Now have U*(U.X)(1-cos(theta))+(UxX)*sin(theta) [in X] vector_add(&temp_x,X,X); //Now have X*cos(theta)+U*(U.X)(1-cos(theta))+(UxX)*sin(theta) [in X] return(0); } //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Reply to
TR Oltrogge

urs 56 minutes 4 seconds (a sidereal day). So

d longitude his effective longitude increases

I still think it is remarkable that there is a large section of people who have actually found a way to believe the nonsense of a correlation between the Earth's rotation through 360 degrees and the value of 23 hours 56 min 04 seconds.If axial rotation is constant,and it has not being isolated as an independent motion to be checked ,the value will almost certainly be 24 hours or,in other words,it will nowhere near 23 hours 56 minutes 04 seconds.

Surely somebody has an interest in how clocks were used to determine location on the planet through theiolate inv correlation of 4 minutes of clock time for each degree of geographical seperation making precisely 24 hours/360 degrees. As latitude on the planet is easy enough to determine,a specific value in miles was assigned to each degree depending how far from the equator or poles a person determined his position to be -

formatting link
It was East to West that was so difficult to determine which is why it was important to have accurate clocks which could maintain a standard pace and not run fast or slow .Using natural noon as a benchmark,and using the correction which equalises variations in natural noon to an average 24 hours and keeps the 24 hour cycles elapsing into each other,great men exploited this principle and overlayed it on terrestrial geometry.when they created the system after Copernicus discovered axial rotation to be the cause of the daily cycle ,they did not know why the natural noon cycles were unequal and it was irrelevent for the principles on which the correlation between clocks,the axial cycle and longitudes are dependent.

Is it so difficult to see how the natural noon benchmark provides the basis for the Equation of Time tranfer of the average 24 hour day to the axial cycle as a 'constant',not as an observed occurence but simply as a convenient assumption used to allow clocks to measure distance,the still the basis of the GPS system.Even allowing that the vast majority here subscribe to the Piltdown man syndrome where proponents were allowed to die off peacefully (took 40 years to do it),I find it hard to believe that not one individual has intelligence to admire what our ancestors achieved and how they achieved it,first the creation of the 24 hour day and then the heliocentric adaption to longitudes and terrestrial geography.

There is so much junk in what you wrote,the unfortunate thing is that everyone will applaud you.

Reply to
oriel36

minutes 4 seconds (a sidereal day). So

longitude his effective longitude increases

How would you measure the rotation: suppose you landed on this planet from outer space, with your own time-measuring devices (that were marked in some totally alien units, independent of Earthlings' "hours"), and were wondering what the rotation period of the planet was. By what observation would you take that measurement? (Observing the apparent position of distant stars? Observing the position of sun? Using a Foucault pendulum at the pole?)

-dave w

Reply to
David Weinshenker

Which is very much the situation with us as the "aliens" when we measure the rotation period of every other body in the Solar System. We specify the period with respect to the stars (which is exactly what you would get with an inertial measurement, of course). In some cases, we _measure_ with respect to something else, such as an orbiting spacecraft. But then we convert to the inertial (sidereal) value. _________________________________________________

Chris L Peterson Cloudbait Observatory

formatting link

Reply to
Chris L Peterson

Yeah, it seems that such distinctions are lost on "oriel36", who seems to believe that there is something inherent or absolute about the "24 hours = 360 degrees of rotation" relationship (independent of how that rotation is measured), appears to be unaware that the observation of stellar transits -is- a way of "isolating axial rotation as an independent motion to be checked". and appears not to have spent enough time observing the night sky to note that particular stars do in fact rise "earlier" (relative to the solar day of terrestrial timekeeping) each night.

-dave w

Reply to
David Weinshenker

3 hours 56 minutes 4 seconds (a sidereal day). So

e and longitude his effective longitude increases

Go ask the guys at the South Pole,they would have no choice but to work on the 24 hour/360 degree correlation because that is what Huygens and Harrison worked off -

"The pendulum will travel in a circle relative to the floor a distance of (3600)Sinf in 24 hours, ... The latitude at Sonoma State University is 38 Deg 20=92 so the pendulum will subtend an angle of 223Deg in 24 hours, i.e., it has a period of 38 hours, 45 minutes. At the equator the latitude is 0o and (360)Sin0 =3D 0, the pendulum will not appear to rotate at all but only swing back and forth remaining in the same plane relative to the floor. At the South Pole the latitude is 90 Deg and (360)Sin90 =3D 360 deg so the pendulum will make one complete revolution in one day. This makes sense because the Earth rotates once on its axis every 24 hours and the South Pole marks the Earth=92s axis of rotation "

formatting link

No offence to hypothetical 'outer space' reasoning,this is actually a technical and engineering argument drawn from millenia of astronomical refinements.If you cannot understand how the Equation of Time transfers the average 24 hour day to the axial cycle as a constant then you have a rather large problem that I cannot help you with,the same goes for everyone else.

With a srtetch I can say it must be some obligation to your peers but personally the value of 23 hours 56 minutes 04 seconds assigned to axial rotation through 360 degrees is on par with a flat Earth notion,that is no exaggeration.

Reply to
oriel36

A clock is a clock,it does not have any magical properties and basically functions as an object which keeps a regular pace.That is all that was needed to determine geographical seperation where 4 minutes of clock time equals about 69 miles at the equator and diminishing values further towards the poles,you can say it as 1 degree of longitude,or 1 degree of geographical seperation but ultimately it is all held together by the original core principles which distinguish natural noon and 24 hour clcok noon .

formatting link
I do not know why people here choose to insult the great structural and timekeeping astronomers unless they have no self-respect and genuinely want to believe in a correlation which has all the substance of a flat Earth idea.

Reply to
oriel36

If using the transit of stars as a reference for the earth's rotation is "formalized astrology", then what would you call using the noon cycle as such a reference? "Formalized heliology"?

-dave w

Reply to
David Weinshenker

minutes 4 seconds (a sidereal day). So

his effective longitude increases

OMG, are we going to rehash this nonsense YET AGAIN??????????? Just google if you really want to see the same inane arguments over and over again. There's nothing new to say here!

Reply to
NoSuchPerson

Kelleher, aka Oriel36, is a well known crank who seems to be fixated upon a geocentric universe.

Definitely not worth the effort.

Reply to
Greg Neill

Cabling-Design.com Forums website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.