1
2(********************************************************************)
3(*                                                                  *)
4(*  planets.sd7   Display information about the planets             *)
5(*  Copyright (C) 2006, 2007  Thomas Mertes                         *)
6(*                                                                  *)
7(*  This program is free software; you can redistribute it and/or   *)
8(*  modify it under the terms of the GNU General Public License as  *)
9(*  published by the Free Software Foundation; either version 2 of  *)
10(*  the License, or (at your option) any later version.             *)
11(*                                                                  *)
12(*  This program is distributed in the hope that it will be useful, *)
13(*  but WITHOUT ANY WARRANTY; without even the implied warranty of  *)
14(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *)
15(*  GNU General Public License for more details.                    *)
16(*                                                                  *)
17(*  You should have received a copy of the GNU General Public       *)
18(*  License along with this program; if not, write to the           *)
19(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
20(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
21(*                                                                  *)
22(********************************************************************)
23
24
25$ include "seed7_05.s7i";
26  include "time.s7i";
27  include "duration.s7i";
28  include "float.s7i";
29  include "keybd.s7i";
30  include "echo.s7i";
31  include "line.s7i";
32  include "draw.s7i";
33  include "graph_file.s7i";
34  include "window.s7i";
35  include "stars.s7i";
36  include "math.s7i";
37
38var text: scr is STD_NULL;
39
40const float:   julianDayOfEpoch is 2451545.0; # 2000-01-01 12:00:00
41const integer: numberOfPlanets is 9;
42const integer: centerX is 440;
43const integer: centerY is 345;
44const integer: panoramaHorizont is 700;
45const integer: panoramaXMin is 62;
46const float:   sizePanorama is 900.0;
47const integer: windowWidth is 1024;
48const integer: windowHeight is 768;
49const float:   screenAspect is 1.0;
50const integer: scaleForInnerPlanets is 210;
51const integer: scaleForOuterPlanets is 9;
52const float:   radianToDegrees is 57.295779513082320876798154814114;
53const float:   degreesToRadian is 0.017453292519943295769236907684883;
54
55var boolean: displayDegreesInDecimal is FALSE;
56var boolean: displayHourAngleInDegrees is FALSE;
57var boolean: displayRAinDegrees is FALSE;
58
59
60const type: orbitType is new struct
61    var string: name is "";
62    var float: orbitalPeriod is 0.0;                   # days
63    var float: meanLongitudeAtEpoch is 0.0;            # deg
64    var float: longitudeOfPerihelion is 0.0;           # deg
65    var float: longitudeOfPerihelionPerCentury is 0.0; # deg/century
66    var float: eccentricity is 0.0;                    # rad
67    var float: eccentricityPerCentury is 0.0;          # rad/century
68    var float: axis is 0.0;                            # AU
69    var float: axisPerCentury is 0.0;                  # AU/century
70    var float: inclination is 0.0;
71    var float: longitudeOfTheAscendingNode is 0.0;
72    var float: argumentOfThePerihelion is 0.0;
73    var float: size is 0.0;
74    var float: brightness is 0.0;
75  end struct;
76
77var array orbitType: orbitDescr is numberOfPlanets times orbitType.value;
78
79var time: aTime is time.value;
80var integer: currTimeChange is 4;
81
82const type: heliocentricPlanetType is new struct
83    var float: axis is 0.0;
84    var float: eccentricity is 0.0;
85    var float: meanLongitude is 0.0;
86    var float: longitudeOfPerihelion is 0.0;
87    var float: meanAnomaly is 0.0;
88    var float: heliocentricLongitude is 0.0;
89    var float: trueAnomaly is 0.0;
90    var float: radiusVector is 0.0;
91    var float: heliocentricEclipticLatitude is 0.0;
92    var float: projectedHeliocentricLongitude is 0.0;
93    var float: projectedRadius is 0.0;
94  end struct;
95
96const type: eclipticalCoordinateType is new struct
97    var float: geocentricEclipticLongitude is 0.0;
98    var float: geocentricLatitude is 0.0;
99  end struct;
100
101const type: equatorialCoordinateType is new struct
102    var float: rightAscension is 0.0;
103    var float: declination is 0.0;
104  end struct;
105
106const type: horizontalCoordinateType is new struct
107    var float: azimuth is 0.0;
108    var float: altitude is 0.0;
109  end struct;
110
111const type: earthCoordinateType is new struct
112    var float: longitude is -16.42;
113    var float: latitude  is  48.25;
114  end struct;
115
116const type: geocentricPlanetType is new struct
117    var eclipticalCoordinateType: eclipticalCoordinate is eclipticalCoordinateType.value;
118    var equatorialCoordinateType: equatorialCoordinate is equatorialCoordinateType.value;
119    var float: hourAngle is 0.0;
120    var horizontalCoordinateType: horizontalCoordinate is horizontalCoordinateType.value;
121  end struct;
122
123var earthCoordinateType: posAtEarth is earthCoordinateType.value;
124var float: localSiderealTime is 0.0;
125
126
127(* Trigonometric functions for degrees *)
128
129
130const func float: toDegrees (in float: x) is
131  return x * radianToDegrees;
132
133
134const func float: toRadians (in float: x) is
135  return x * degreesToRadian;
136
137
138const func float: sinD (in float: x) is
139  return sin(x * degreesToRadian);
140
141
142const func float: cosD (in float: x) is
143  return cos(x * degreesToRadian);
144
145
146const func float: tanD (in float: x) is
147  return tan(x * degreesToRadian);
148
149
150const func float: asinD (in float: x) is
151  return asin(x) * radianToDegrees;
152
153
154const func float: acosD (in float: x) is
155  return acos(x) * radianToDegrees;
156
157
158const func float: atanD (in float: x) is
159  return atan(x) * radianToDegrees;
160
161
162const func float: atan2D (in float: y, in float: x) is
163  return atan2(y, x) * radianToDegrees;
164
165
166const func float: frac (in float: x) is
167  return x - flt(trunc(x));
168
169
170(* Astronomy functions *)
171
172
173const func float: julianDay (in time: aTime) is
174  return flt(julianDayNumber(aTime)) +
175      ((flt(aTime.second) / 60.0 + flt(aTime.minute)) / 60.0 + flt(aTime.hour - 12)) / 24.0;
176
177
178const func integer: minutes (in float: time) is
179  return abs(trunc(60.0 * (frac(time))));
180
181
182const func integer: seconds (in float: time) is
183  return abs(trunc(60.0 * (frac(60.0 * frac(time)))));
184
185
186const func string: angleAsDegrees (in float: angle) is func
187  result
188    var string: degreesMinutesSeconds is "";
189  begin
190    if displayDegreesInDecimal then
191      degreesMinutesSeconds &:= angle digits 4 lpad 9;
192      degreesMinutesSeconds &:= "° ";
193    else
194      if angle < 0.0 then
195        degreesMinutesSeconds := "-";
196      else
197        degreesMinutesSeconds := " ";
198      end if;
199      degreesMinutesSeconds &:= abs(trunc(angle)) lpad 3;
200      degreesMinutesSeconds &:= "° ";
201      degreesMinutesSeconds &:= minutes(angle) lpad 2;
202      degreesMinutesSeconds &:= "' ";
203      degreesMinutesSeconds &:= seconds(angle) lpad 2;
204      degreesMinutesSeconds &:= "\"";
205    end if;
206  end func;
207
208
209const func string: angleAsHours (in float: angle) is func
210  result
211    var string: hoursMinutesSeconds is "";
212  begin
213    if displayDegreesInDecimal then
214      hoursMinutesSeconds &:= angle / 15.0 digits 4 lpad 8;
215      hoursMinutesSeconds &:= "h";
216    else
217      if angle < 0.0 then
218        hoursMinutesSeconds := "-";
219      else
220        hoursMinutesSeconds := " ";
221      end if;
222      hoursMinutesSeconds &:= abs(trunc(angle / 15.0)) lpad 3;
223      hoursMinutesSeconds &:= "h ";
224      hoursMinutesSeconds &:= minutes(angle / 15.0) lpad 2;
225      hoursMinutesSeconds &:= "m ";
226      hoursMinutesSeconds &:= seconds(angle / 15.0) lpad 2;
227      hoursMinutesSeconds &:= "s";
228    end if;
229  end func;
230
231
232const func float: anomaly (in float: m, in float: eccentricity) is func
233  result
234    var float: anomaly is 0.0;
235  local
236    var float: d is 0.0;
237    var float: e is 0.0;
238  begin
239    e := m;
240    d := -eccentricity * sin(m);
241    while abs(d) > 0.000001 do
242      e := e - d / (1.0 - eccentricity * cos(e));
243      d := e - eccentricity * sin(e) - m;
244    end while;
245    anomaly := 2.0 * atan(sqrt((1.0 + eccentricity) / (1.0 - eccentricity)) * tan(e / 2.0))
246  end func; (* anomaly *)
247
248
249const func float: anomaly2 (in float: m, in float: eccentricity) is func
250  result
251    var float: anomaly is 0.0;
252  local
253    var float: e is 0.0;
254    var float: deltaM is 0.0;
255    var float: deltaE is 0.0;
256  begin
257    e := m - eccentricity * sin(m);
258    repeat
259      deltaM := m - e - eccentricity * sin(e);
260      deltaE := deltaM / (1.0 - eccentricity * cos(e));
261      e +:= deltaE;
262    until abs(deltaE) < 0.000001;
263    anomaly := e;
264  end func; (* anomaly2 *)
265
266
267const func float: eclipticalCoordinatesToRightAscension (in float: l, in float: b) is func
268  result
269    var float: rightAscension is 0.0;
270  local
271    var float: x is 0.0;
272    var float: y is 0.0;
273  begin
274    x := cosD(l);
275    y := sinD(l) * 0.91746406 - tanD(b) * 0.397818676;
276    rightAscension := atan2D(y, x) / 15.0;
277  end func; (* eclipticalCoordinatesToRightAscension *)
278
279
280const func float: eclipticalCoordinatesToDeclination (in float: l, in float: b) is
281  return asinD(sinD(b) * 0.91746406 + cosD(b) * sinD(l) * 0.397818676);
282
283
284const proc: degreesToRange (inout float: degrees) is func
285  begin
286    while degrees > 360.0 do
287      degrees -:= 360.0;
288    end while;
289    while degrees < 0.0 do
290      degrees +:= 360.0;
291    end while;
292  end func;
293
294
295const proc: hoursToRange (inout float: hours) is func
296  begin
297    while hours >= 24.0 do
298      hours -:= 24.0;
299    end while;
300    while hours <  0.0 do
301      hours +:= 24.0;
302    end while;
303  end func;
304
305
306const func float: localSiderealToCivilTime (in float: localSiderealTime,
307    in float: longitude, in time: aTime) is func
308  result
309    var float: localCivilTime is 0.0
310  local
311    var float: t is 0.0;
312    var float: b is 0.0;
313    var float: greenwichSiderealTime is 0.0;
314    var float: greenwichMeanTime is 0.0;
315  begin
316    greenwichSiderealTime := localSiderealTime + longitude / 15.0;
317    if greenwichSiderealTime > 24.0 then
318      greenwichSiderealTime -:= 24.0;
319    end if;
320    if greenwichSiderealTime < 0.0 then
321      greenwichSiderealTime +:= 24.0;
322    end if;
323    t := (julianDay(truncToYear(aTime) - 1 . DAYS) - 2415020.0) / 36525.0;
324    b := 24.0 - 6.6460656 - (2400.051262 * t) - (0.00002581 * t * t) +
325        flt(24 * (aTime.year - 1900));
326    t := greenwichSiderealTime - flt(dayOfYear(aTime)) * 0.0657098 + b;
327    hoursToRange(t);
328    greenwichMeanTime := t * 0.99727;
329    localCivilTime := greenwichMeanTime + flt(aTime.timeZone) / 60.0;
330  end func;
331
332
333const proc: showLocalTime (inout text: win) is func
334  local
335    var string: timeStri is "";
336    const integer: column is 1;
337  begin
338    timeStri := aTime lpad 19;
339    setPos(win, 1, column);
340    writeln(win, timeStri);
341    color(win, light_cyan);
342    case currTimeChange of
343      when {1}:
344        setPos(win, 1, column);
345        write(win, timeStri[.. 4]);
346      when {2}:
347        setPos(win, 1, column + 5);
348        write(win, timeStri[6 .. 7]);
349      when {3}:
350        setPos(win, 1, column + 8);
351        write(win, timeStri[9 .. 10]);
352      when {4}:
353        setPos(win, 1, column + 11);
354        write(win, timeStri[12 .. 13]);
355      when {5}:
356        setPos(win, 1, column + 14);
357        write(win, timeStri[15 .. 16]);
358      when {6}:
359        setPos(win, 1, column + 17);
360        write(win, timeStri[18 .. 19]);
361      when {7}:
362        setPos(win, 1, column + 20);
363        write(win, timeStri[21 .. 26]);
364    end case;
365    color(win, white);
366    writeln(win);
367  end func;
368
369
370const func float: calculateGreenwichMeanSiderealTime (in time: gmTime) is func
371  result
372    var float: greenwichMeanSiderealTime is 0.0;
373  local
374    var float: d is 0.0;
375    var float: d0 is 0.0;
376    var float: h is 0.0;
377    var integer: t is 0;
378  begin
379    d := julianDay(gmTime) - julianDayOfEpoch;
380    d0 := flt(julianDayNumber(gmTime)) + 0.5 - julianDayOfEpoch;
381    h := (flt(gmTime.second) / 60.0 + flt(gmTime.minute)) / 60.0 + flt(gmTime.hour);
382    t := round(d) div 36525;
383    greenwichMeanSiderealTime := 6.697374558 + 0.06570982441908 * d0 +
384                                 1.00273790935 * h + 0.000026 * flt(t * t);
385    hoursToRange(greenwichMeanSiderealTime);
386  end func;
387
388
389const proc: showTime is func
390  local
391    var text: win is STD_NULL;
392    var integer: julianDayNumber is 0;
393    var float: greenwichMeanSiderealTime is 0.0;
394    var time: gmTime is time.value;
395  begin
396    win := openWindow(scr, 2, 58, 8, 32);
397    showLocalTime(win);
398    julianDayNumber := julianDayNumber(aTime);
399    writeln(win, "julianDayNum =" <& julianDayNumber lpad 10);
400    writeln(win, "Longitude    = " <& angleAsDegrees(posAtEarth.longitude));
401    writeln(win, "Latitude     = " <& angleAsDegrees(posAtEarth.latitude));
402    write(win, "localCivilTime        = ");
403    writeln(win, strTime(aTime));
404    gmTime := toUTC(aTime);
405    write(win, "greenwichMeanTime     = ");
406    writeln(win, strTime(gmTime));
407    greenwichMeanSiderealTime := calculateGreenwichMeanSiderealTime(gmTime);
408    write(win, "greenwichSiderealTime = ");
409    write(win, trunc(greenwichMeanSiderealTime) lpad0 2);
410    write(win, ":");
411    write(win, minutes(greenwichMeanSiderealTime) lpad0 2);
412    write(win, ":");
413    writeln(win, seconds(greenwichMeanSiderealTime) lpad0 2);
414    localSiderealTime := greenwichMeanSiderealTime - posAtEarth.longitude / 15.0;
415    hoursToRange(localSiderealTime);
416    write(win, "localSiderealTime     = ");
417    write(win, trunc(localSiderealTime) lpad0 2);
418    write(win, ":");
419    write(win, minutes(localSiderealTime) lpad0 2);
420    write(win, ":");
421    write(win, seconds(localSiderealTime) lpad0 2);
422  end func; (* showTime *)
423
424
425const func heliocentricPlanetType: locatePositionOfPlanetInItsOrbitalPlane (
426    in orbitType: planetDescr) is func
427  result
428    var heliocentricPlanetType: planet is heliocentricPlanetType.value;
429  begin
430    planet.axis := planetDescr.axis +
431        planetDescr.axisPerCentury * (julianDay(aTime) - julianDayOfEpoch) / 36525.0;
432    planet.eccentricity := planetDescr.eccentricity +
433        planetDescr.eccentricityPerCentury * (julianDay(aTime) - julianDayOfEpoch) / 36525.0;
434    planet.meanLongitude := planetDescr.meanLongitudeAtEpoch +
435        360.0 / (planetDescr.orbitalPeriod / orbitDescr[3].orbitalPeriod  * 365.2425) *
436        (julianDay(aTime) - julianDayOfEpoch);
437    degreesToRange(planet.meanLongitude);
438    planet.longitudeOfPerihelion := planetDescr.longitudeOfPerihelion +
439        planetDescr.longitudeOfPerihelionPerCentury * (julianDay(aTime) - julianDayOfEpoch) / 36525.0;
440    planet.meanAnomaly := planet.meanLongitude - planet.longitudeOfPerihelion;
441    planet.trueAnomaly := toDegrees(anomaly(toRadians(planet.meanAnomaly), planet.eccentricity));
442    planet.heliocentricLongitude := planet.trueAnomaly + planet.longitudeOfPerihelion;
443    degreesToRange(planet.heliocentricLongitude);
444    planet.radiusVector := planetDescr.axis * (1.0 - planet.eccentricity * planet.eccentricity) /
445        (1.0 + planet.eccentricity * cosD(planet.trueAnomaly));
446    planet.heliocentricEclipticLatitude := asinD(sinD(planet.heliocentricLongitude -
447        planetDescr.longitudeOfTheAscendingNode) * sinD(planetDescr.inclination));
448  end func;
449
450
451const proc: plotPlanet (in integer: planetNumber, in integer: scale) is func
452  local
453    const func float: computeRadiusVector (in heliocentricPlanetType: planet, in integer: angle) is
454      return planet.axis *
455             (1.0 - planet.eccentricity * planet.eccentricity) /
456             (1.0 + planet.eccentricity * cosD(flt(angle) - planet.longitudeOfPerihelion));
457
458    var heliocentricPlanetType: planet is heliocentricPlanetType.value;
459    var integer: oldX is 0;
460    var integer: oldY is 0;
461    var integer: newX is 0;
462    var integer: newY is 0;
463    var integer: angle is 0;
464  begin
465    planet := locatePositionOfPlanetInItsOrbitalPlane(orbitDescr[planetNumber]);
466    newY := round(flt(scale) * planet.radiusVector * sinD(planet.heliocentricLongitude)) +
467            centerY;
468    newX := round(flt(scale) * planet.radiusVector * cosD(planet.heliocentricLongitude) *
469            screenAspect) + centerX;
470    fcircle(newX, windowHeight - newY, 3, light_cyan);
471    setPosXY(scr, newX + 4, windowHeight - newY);
472    write(scr, orbitDescr[planetNumber].name);
473
474    newX := centerX + round(computeRadiusVector(planet, 0) * flt(scale) * screenAspect);
475    newY := windowHeight - centerY;
476    for angle range 1 to 60 do
477      oldX := newX;
478      oldY := newY;
479      newX := round(computeRadiusVector(planet, angle * 6) * flt(scale) * screenAspect
480              * cosD(flt(angle * 6))) + centerX;
481      newY := windowHeight -
482              (round(computeRadiusVector(planet, angle * 6) * flt(scale) * sinD(flt(angle * 6)))
483              + centerY);
484      lineTo(oldX, oldY, newX, newY, light_cyan);
485    end for;
486  end func; (* plotPlanet *)
487
488
489const proc: showMenu is func
490  local
491    var integer: num is 0;
492  begin
493    clear(black);
494    showTime;
495    setPos(scr, 2, 1);
496    writeln(scr, " P L A N E T S   Display information about the planets");
497    setPos(scr, 4, 1);
498    writeln(scr, " Copyright (C) 2006, 2007  Thomas Mertes");
499    setPos(scr, 6, 1);
500    writeln(scr, " This program is free software under the");
501    writeln(scr, " terms of the GNU General Public License");
502    setPos(scr, 9, 1);
503    writeln(scr, " Planets is written in the Seed7 programming language");
504    writeln(scr, " Homepage:    http://seed7.sourceforge.net");
505    writeln(scr);
506    writeln(scr);
507    writeln(scr, " Possible commands are:");
508    writeln(scr);
509    writeln(scr, " M  Menu");
510    writeln(scr, " I  Plot Inner Planets");
511    writeln(scr, " O  Plot Outer Planets");
512    writeln(scr, " D  Change Date/Time");
513    writeln(scr, " L  Change Long/Lat");
514    writeln(scr, " S  Set_up");
515    writeln(scr, " Q  Quit");
516    for num range 1 to numberOfPlanets do
517      setPos(scr, 14 + num, 25);
518      writeln(scr, " " <& num <& "  " <& orbitDescr[num].name);
519    end for;
520    setPos(scr, 15, 42);
521    writeln(scr, "Horizontal cursor keys:");
522    setPos(scr, 16, 42);
523    writeln(scr, "  Select time step");
524    setPos(scr, 18, 42);
525    writeln(scr, "Vertical cursor keys:");
526    setPos(scr, 19, 42);
527    write(scr,   "  Add or subtract a time step");
528    setPos(scr, 25, 1);
529    writeln(scr, " Enter command:");
530  end func; (* showMenu *)
531
532
533const proc: setup is func
534  local
535    var char: ch is ' ';
536  begin
537    clear(curr_win);
538    setPos(scr, 1, 1);
539    writeln(scr, "Display Degrees in Decimal Degrees (Y/N)? ");
540    ch := getc(KEYBOARD);
541    if upper(ch) = 'Y' then
542      displayDegreesInDecimal := TRUE;
543    elsif upper(ch) = 'N' then
544      displayDegreesInDecimal := FALSE;
545    end if;
546    writeln(scr, "Display Hour Angle in Degrees (Y/N)? ");
547    ch := getc(KEYBOARD);
548    if upper(ch) = 'Y' then
549      displayHourAngleInDegrees := TRUE;
550    elsif upper(ch) = 'N' then
551      displayHourAngleInDegrees := FALSE;
552    end if;
553    writeln(scr, "Display RA in Degrees (Y/N)? ");
554    ch := getc(KEYBOARD);
555    if upper(ch) = 'Y' then
556      displayRAinDegrees := TRUE;
557    elsif upper(ch) = 'N' then
558      displayRAinDegrees := FALSE;
559    end if;
560  end func; (* setup *)
561
562
563const proc: plotInnerPlanets is func
564  local
565    var text: win is STD_NULL;
566    var integer: julianDayNumber is 0;
567    var integer: planetNumber is 0;
568  begin
569    clear(curr_win);
570    win := openWindow(scr, 2, 135, 7, 32);
571    showLocalTime(win);
572    julianDayNumber := julianDayNumber(aTime);
573    writeln(win, "julianDayNum =" <& julianDayNumber lpad 10);
574    writeln(win);
575    writeln(win, "Horizontal cursor keys:");
576    writeln(win, "  Select time step");
577    writeln(win, "Vertical cursor keys:");
578    write(win,   "  Add or subtract a time step");
579    fcircle(centerX, windowHeight - centerY, 3, light_cyan);
580    setPosXY(scr, centerX + 4, windowHeight - centerY);
581    write(scr, "Sun");
582    for planetNumber range 1 to 4 do
583      plotPlanet(planetNumber, scaleForInnerPlanets);
584    end for;
585  end func; (* plotInnerPlanets *)
586
587
588const proc: plotOuterPlanets is func
589  local
590    var text: win is STD_NULL;
591    var integer: julianDayNumber is 0;
592    var integer: planetNumber is 0;
593  begin
594    clear(curr_win);
595    win := openWindow(scr, 2, 135, 7, 32);
596    showLocalTime(win);
597    julianDayNumber := julianDayNumber(aTime);
598    writeln(win, "julianDayNum =" <& julianDayNumber lpad 10);
599    writeln(win);
600    writeln(win, "Horizontal cursor keys:");
601    writeln(win, "  Select time step");
602    writeln(win, "Vertical cursor keys:");
603    write(win,   "  Add or subtract a time step");
604    point(centerX, windowHeight - centerY, light_cyan);
605    plotPlanet(3, scaleForOuterPlanets);
606    for planetNumber range 5 to 9 do
607      plotPlanet(planetNumber, scaleForOuterPlanets);
608    end for;
609  end func; (* plotOuterPlanets *)
610
611
612const proc: changeDateTime is func
613  local
614    var text: win is STD_NULL;
615  begin
616    clear(curr_win);
617    showTime;
618    setPos(scr, 1, 1);
619    case currTimeChange of
620      when {1}:
621        write(scr, "Enter year: ");
622        read(aTime.year);
623      when {2}:
624        write(scr, "Enter month: ");
625        read(aTime.month);
626      when {3}:
627        write(scr, "Enter day: ");
628        read(aTime.day);
629      when {4}:
630        write(scr, "Enter hour: ");
631        read(aTime.hour);
632      when {5}:
633        write(scr, "Enter minute: ");
634        read(aTime.minute);
635      when {6}:
636        write(scr, "Enter second: ");
637        read(aTime.second);
638    end case;
639    showMenu;
640  end func;
641
642
643const proc: changeLongLat is func
644  begin
645    clear(curr_win);
646    showTime;
647    setPos(scr, 1, 1);
648    writeln("Enter Longitude");
649    readln(posAtEarth.longitude);
650    writeln("Enter Latitude");
651    readln(posAtEarth.latitude);
652    showMenu;
653  end func;
654
655
656const func float: computeProjectedHeliocentricLongitude (in heliocentricPlanetType: planet,
657    in orbitType: orbitDescr) is func
658  result
659    var float: projectedHeliocentricLongitude is 0.0;
660  local
661    var float: y is 0.0;
662    var float: x is 0.0;
663    var float: z is 0.0;
664  begin
665    y := sinD(planet.heliocentricLongitude - orbitDescr.longitudeOfTheAscendingNode) *
666         cosD(orbitDescr.inclination);
667    x := cosD(planet.heliocentricLongitude - orbitDescr.longitudeOfTheAscendingNode);
668    z := atan2D(y, x);
669    projectedHeliocentricLongitude := z + orbitDescr.longitudeOfTheAscendingNode;
670  end func;
671
672
673const proc: ProjectPlanetOntoEclipticalPlane (inout heliocentricPlanetType: planet,
674    in orbitType: orbitDescr) is func
675  begin
676    planet.projectedHeliocentricLongitude := computeProjectedHeliocentricLongitude(planet, orbitDescr);
677    degreesToRange(planet.projectedHeliocentricLongitude);
678    planet.projectedRadius := planet.radiusVector * cosD(planet.heliocentricEclipticLatitude);
679  end func;
680
681
682const func eclipticalCoordinateType: calculateEclipticalCoordinates (in heliocentricPlanetType: planet,
683    in heliocentricPlanetType: earth) is func
684  result
685    var eclipticalCoordinateType: eclipticalCoordinate is eclipticalCoordinateType.value;
686  begin
687    if planet.radiusVector < earth.radiusVector then
688      eclipticalCoordinate.geocentricEclipticLongitude := earth.heliocentricLongitude + 180.0 +
689          atanD(planet.projectedRadius * sinD(earth.heliocentricLongitude -
690          planet.projectedHeliocentricLongitude) / (earth.radiusVector -
691          planet.projectedRadius * cosD(earth.heliocentricLongitude -
692          planet.projectedHeliocentricLongitude)));
693    else
694      eclipticalCoordinate.geocentricEclipticLongitude := planet.projectedHeliocentricLongitude +
695          atanD(earth.radiusVector * sinD(planet.projectedHeliocentricLongitude -
696          earth.heliocentricLongitude) / (planet.projectedRadius -
697          earth.radiusVector * cosD(planet.projectedHeliocentricLongitude -
698          earth.heliocentricLongitude)));
699    end if;
700    degreesToRange(eclipticalCoordinate.geocentricEclipticLongitude);
701    eclipticalCoordinate.geocentricLatitude := atanD(planet.projectedRadius *
702        tanD(planet.heliocentricEclipticLatitude) *
703        sinD(eclipticalCoordinate.geocentricEclipticLongitude -
704        planet.projectedHeliocentricLongitude) / (earth.radiusVector *
705        sinD(planet.projectedHeliocentricLongitude - earth.heliocentricLongitude)));
706  end func;
707
708
709const func equatorialCoordinateType: calculateEquatorialCoordinates (
710    in eclipticalCoordinateType: eclipticalCoordinate) is func
711  result
712    var equatorialCoordinateType: equatorialCoordinate is equatorialCoordinateType.value;
713  begin
714    equatorialCoordinate.rightAscension := eclipticalCoordinatesToRightAscension(
715        eclipticalCoordinate.geocentricEclipticLongitude,
716        eclipticalCoordinate.geocentricLatitude);
717    equatorialCoordinate.declination := eclipticalCoordinatesToDeclination(
718        eclipticalCoordinate.geocentricEclipticLongitude,
719        eclipticalCoordinate.geocentricLatitude);
720  end func;
721
722
723const func float: calculateHourAngle (in equatorialCoordinateType: equatorialCoordinate,
724    in float: localSiderealTime) is func
725  result
726    var float: hourAngle is 0.0;
727  begin
728    hourAngle := localSiderealTime - equatorialCoordinate.rightAscension;
729    if hourAngle < 0.0 then
730      hourAngle +:= 24.0;
731    end if;
732  end func;
733
734
735const func horizontalCoordinateType: calculateHorizontalCoordinates (
736    in equatorialCoordinateType: equatorialCoordinate,
737    in earthCoordinateType: posAtEarth, in float: hourAngle) is func
738  result
739    var horizontalCoordinateType: horizontalCoordinate is horizontalCoordinateType.value;
740  begin
741    horizontalCoordinate.altitude := asinD(sinD(equatorialCoordinate.declination) * sinD(posAtEarth.latitude) +
742        cosD(equatorialCoordinate.declination) * cosD(posAtEarth.latitude) *
743        cosD(hourAngle * 15.0));
744    horizontalCoordinate.azimuth := acosD((sinD(equatorialCoordinate.declination) -
745        sinD(posAtEarth.latitude) * sinD(horizontalCoordinate.altitude)) /
746        (cosD(posAtEarth.latitude) * cosD(horizontalCoordinate.altitude)));
747    if sinD(hourAngle * 15.0) > 0.0 then
748      horizontalCoordinate.azimuth := 360.0 - horizontalCoordinate.azimuth;
749    end if;
750  end func;
751
752
753const func geocentricPlanetType: genGeocentricPos (in heliocentricPlanetType: planet,
754    in heliocentricPlanetType: earth, in float: localSiderealTime,
755    in earthCoordinateType: posAtEarth) is func
756  result
757    var geocentricPlanetType: geocentricPos is geocentricPlanetType.value;
758  begin
759    geocentricPos.eclipticalCoordinate := calculateEclipticalCoordinates(planet, earth);
760    geocentricPos.equatorialCoordinate := calculateEquatorialCoordinates(geocentricPos.eclipticalCoordinate);
761    geocentricPos.hourAngle := calculateHourAngle(geocentricPos.equatorialCoordinate, localSiderealTime);
762    geocentricPos.horizontalCoordinate := calculateHorizontalCoordinates(geocentricPos.equatorialCoordinate,
763        posAtEarth, geocentricPos.hourAngle);
764  end func;
765
766
767const func integer: panoramaXPos (in float: azimuth) is
768  return panoramaXMin + round(azimuth / 360.0 * sizePanorama);
769
770
771const func integer: panoramaYPos (in float: altitude) is
772  return panoramaHorizont - round(altitude  / 360.0 * sizePanorama);
773
774
775const proc: drawAllPlanets (in heliocentricPlanetType: earth) is func
776  local
777    var integer: planetNumber is 0;
778    var heliocentricPlanetType: planet is heliocentricPlanetType.value;
779    var geocentricPlanetType: geocentricPos is geocentricPlanetType.value;
780  begin
781    for planetNumber range 1 to 9 do
782      if planetNumber <> 3 then
783        planet := locatePositionOfPlanetInItsOrbitalPlane(orbitDescr[planetNumber]);
784        ProjectPlanetOntoEclipticalPlane(planet, orbitDescr[planetNumber]);
785        geocentricPos := genGeocentricPos(planet, earth, localSiderealTime, posAtEarth);
786        if geocentricPos.horizontalCoordinate.altitude >= 0.0 then
787          fcircle(panoramaXPos(geocentricPos.horizontalCoordinate.azimuth),
788              panoramaYPos(geocentricPos.horizontalCoordinate.altitude), 2, light_cyan);
789        end if;
790      end if;
791    end for;
792  end func;
793
794
795const proc: drawSun (in heliocentricPlanetType: earth) is func
796  local
797    var heliocentricPlanetType: sun is heliocentricPlanetType.value;
798    var geocentricPlanetType: geocentricPos is geocentricPlanetType.value;
799  begin
800    geocentricPos := genGeocentricPos(sun, earth, localSiderealTime, posAtEarth);
801    if geocentricPos.horizontalCoordinate.altitude >= 0.0 then
802      fcircle(panoramaXPos(geocentricPos.horizontalCoordinate.azimuth),
803          panoramaYPos(geocentricPos.horizontalCoordinate.altitude), 4, yellow);
804    end if;
805  end func;
806
807
808const proc: drawHorizont is func
809  local
810    var integer: azimuth is 0;
811    var integer: length is 0;
812    const array string: name is [] ("North", "East", "South", "West", "North");
813    var integer: pos is 1;
814  begin
815    lineTo(0, panoramaHorizont, 1023, panoramaHorizont, white);
816    for azimuth range 0 to 360 do
817      if azimuth rem 90 = 0 then
818        length := 12;
819        setPosXY(scr, panoramaXPos(flt(azimuth)) - 12, panoramaHorizont + 25);
820        writeln(scr, name[pos]);
821        incr(pos);
822      elsif azimuth rem 45 = 0 then
823        length := 10;
824      elsif azimuth rem 10 = 0 then
825        length := 8;
826      elsif azimuth rem 5 = 0 then
827        length := 6;
828      elsif azimuth rem 5 = 0 then
829        length := 4;
830      else
831        length := 2;
832      end if;
833      line(panoramaXPos(flt(azimuth)), panoramaHorizont, 0, length, white);
834    end for;
835  end func;
836
837
838const proc: writePoint (in integer: x, in integer: y, in float: vis) is func
839  begin
840    if vis >= 5.0 then
841      point(x, y, dark_gray);
842    elsif vis >= 4.5 then
843      point(x, y, Gray);
844    elsif vis >= 4.0 then
845      point(x, y, light_gray);
846    elsif vis >= 3.5 then
847      point(x, y, white);
848    elsif vis >= 3.0 then
849      point(x, y, white);
850      point(succ(x), y, dark_gray);
851      point(pred(x), y, dark_gray);
852      point(x, succ(y), dark_gray);
853      point(x, pred(y), dark_gray);
854    elsif vis >= 2.5 then
855      point(x, y, white);
856      point(succ(x), y, Gray);
857      point(pred(x), y, Gray);
858      point(x, succ(y), Gray);
859      point(x, pred(y), Gray);
860    elsif vis >= 2.0 then
861      point(x, y, white);
862      point(succ(x), y, light_gray);
863      point(pred(x), y, light_gray);
864      point(x, succ(y), light_gray);
865      point(x, pred(y), light_gray);
866    elsif vis >= 1.5 then
867      line(pred(x), y, 2, 0, white);
868      point(x, succ(y), white);
869      point(x, pred(y), white);
870    elsif vis >= 1.0 then
871      line(pred(x), y, 2, 0, white);
872      point(x, succ(y), white);
873      point(x, pred(y), white);
874      point(succ(x), succ(y), dark_gray);
875      point(succ(x), pred(y), dark_gray);
876      point(pred(x), succ(y), dark_gray);
877      point(pred(x), pred(y), dark_gray);
878    elsif vis >= 0.5 then
879      line(pred(x), y, 2, 0, white);
880      point(x, succ(y), white);
881      point(x, pred(y), white);
882      point(succ(x), succ(y), Gray);
883      point(succ(x), pred(y), Gray);
884      point(pred(x), succ(y), Gray);
885      point(pred(x), pred(y), Gray);
886    elsif vis >= 0.0 then
887      line(pred(x), y, 2, 0, white);
888      point(x, succ(y), white);
889      point(x, pred(y), white);
890      point(succ(x), succ(y), light_gray);
891      point(succ(x), pred(y), light_gray);
892      point(pred(x), succ(y), light_gray);
893      point(pred(x), pred(y), light_gray);
894    elsif vis >= -0.5 then
895      rect (pred(x), pred(y), 3, 3, white);
896    else
897      fcircle(x, y, 2, white);
898    end if;
899  end func;
900
901
902const proc: drawStars is func
903  local
904    var integer: index is 0;
905    var equatorialCoordinateType: equatorialCoordinate is equatorialCoordinateType.value;
906    var float: hourAngle is 0.0;
907    var horizontalCoordinateType: horizontalCoordinate is horizontalCoordinateType.value;
908    var integer: xPos is 0;
909    var integer: yPos is 0;
910  begin
911    for index range length(stars) downto 1 do
912      equatorialCoordinate.rightAscension := stars[index].rightAscension;
913      equatorialCoordinate.declination := stars[index].declination;
914      hourAngle := calculateHourAngle(equatorialCoordinate, localSiderealTime);
915      horizontalCoordinate := calculateHorizontalCoordinates(equatorialCoordinate,
916          posAtEarth, hourAngle);
917      if horizontalCoordinate.altitude >= 0.0 then
918        xPos := panoramaXPos(horizontalCoordinate.azimuth);
919        yPos := panoramaYPos(horizontalCoordinate.altitude);
920        writePoint(xPos, yPos, stars[index].magnitude);
921        if xPos + round(sizePanorama) < windowWidth then
922          writePoint(xPos + round(sizePanorama), yPos, stars[index].magnitude);
923        end if;
924        if xPos - round(sizePanorama) >= 0 then
925          writePoint(xPos - round(sizePanorama), yPos, stars[index].magnitude);
926        end if;
927      end if;
928    end for;
929  end func;
930
931
932const proc: locatePlanet (in integer: planetNumber) is func
933  local
934    var text: win is STD_NULL;
935    var heliocentricPlanetType: planet is heliocentricPlanetType.value;
936    var heliocentricPlanetType: earth is heliocentricPlanetType.value;
937    var geocentricPlanetType: geocentricPos is geocentricPlanetType.value;
938    var float: azimuthOfRise is 0.0;
939    var float: azimuthOfSet is 0.0;
940    var float: localSiderealTime_Rise is 0.0;
941    var float: localSiderealTime_Set  is 0.0;
942    var float: localCivilTime_Rise is 0.0;
943    var float: localCivilTime_Set  is 0.0;
944    var float: phase is 0.0;
945    var float: distanceFromEarth is 0.0;
946    var float: diameter is 0.0;
947    var float: magnitude is 0.0;
948  begin
949    clear(curr_win, black);
950    showTime;
951    planet := locatePositionOfPlanetInItsOrbitalPlane(orbitDescr[planetNumber]);
952
953    win := openWindow(scr, 2, 3, 7, 51);
954    setPos(win, 1, 1);
955    write  (win, "                          ");
956    writeln(win, orbitDescr[planetNumber].name);
957    writeln(win, "mean longitude       = " <& angleAsDegrees(planet.meanLongitude));
958    writeln(win, "mean anomaly         = " <& angleAsDegrees(planet.meanAnomaly));
959    writeln(win, "true anomaly         = " <& angleAsDegrees(planet.trueAnomaly));
960    writeln(win, "heliocentric long    = " <& angleAsDegrees(planet.heliocentricLongitude));
961    writeln(win, "radius vector        = " <& planet.radiusVector digits 3 lpad 8 <& " AU");
962    write  (win, "helio ecliptic lat.  = " <& angleAsDegrees(planet.heliocentricEclipticLatitude));
963
964    if planetNumber <> 3 then
965
966      earth := locatePositionOfPlanetInItsOrbitalPlane(orbitDescr[3]);
967      win := openWindow(scr, 2, 40, 7, 14);
968      setPos(win, 1, 1);
969      writeln(win, "     Earth");
970      writeln(win, angleAsDegrees(earth.meanLongitude));
971      writeln(win, angleAsDegrees(earth.meanAnomaly));
972      writeln(win, angleAsDegrees(earth.trueAnomaly));
973      writeln(win, angleAsDegrees(earth.heliocentricLongitude));
974      writeln(win, earth.radiusVector digits 3 lpad 8 <& " AU");
975      write  (win, angleAsDegrees(0.0));
976
977
978      (* Project Position of Planet onto plane of ecliptic *)
979      ProjectPlanetOntoEclipticalPlane(planet, orbitDescr[planetNumber]);
980
981      win := openWindow(scr, 11, 12, 2, 68);
982      setPos(win, 1, 1);
983      write(win, "Projected Longitude  = ");
984      write(win, angleAsDegrees(planet.projectedHeliocentricLongitude));
985      writeln(win);
986      write(win, "Projected Radius     = ");
987      write(win, planet.projectedRadius digits 3 lpad 8);
988      write(win, " AU");
989
990
991      (* Calculate Ecliptical Coordinates *)
992      geocentricPos.eclipticalCoordinate := calculateEclipticalCoordinates(planet, earth);
993
994      win := openWindow(scr, 19, 3, 5, 22);
995      setPos(win, 1, 1);
996      writeln(win, "      ECLIPTIC");
997      writeln(win);
998      writeln(win, " Long = " <& angleAsDegrees(
999          geocentricPos.eclipticalCoordinate.geocentricEclipticLongitude));
1000      write(win, " Lat  = " <& angleAsDegrees(
1001          geocentricPos.eclipticalCoordinate.geocentricLatitude));
1002
1003
1004      (* Calculate Equatorial Coordinates *)
1005      geocentricPos.equatorialCoordinate := calculateEquatorialCoordinates(
1006          geocentricPos.eclipticalCoordinate);
1007
1008      win := openWindow(scr, 19, 29, 5, 23);
1009      setPos(win, 1, 1);
1010      writeln(win, "     EQUATORIAL");
1011      writeln(win);
1012      write(win, " RA  = ");
1013      if displayRAinDegrees then
1014        writeln(win, angleAsDegrees(geocentricPos.equatorialCoordinate.rightAscension * 15.0));
1015      else
1016        writeln(win, angleAsHours(geocentricPos.equatorialCoordinate.rightAscension * 15.0));
1017      end if;
1018      writeln(win, " DEC = " <& angleAsDegrees(geocentricPos.equatorialCoordinate.declination));
1019
1020
1021      geocentricPos.hourAngle := calculateHourAngle(geocentricPos.equatorialCoordinate, localSiderealTime);
1022      write(win, " HA  = ");
1023      if displayHourAngleInDegrees then
1024        write(win, angleAsDegrees(geocentricPos.hourAngle * 15.0));
1025      else
1026        write(win, angleAsHours(geocentricPos.hourAngle * 15.0));
1027      end if;
1028
1029
1030      (* Calculate Horizontal Coordinates *)
1031      geocentricPos.horizontalCoordinate := calculateHorizontalCoordinates(
1032          geocentricPos.equatorialCoordinate,
1033          posAtEarth, geocentricPos.hourAngle);
1034
1035      win := openWindow(scr, 19, 56, 5, 23);
1036      setPos(win, 1, 1);
1037      writeln(win, "      HORIZONTAL");
1038      writeln(win);
1039      writeln(win, " Alt  = " <& angleAsDegrees(geocentricPos.horizontalCoordinate.altitude));
1040      write(win, " Azim = " <& angleAsDegrees(geocentricPos.horizontalCoordinate.azimuth));
1041
1042
1043      (* Calculate Time and Position of Rise and Set *)
1044
1045      win := openWindow(scr, 15, 4, 2, 50);
1046      setPos(win, 1, 1);
1047      azimuthOfRise := sinD(geocentricPos.equatorialCoordinate.declination) / cosD(posAtEarth.latitude);
1048      if azimuthOfRise < -1.0 or azimuthOfRise > 1.0 then
1049        if geocentricPos.horizontalCoordinate.altitude < 0.0 then
1050          writeln(win, "never rises above horizon");
1051        else
1052          writeln(win, "never sets below horizon");
1053        end if;
1054      else
1055        azimuthOfRise := acosD(azimuthOfRise);
1056        azimuthOfSet := 360.0 - azimuthOfRise;
1057        localSiderealTime_Rise := 24.0 + geocentricPos.equatorialCoordinate.rightAscension -
1058            (acosD(-tanD(posAtEarth.latitude) *
1059            tanD(geocentricPos.equatorialCoordinate.declination))) / 15.0;
1060        if localSiderealTime_Rise > 24.0 then
1061          localSiderealTime_Rise := localSiderealTime_Rise - 24.0;
1062        end if;
1063        localSiderealTime_Set := geocentricPos.equatorialCoordinate.rightAscension +
1064            (acosD(-tanD(posAtEarth.latitude) *
1065            tanD(geocentricPos.equatorialCoordinate.declination))) / 15.0;
1066        if localSiderealTime_Set > 24.0 then
1067          localSiderealTime_Set := localSiderealTime_Set - 24.0;
1068        end if;
1069
1070        localCivilTime_Set := localSiderealToCivilTime(localSiderealTime_Set,
1071            posAtEarth.longitude, aTime);
1072        if localCivilTime_Set < 0.0 then
1073          localCivilTime_Set := localCivilTime_Set + 24.0;
1074        end if;
1075
1076        localCivilTime_Rise := localSiderealToCivilTime(localSiderealTime_Rise,
1077            posAtEarth.longitude, aTime);
1078        if localCivilTime_Rise < 0.0 then
1079          localCivilTime_Rise := localCivilTime_Rise + 24.0;
1080        end if;
1081
1082        write(win, "Rises at ");
1083        write(win, trunc(localCivilTime_Rise) lpad 2 <& ":" <&
1084            minutes(localCivilTime_Rise) lpad 2 <& " local time");
1085        write(win, "  Azimuth ");
1086        write(win, angleAsDegrees(azimuthOfRise));
1087        writeln(win);
1088        write(win, "Sets at  ");
1089        write(win, trunc(localCivilTime_Set) lpad 2 <& ":" <&
1090            minutes(localCivilTime_Set) lpad 2 <& " local time");
1091        write(win, "  Azimuth ");
1092        write(win, angleAsDegrees(azimuthOfSet));
1093
1094        line(panoramaXPos(azimuthOfRise), panoramaHorizont - 4, 0, 4, light_red);
1095        line(panoramaXPos(azimuthOfSet), panoramaHorizont - 4, 0, 4, light_red);
1096      end if;
1097
1098
1099      (* Calculate Phase, Distance, Diameter, Magnitude *)
1100
1101      win := openWindow(scr, 12, 57, 5, 23);
1102      setPos(win, 1, 1);
1103
1104      phase := (1.0 + cosD(geocentricPos.eclipticalCoordinate.geocentricEclipticLongitude -
1105          planet.heliocentricLongitude)) / 2.0;
1106      write(win, "Phase     = ");
1107      write(win, 100.0 * phase digits 2 lpad 6);
1108      writeln(win, "%");
1109      distanceFromEarth := sqrt(earth.radiusVector * earth.radiusVector
1110                             + planet.radiusVector * planet.radiusVector
1111                             - 2.0 * earth.radiusVector * planet.radiusVector *
1112                             cosD(planet.heliocentricLongitude -
1113                             earth.heliocentricLongitude));
1114      write(win, "Distance  = ");
1115      write(win, distanceFromEarth digits 2 lpad 6);
1116      writeln(win, " AU");
1117
1118      diameter := orbitDescr[planetNumber].size / distanceFromEarth;
1119      write(win, "Diameter  = ");
1120      write(win, diameter digits 2 lpad 6);
1121      writeln(win, "\"");
1122
1123      write(win, "magnitude = ");
1124      if distanceFromEarth * planet.radiusVector /
1125                   orbitDescr[planetNumber].brightness * sqrt(phase) <> 0.0 then
1126        magnitude := 2.17147 * log(distanceFromEarth * planet.radiusVector /
1127                     orbitDescr[planetNumber].brightness * sqrt(phase)) - 26.7;
1128        write(win, magnitude digits 2 lpad 6);
1129      else
1130        write(win, "*****" lpad 6);
1131      end if;
1132
1133      drawHorizont;
1134      drawStars;
1135      if geocentricPos.horizontalCoordinate.altitude >= 0.0 then
1136        fcircle(panoramaXPos(geocentricPos.horizontalCoordinate.azimuth),
1137            panoramaYPos(geocentricPos.horizontalCoordinate.altitude), 2, light_cyan);
1138      end if;
1139      drawSun(earth);
1140    else
1141      drawHorizont;
1142      drawStars;
1143      drawAllPlanets(planet);
1144      drawSun(planet);
1145    end if;
1146  end func; (* locatePlanet *)
1147
1148
1149const proc: showName is func
1150  local
1151    var integer: mouseXPos is 0;
1152    var integer: mouseYPos is 0;
1153    var integer: index is 0;
1154    var equatorialCoordinateType: equatorialCoordinate is equatorialCoordinateType.value;
1155    var float: hourAngle is 0.0;
1156    var horizontalCoordinateType: horizontalCoordinate is horizontalCoordinateType.value;
1157    var heliocentricPlanetType: sun is heliocentricPlanetType.value;
1158    var heliocentricPlanetType: earth is heliocentricPlanetType.value;
1159    var heliocentricPlanetType: planet is heliocentricPlanetType.value;
1160    var geocentricPlanetType: geocentricPos is geocentricPlanetType.value;
1161    var integer: xPos is 0;
1162    var integer: yPos is 0;
1163    var integer: delta is 0;
1164    var integer: minDelta is 0;
1165    var integer: selectedStar is 0;
1166    var integer: selectedPlanet is 0;
1167    var string: name is "";
1168  begin
1169    mouseXPos := getxpos(KEYBOARD);
1170    mouseYPos := getypos(KEYBOARD);
1171    for index range 1 to length(stars) do
1172      equatorialCoordinate.rightAscension := stars[index].rightAscension;
1173      equatorialCoordinate.declination := stars[index].declination;
1174      hourAngle := calculateHourAngle(equatorialCoordinate, localSiderealTime);
1175      horizontalCoordinate := calculateHorizontalCoordinates(equatorialCoordinate,
1176          posAtEarth, hourAngle);
1177      if horizontalCoordinate.altitude >= 0.0 then
1178        xPos := panoramaXPos(horizontalCoordinate.azimuth);
1179        yPos := panoramaYPos(horizontalCoordinate.altitude);
1180        delta := (mouseXPos - xPos) ** 2 + (mouseYPos - yPos) ** 2;
1181        if selectedStar = 0 or delta < minDelta then
1182          selectedStar := index;
1183          minDelta := delta;
1184        end if;
1185        if xPos + round(sizePanorama) < windowWidth then
1186          delta := (mouseXPos - xPos - round(sizePanorama)) ** 2 + (mouseYPos - yPos) ** 2;
1187          if selectedStar = 0 or delta < minDelta then
1188            selectedStar := index;
1189            minDelta := delta;
1190          end if;
1191        end if;
1192        if xPos - round(sizePanorama) >= 0 then
1193          delta := (mouseXPos - xPos + round(sizePanorama)) ** 2 + (mouseYPos - yPos) ** 2;
1194          if selectedStar = 0 or delta < minDelta then
1195            selectedStar := index;
1196            minDelta := delta;
1197          end if;
1198        end if;
1199      end if;
1200    end for;
1201    earth := locatePositionOfPlanetInItsOrbitalPlane(orbitDescr[3]);
1202    for index range 1 to 9 do
1203      if index <> 3 then
1204        planet := locatePositionOfPlanetInItsOrbitalPlane(orbitDescr[index]);
1205        ProjectPlanetOntoEclipticalPlane(planet, orbitDescr[index]);
1206        geocentricPos := genGeocentricPos(planet, earth, localSiderealTime, posAtEarth);
1207        if geocentricPos.horizontalCoordinate.altitude >= 0.0 then
1208          xPos := panoramaXPos(geocentricPos.horizontalCoordinate.azimuth);
1209          yPos := panoramaYPos(geocentricPos.horizontalCoordinate.altitude);
1210          delta := (mouseXPos - xPos) ** 2 + (mouseYPos - yPos) ** 2;
1211          if delta <= minDelta then
1212            selectedStar := 0;
1213            selectedPlanet := index;
1214            minDelta := delta;
1215          end if;
1216        end if;
1217      end if;
1218    end for;
1219    geocentricPos := genGeocentricPos(sun, earth, localSiderealTime, posAtEarth);
1220    if geocentricPos.horizontalCoordinate.altitude >= 0.0 then
1221      xPos := panoramaXPos(geocentricPos.horizontalCoordinate.azimuth);
1222      yPos := panoramaYPos(geocentricPos.horizontalCoordinate.altitude);
1223      delta := (mouseXPos - xPos) ** 2 + (mouseYPos - yPos) ** 2;
1224      if delta <= minDelta then
1225        selectedStar := 0;
1226        selectedPlanet := 0;
1227        minDelta := delta;
1228      end if;
1229    end if;
1230    if selectedStar <> 0 then
1231      equatorialCoordinate.rightAscension := stars[selectedStar].rightAscension;
1232      equatorialCoordinate.declination := stars[selectedStar].declination;
1233      hourAngle := calculateHourAngle(equatorialCoordinate, localSiderealTime);
1234      horizontalCoordinate := calculateHorizontalCoordinates(equatorialCoordinate,
1235          posAtEarth, hourAngle);
1236      xPos := panoramaXPos(horizontalCoordinate.azimuth);
1237      yPos := panoramaYPos(horizontalCoordinate.altitude);
1238      name := stars[selectedStar].name;
1239    elsif selectedPlanet <> 0 then
1240      planet := locatePositionOfPlanetInItsOrbitalPlane(orbitDescr[selectedPlanet]);
1241      ProjectPlanetOntoEclipticalPlane(planet, orbitDescr[selectedPlanet]);
1242      geocentricPos := genGeocentricPos(planet, earth, localSiderealTime, posAtEarth);
1243      xPos := panoramaXPos(geocentricPos.horizontalCoordinate.azimuth);
1244      yPos := panoramaYPos(geocentricPos.horizontalCoordinate.altitude);
1245      name := orbitDescr[selectedPlanet].name;
1246    else
1247      geocentricPos := genGeocentricPos(sun, earth, localSiderealTime, posAtEarth);
1248      xPos := panoramaXPos(geocentricPos.horizontalCoordinate.azimuth);
1249      yPos := panoramaYPos(geocentricPos.horizontalCoordinate.altitude);
1250      name := "Sun";
1251    end if;
1252    if minDelta <= 900 then
1253      setPosXY(scr, xPos + 4, yPos);
1254      write(name);
1255      if xPos + round(sizePanorama) < windowWidth then
1256        setPosXY(scr, xPos + round(sizePanorama) + 4, yPos);
1257        write(name);
1258      end if;
1259      if xPos - round(sizePanorama) >= 0 then
1260        setPosXY(scr, xPos - round(sizePanorama) + 4, yPos);
1261        write(name);
1262      end if;
1263    end if;
1264  end func;
1265
1266
1267const proc: initOrbitDescr is func
1268  begin
1269    orbitDescr[1].name :="Mercury";
1270    orbitDescr[1].orbitalPeriod := 87.96934; # 0.2408469 years
1271    orbitDescr[1].meanLongitudeAtEpoch := 252.25032350;
1272    orbitDescr[1].longitudeOfPerihelion := 77.45779628;
1273    orbitDescr[1].longitudeOfPerihelionPerCentury := 0.16047689;
1274    orbitDescr[1].eccentricity := 0.20563593;
1275    orbitDescr[1].eccentricityPerCentury := 0.00001906;
1276    orbitDescr[1].axis := 0.38709927;
1277    orbitDescr[1].axisPerCentury := 0.00000037;
1278    orbitDescr[1].inclination := 7.00497902;
1279    orbitDescr[1].longitudeOfTheAscendingNode := 48.33167;
1280    orbitDescr[1].argumentOfThePerihelion := 29.12478;
1281    orbitDescr[1].size := 6.74;
1282    orbitDescr[1].brightness := 0.000001918;
1283
1284    orbitDescr[2].name :="Venus";
1285    orbitDescr[2].orbitalPeriod := 224.70069; # 0.6151970 years
1286    orbitDescr[2].meanLongitudeAtEpoch := 181.97909950;
1287    orbitDescr[2].longitudeOfPerihelion := 131.60246718;
1288    orbitDescr[2].longitudeOfPerihelionPerCentury := 0.00268329;
1289    orbitDescr[2].eccentricity := 0.00677672;
1290    orbitDescr[2].eccentricityPerCentury := -0.00004107;
1291    orbitDescr[2].axis := 0.72333566;
1292    orbitDescr[2].axisPerCentury := 0.00000390;
1293    orbitDescr[2].inclination := 3.39467605;
1294    orbitDescr[2].longitudeOfTheAscendingNode := 76.68069;
1295    orbitDescr[2].argumentOfThePerihelion := 54.85229;
1296    orbitDescr[2].size := 16.92;
1297    orbitDescr[2].brightness := 0.00001721;
1298
1299    orbitDescr[3].name :="Earth";
1300    orbitDescr[3].orbitalPeriod := 365.256366; # 1.0000175 years
1301    orbitDescr[3].meanLongitudeAtEpoch := 100.46457166;
1302    orbitDescr[3].longitudeOfPerihelion := 102.93768193;
1303    orbitDescr[3].longitudeOfPerihelionPerCentury := 0.32327364;
1304    orbitDescr[3].eccentricity := 0.01671123;
1305    orbitDescr[3].eccentricityPerCentury := -0.00004392;
1306    orbitDescr[3].axis := 1.00000261;
1307    orbitDescr[3].axisPerCentury := 0.00000562;
1308    orbitDescr[3].inclination := 0.00001531;
1309    orbitDescr[3].longitudeOfTheAscendingNode := 348.73936;
1310    orbitDescr[3].argumentOfThePerihelion := 114.20783;
1311    orbitDescr[3].size := 17.0;
1312    orbitDescr[3].brightness := 0.0;
1313
1314    orbitDescr[4].name :="Mars";
1315    orbitDescr[4].orbitalPeriod := 686.9600; # 1.8808 years
1316    orbitDescr[4].meanLongitudeAtEpoch := 355.44656795;
1317    orbitDescr[4].longitudeOfPerihelion := 336.05637041;
1318    orbitDescr[4].longitudeOfPerihelionPerCentury := 0.44441088;
1319    orbitDescr[4].eccentricity := 0.09339410;
1320    orbitDescr[4].eccentricityPerCentury := 0.00007882;
1321    orbitDescr[4].axis := 1.52371034;
1322    orbitDescr[4].axisPerCentury := 0.00001847;
1323    orbitDescr[4].inclination := 1.84969142;
1324    orbitDescr[4].longitudeOfTheAscendingNode := 49.57854;
1325    orbitDescr[4].argumentOfThePerihelion := 286.46230;
1326    orbitDescr[4].size := 9.36;
1327    orbitDescr[4].brightness := 0.00000454;
1328
1329    orbitDescr[5].name :="Jupiter";
1330    orbitDescr[5].orbitalPeriod := 4333.2867; # 11.86 years
1331    orbitDescr[5].meanLongitudeAtEpoch := 34.39644051;
1332    orbitDescr[5].longitudeOfPerihelion := 14.72847983;
1333    orbitDescr[5].longitudeOfPerihelionPerCentury := 0.21252668;
1334    orbitDescr[5].eccentricity := 0.04838624;
1335    orbitDescr[5].eccentricityPerCentury := -0.00013253;
1336    orbitDescr[5].axis := 5.20288700;
1337    orbitDescr[5].axisPerCentury := -0.00011607;
1338    orbitDescr[5].inclination := 1.30439695;
1339    orbitDescr[5].longitudeOfTheAscendingNode := 100.55615;
1340    orbitDescr[5].argumentOfThePerihelion := 274.19770;
1341    orbitDescr[5].size := 196.74;
1342    orbitDescr[5].brightness := 0.0001994;
1343
1344    orbitDescr[6].name :="Saturn";
1345    orbitDescr[6].orbitalPeriod := 10756.1995; # 29.45 years
1346    orbitDescr[6].meanLongitudeAtEpoch := 49.95424423;
1347    orbitDescr[6].longitudeOfPerihelion := 92.59887831;
1348    orbitDescr[6].longitudeOfPerihelionPerCentury := -0.41897216;
1349    orbitDescr[6].eccentricity := 0.05386179;
1350    orbitDescr[6].eccentricityPerCentury := -0.00050991;
1351    orbitDescr[6].axis := 9.53667594;
1352    orbitDescr[6].axisPerCentury := -0.00125060;
1353    orbitDescr[6].inclination := 2.48599187;
1354    orbitDescr[6].longitudeOfTheAscendingNode := 113.71504;
1355    orbitDescr[6].argumentOfThePerihelion := 338.71690;
1356    orbitDescr[6].size := 165.60;
1357    orbitDescr[6].brightness := 0.0001740;
1358
1359    orbitDescr[7].name :="Uranus";
1360    orbitDescr[7].orbitalPeriod := 30707.4896; # 84.07 years
1361    orbitDescr[7].meanLongitudeAtEpoch := 313.23810451;
1362    orbitDescr[7].longitudeOfPerihelion := 170.95427630;
1363    orbitDescr[7].longitudeOfPerihelionPerCentury := 0.40805281;
1364    orbitDescr[7].eccentricity := 0.04725744;
1365    orbitDescr[7].eccentricityPerCentury := -0.00004397;
1366    orbitDescr[7].axis := 19.18916464;
1367    orbitDescr[7].axisPerCentury := -0.00196176;
1368    orbitDescr[7].inclination := 0.77263783;
1369    orbitDescr[7].longitudeOfTheAscendingNode := 74.22988;
1370    orbitDescr[7].argumentOfThePerihelion := 96.73436;
1371    orbitDescr[7].size := 65.80;
1372    orbitDescr[7].brightness := 0.00007768;
1373
1374    orbitDescr[8].name :="Neptune";
1375    orbitDescr[8].orbitalPeriod := 60223.3528; # 164.88 years
1376    orbitDescr[8].meanLongitudeAtEpoch := 304.87997031;
1377    orbitDescr[8].longitudeOfPerihelion := 44.96476227;
1378    orbitDescr[8].longitudeOfPerihelionPerCentury := -0.32241464;
1379    orbitDescr[8].eccentricity := 0.00859048;
1380    orbitDescr[8].eccentricityPerCentury := 0.00005105;
1381    orbitDescr[8].axis := 30.06992276;
1382    orbitDescr[8].axisPerCentury := 0.00026291;
1383    orbitDescr[8].inclination := 1.77004347;
1384    orbitDescr[8].longitudeOfTheAscendingNode := 131.72169;
1385    orbitDescr[8].argumentOfThePerihelion := 273.24966;
1386    orbitDescr[8].size := 62.20;
1387    orbitDescr[8].brightness := 0.00007597;
1388
1389    orbitDescr[9].name :="Pluto";
1390    orbitDescr[9].orbitalPeriod := 90613.3055; # 248.09 years
1391    orbitDescr[9].meanLongitudeAtEpoch := 238.92903833;
1392    orbitDescr[9].longitudeOfPerihelion := 224.06891629;
1393    orbitDescr[9].longitudeOfPerihelionPerCentury := -0.04062942;
1394    orbitDescr[9].eccentricity := 0.24882730;
1395    orbitDescr[9].eccentricityPerCentury := 0.00005170;
1396    orbitDescr[9].axis := 39.48211675;
1397    orbitDescr[9].axisPerCentury := -0.00031596;
1398    orbitDescr[9].inclination := 17.14001206;
1399    orbitDescr[9].longitudeOfTheAscendingNode := 110.30347;
1400    orbitDescr[9].argumentOfThePerihelion := 113.76329;
1401    orbitDescr[9].size := 8.2;
1402    orbitDescr[9].brightness := 0.000004073;
1403  end func; (* initOrbitDescr *)
1404
1405
1406const proc: main is func
1407  local
1408    var char: ch is ' ';
1409    var char: command is 'M';
1410    var char: old_command is 'M';
1411  begin
1412    screen(windowWidth, windowHeight);
1413    selectInput(curr_win, KEY_CLOSE, TRUE);
1414    clear(curr_win, black);
1415    KEYBOARD := GRAPH_KEYBOARD;
1416    scr := open(curr_win);
1417    IN := openEcho(KEYBOARD, scr);
1418    IN := openLine(IN);
1419    OUT := scr;
1420    aTime := truncToSecond(time(NOW));
1421    initOrbitDescr;
1422
1423    showMenu;
1424    repeat
1425      ch := getc(KEYBOARD);
1426      if ch = KEY_RIGHT then
1427        if currTimeChange < 7 then
1428          incr(currTimeChange);
1429        end if;
1430      elsif ch = KEY_LEFT then
1431        if currTimeChange > 1 then
1432          decr(currTimeChange);
1433        end if;
1434      elsif ch = KEY_UP then
1435        case currTimeChange of
1436          when {1}: aTime -:= 1 . YEARS;
1437          when {2}: aTime -:= 1 . MONTHS;
1438          when {3}: aTime -:= 1 . DAYS;
1439          when {4}: aTime -:= 1 . HOURS;
1440          when {5}: aTime -:= 1 . MINUTES;
1441          when {6}: aTime -:= 1 . SECONDS;
1442          when {7}: if aTime.timeZone > -720 then
1443                      aTime.timeZone -:= 60;
1444                    end if;
1445        end case;
1446      elsif ch = KEY_DOWN then
1447        case currTimeChange of
1448          when {1}: aTime +:= 1 . YEARS;
1449          when {2}: aTime +:= 1 . MONTHS;
1450          when {3}: aTime +:= 1 . DAYS;
1451          when {4}: aTime +:= 1 . HOURS;
1452          when {5}: aTime +:= 1 . MINUTES;
1453          when {6}: aTime +:= 1 . SECONDS;
1454          when {7}: if aTime.timeZone < 840 then
1455                      aTime.timeZone +:= 60;
1456                    end if;
1457        end case;
1458      else
1459        old_command := command;
1460        command := ch;
1461      end if;
1462      case command of
1463        when {'M', 'm'}: showMenu;
1464        when {'I', 'i'}: plotInnerPlanets;
1465        when {'O', 'o'}: plotOuterPlanets;
1466        when {'D', 'd'}: changeDateTime;
1467                         command := 'M';
1468        when {'L', 'l'}: changeLongLat;
1469                         command := 'M';
1470        when {'S', 's'}: setup;
1471        when {'1', '2', '3', '4', '5', '6', '7', '8', '9'}:
1472          locatePlanet(ord(command) - ord('0'));
1473        when {KEY_MOUSE1}:
1474          if old_command in {'1', '2', '3', '4', '5', '6', '7', '8', '9'} then
1475            showName;
1476          end if;
1477          command := old_command;
1478        otherwise:
1479          command := old_command;
1480      end case;
1481      setPos(scr, 58, 1);
1482      write(scr, "(M)enu (I)nner (O)uter (D)ate (L)ong (Q)uit  (1-9) Planet");
1483    until ch = 'Q' or ch = 'q' or ch = KEY_CLOSE;
1484  end func;
1485