# Solar System (#227)

**URL:** <https://www.ruby-forum.com/t/solar-system-227/181699>\
**Category:** Ruby\
**Created:** [January 16, 2010, 6:08am UTC](https://www.ruby-forum.com/t/solar-system-227/181699 "2010-01-16T06:08:22Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [January 16, 2010, 6:08am UTC](https://www.ruby-forum.com/t/solar-system-227/181699/1 "2010-01-16T06:08:22Z")

</div>

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

The three rules of Ruby Q.:

1. Please do not post any solutions or spoiler discussion for this  
quiz until 48 hours have elapsed from the time this message was  
sent.

2. Support Ruby Q. by submitting ideas and responses  
as often as you can.

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem  
helps everyone on Ruby T. follow the discussion. Please reply to  
the original quiz message, if you can.

* * *

RSS Feed: [http://rubyquiz.strd6.com/quizzes.rss](http://rubyquiz.strd6.com/quizzes.rss)

Suggestions?: [http://rubyquiz.strd6.com/suggestions](http://rubyquiz.strd6.com/suggestions)

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

## Solar System (#227)

Kaltxì Rubyists,

This week’s quiz is to create a solar system simulator. It will a date  
as input then return a list of the distances to each planet from Earth  
at that date.

Have fun!

---

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [January 19, 2010, 7:35pm UTC](https://www.ruby-forum.com/t/solar-system-227/181699/2 "2010-01-19T19:35:27Z")

</div>

Daniel M. wrote:

> ## Solar System (#227)
> 
> This week’s quiz is to create a solar system simulator. It will a date  
> as input then return a list of the distances to each planet from Earth  
> at that date.

Not a whole lot of Ruby-specific stuff here, FWIW.

---

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [January 19, 2010, 9:14pm UTC](https://www.ruby-forum.com/t/solar-system-227/181699/3 "2010-01-19T21:14:57Z")

</div>

Lovely maths! I found the Horizons web site too - I was hoping to  
implement an application to extract the necessary data via net::telnet  
library but couldn’t see how to get the data I needed! - there’s an  
awful lot of it!

---

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [January 29, 2010, 9:46pm UTC](https://www.ruby-forum.com/t/solar-system-227/181699/4 "2010-01-29T21:46:32Z")

</div>

Daniel X Moore wrote:

> ## Solar System (#227)
> 
> Kaltxï¿½ Rubyists,
> 
> This week’s quiz is to create a solar system simulator. It will a date  
> as input then return a list of the distances to each planet from Earth  
> at that date.
> 
> Have fun!

Is anyone doing anything with this? I’m quite curious to see some  
solutions, and I’m definitely not clever enough yet to come up with  
something.

---

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [May 10, 2010, 10:30pm UTC](https://www.ruby-forum.com/t/solar-system-227/181699/5 "2010-05-10T22:30:59Z")

</div>

There were two solutions for this quiz, a mathematical computation  
approach, and a web services based approach.

Glen F. Pankow used a mathematical approach from the NASA Horizons  
site as described in Formulae for Computing Approximate Planetary  
Positions ([http://ssd.jpl.nasa.gov/txt/aprx\_pos\_planets.pdf](http://ssd.jpl.nasa.gov/txt/aprx_pos_planets.pdf)). Glen’s  
program makes use of a table of data containing the Keplerian elements  
([Orbital elements - Wikipedia](http://en.wikipedia.org/wiki/Orbital_elements#Keplerian_elements)) of  
each planetary body. Then when given the date in question as input,  
the numbers are crunched to produce the x, y, and z positions of each  
planetary body at that point in time. The final step is to compute the  
distance from the position of the Earth to each of those positions and  
display the results.

The mathematical formulas involved in computing the positions can be  
daunting, so a tip of the hat to Glen for writing them up with good  
comments in a step by step solution. Glen’s solution is certainly  
worth taking a look at if you are interested in the mathematics of  
planetary motion.

Jean L. provided a web-services based solution that queried the  
NASA Horizons system directly as well as a summary of the solution.

_The following section of the summary is written by Jean L…_

After asking a friend, who pointed me to the NASA horizons system site  
which provides solar system data, I realised that a simple solution  
would be to use the web version of the NASA’s site  
([http://ssd.jpl.nasa.gov/?horizons](http://ssd.jpl.nasa.gov/?horizons)) tool to retrieve the distances.

Understanding a site…

After playing a little bit with horizons I was able to use it to  
retrieve interactively the distance from earth to any planet of our  
solar system at a given date.

The user sets an amount of parameters first that define some query and  
then he/she asks for the result.

Horizons seems to store in a HTTP session the parameters that define  
what data we want to retrieve. It has several group of parameters like  
the result format, the target planet, the origin planet, etc. Each  
group may contain several parameters. Because we can query only one  
planet at a time, we must submit eight queries to retrieve all the  
distances.

To implement the solution we must find how our program can communicate  
with the server. Using a Firefox addon, HttpFox for instance, we can  
analyse the underlyingHTTP data. We record all the necessary postings,  
then we locate the values we need to change (the date and the  
planets). To retrieve the results we need, our program parse the  
response returned by the server. As Horizons can return simple text,  
parsing the result is easier.

The horizons web application expects post requests using form-data as  
multipart format. Because the standard HTTP layer in Ruby does not  
support multipart data transfer, we are going to add a simple support  
for it.

Multipart form-data

Before presenting how we can add multipart support, let us first see  
one way of using the HTTP library (using Ruby 1.8).

Posting data with Ruby’s HTTP library

Next snippet is a quick overview of a post request using the HTTP  
library.

01 require ‘net/http’  
02  
03 server = ‘mys\_server.com’  
04 port = ‘8080’  
05  
06 http = Net::HTTP.new(server, port)  
07  
08 path = ‘/submit’  
09 content = ‘name=me’  
10 headers = {'‘Content-Type’ =\> ‘application/x-www-form-urlencoded’}  
11  
12 response, body = http.post(path, content, headers)  
13  
At line 6 we create an HTTP object. At line 12 we send a POST request  
with one parameter.

We could write the same code using other APIs from the HTTP library,  
we wrote it this way as a base for what follows.

Extending the default library

We are not going to extend the classes used by the HTTP library, but  
specifically add the multipart support to the objects we create. We  
define a kind of factory method named create\_http\_with\_multipart.

01 require ‘net/http’  
02  
03 def create\_http\_with\_multipart server, port = nil  
04  
05 http = Net::HTTP.new(server, port)  
06  
07 def http.post\_multipart path, parameters, headers = {}  
08 end  
09  
10 http  
11  
12 end  
Line 10 returns a normal HTTP object with one more method named  
post\_multipartadded to the new HTTP object at line 7.

The post\_multipart method expects the server path for the request, a  
hash object with the parameters and a hash object with the optional  
header parameters. (It does not support files as parameters.)

01 def http.post\_multipart path, parameters, headers = {}  
02  
03 boundary = ‘xxboundaryxx’  
04  
05 data = []  
06  
07 parameters.each do |name, value|  
08 data \<\< “–#{boundary}”  
09 data \<\< “Content-Disposition: form-data; name="#{name}"”  
10 data \<\< ‘’  
11 data \<\< value.to\_s  
12 end  
13  
14 data \<\< “–#{boundary}”  
15 data \<\< ‘’  
16  
17 body = data.join(“\r\n”)  
18  
19 headers[‘Content-Type’] = “multipart/form-data;  
boundary=#{boundary}”  
20 headers[‘Content-Length’] = body.length.to\_s  
21  
22 return post(path, body, headers)  
23  
24 end  
The implementation loops through all the parameters (line 7) filling  
an array (data) and adding each time a new part (starting with the  
boundary sequence – line 8). It adds a last boundary after the loop,  
before converting the array to a string (lines ended with carriage  
return / line feed – line 17).

Then it adds the necessary headers, notice the one setting the content  
type with the boundary string (line 19).

And finally it calls the inherited post method (line 22).

Back to distance…

We hide the complexity of the horizons site communication in the  
Horizons class. It initializes the parameters in the server’s session,  
stores the necessary cookie, makes the eight requests and parses the  
result. Parsing the result is very simple as the horizons encloses the  
data table between two lines containing $$SOE and $$EOE.

The main script uses the Horizons class and loops waiting for user to  
input dates.

Conclusion

The result is rather slow (mainly because we need to make eight  
requests).

We didn’t really solve the problem. But we didn’t reinvent the wheel,  
was it possible? 😉

Here is a run result, so that you can check the planet positions on  
the 2nd of May (distances may seem short but they are expressed in AU  
– astronomical unit).

$ ruby solar.rb  
Enter a date or nothing to quit  
Date (YYYY-MM-DD): 2010-05-02  
JUPITER: 5.60264666760831 AU  
VENUS: 1.45723642739716 AU  
SATURN: 8.75025556531919 AU  
MERCURY: 0.56158052050790 AU  
URANUS: 20.8265012641112 AU  
PLUTO: 31.2222615921462 AU  
MARS: 1.29568386653263 AU  
NEPTUNE: 30.2996308594089 AU

(1 AU = 149,597,870.691 km)  
Date (YYYY-MM-DD):  
$

Solar System (#227) - Solutions  
([http://rubyquiz.strd6.com/quizzes/227.tar.gz](http://rubyquiz.strd6.com/quizzes/227.tar.gz))
