Seeing Stars – The Automated Way, Part 2

ASCOM PlatformA while back I introduced the ASCOM platform and explained how it is used to automate astronomy observing. I wanted to spread the word about ASCOM for two reasons. First of all, it’s a great open source project with lots of opportunities for newcomers to participate. Second, automated observing is a great way to see your work come to life and interact with hardware. As an embedded developer, the thing I enjoy the most about my job is getting to interact with different types of hardware in unusual ways. I love throwing together a circuit packed with sensors, switches, motors, and other gizmos, and coming up with creative ways to make my code interact with the “real world”. Pulling off automated observing is not a simple feat and it requires a great deal of ingenuity. ASCOM makes the process much simpler by providing a set of standard device interfaces.

Most of the ASCOM drivers out there are written in C#, VB or C++.  However, it is possible to write software for ASCOM using any language that supports COM objects. In Part 1 of this article, I mentioned that there are no documented cases of anyone using Ruby with the ASCOM platform. I also promised that I would dish out some example code. So, let’s have some fun with Ruby… (Note: I would probably never write a full-fledged ASCOM client app in Ruby but if all you need is a simple automation script, and your a Ruby guru, the option is there for you?)

What Pieces Are Needed?

To recap, there are three basic components that you need to automate a device using ASCOM. First, you need a physical device. This can be a telescope, a filter wheel, a camera, etc, and any combination thereof. Second, you need a driver. The driver is a custom piece of software, generally provided by the device manufacturer (although sometimes enthusiastic hobbyists write their own), that interacts directly with the hardware and implements one of the standard ASCOM device interfaces. The driver usually communicates with the hardware using a Serial Port, USB or Ethernet connection. The final piece that is needed is the client application. The client app is the part that pulls all the other pieces together to perform a desired task. Let’s make an example ASCOM client program using Ruby!

The Objective

For this example, I have defined the following goal for us to try to achieve:

Capture 30 sequential images of the night sky at Right Ascension of 15.26 Hours and Declination 75.4 degrees. Each exposure should be 10 seconds in duration. The first 10 images should use the red filter in the filter wheel, the second set of 10 images should use the green filter, and the last ten images should be taken using the blue filter. Lenses do not focus every wavelengh of light at the same focal point (see Chromatic Aberration) so our focuser must adjust it’s position between captures. Let’s move the focuser to position 22000 steps for the first filter, 21000 for the second and 21500 for the third.

I don’t have any real devices to play with right now, and you wouldn’t be able to see them if i did. Fortunately, the ASCOM platform includes simulators for every device type. These provide a handy way for software developers to write client applications without having to purchase any real hardware. My example is hardcoded to use these simulators. In a real project, you would typically provide the user with a way of selecting their actual hardware driver. The source code for all of the simulators, and many other actual driver examples, can be downloaded from the ASCOM repository on SourceForge, along with the entire of the platform code.

Here’s the example. It uses Ruby’s win32ole gem to interact with several ASCOM devices through COM.

Example Code

require 'win32ole' # this allows us to create instances of Windows COM objects

# create instances of each of our drivers
$filt ="ASCOM.Simulator.FilterWheel")
$foc ="ASCOM.Simulator.Focuser")
$tele ="ASCOM.Simulator.Telescope")
$cam ="ASCOM.Simulator.Camera")

# a function that disconnects from each connected device
def DisconnectAllDevices()
	puts "Disconnecting from all devices..."
	# disconnect from each device
	$filt.Connected = false
	$foc.Connected = false
	$tele.Connected = false
	$cam.Connected = false
	puts "All devices disconnected"

# capture a number of images with a specific exposure time
def CaptureImages(numImages, expTime)
	for i in 1..numImages
		puts "Capturing Image " << i.to_s << "..."
		#$cam.ExposureMin = expTime
		$cam.StartExposure(expTime, true)
		while(!$cam.ImageReady) do end
		# do something with $cam.ImageArray here, probably save it to disk
		puts "Captured Image Successfully!"

# connect up to each device
$filt.Connected = true
if($filt.Connected) then puts "Connected to Filter Wheel!" end
$foc.Connected = true
if ($foc.Connected) then puts "Connected to Focuser!" end
$tele.Connected = true
if($tele.Connected) then puts "Connected to Telescope!" end
$cam.Connected = true
if($cam.Connected) then puts "Connected to Camera!" end

# unpark the telescope so that we can move it to our target position
puts "Unparking telescope..."
while($tele.Slewing) do end
puts "Telescope unparked!"

# slew scope to our target location
puts "Slewing Telescope to target location... RA = " << $tele.RightAscension.to_s << ", DEC = " << $tele.Declination.to_s
# tracking is a feature of the telescope mount that continuously adjusts the position to compensate for the movement of the stars
$tele.Tracking = true
# move the telescope to point to the target position
$tele.SlewToCoordinates(15.26, 75.4)
puts  "Slew Complete! RA = " << $tele.RightAscension.to_s << ", DEC = " << $tele.Declination.to_s
# move filter wheel to postion 1
$filt.Position = 1
while($foc.IsMoving) do end
puts "Focuser Position = " << $foc.Position.to_s
puts "Filter Wheel Positon = " << $filt.Position.to_s
# capture a set of images
CaptureImages(10, 2)
# move filter wheel to postion 2
$filt.Position = 2
while($foc.IsMoving) do end
puts "Focuser Position = " << $foc.Position.to_s
puts "Filter Wheel Positon = " << $filt.Position.to_s
# capture a set of images
CaptureImages(10, 1)
# move filter wheel to postion 3
$filt.Position = 3
while($foc.IsMoving) do end
puts "Focuser Position = " << $foc.Position.to_s
puts "Filter Wheel Positon = " << $filt.Position.to_s
# capture a set of images
CaptureImages(10, 3)

# park the telescope
puts "Parking telescope..."
while($tele.Slewing) do end
puts "Telescope parked!"

# disconnect

puts "Process Completed Successfully!"

To run the script simply save this file to your computer and enter the following command into the command prompt: ruby AscomRubyExample.rb

Here's the script output:

Connected to Filter Wheel!
Connected to Focuser!
Connected to Telescope!
Connected to Camera!
Unparking telescope…
Telescope unparked!
Slewing Telescope to target location… RA = 10.6763720360123, DEC = -3.18055468146352e-015
Slew Complete! RA = 15.26, DEC = 75.4
Focuser Position = 22000
Filter Wheel Positon = 1
Capturing Image 1…
Captured Image Successfully!
Capturing Image 10…
Captured Image Successfully!
Focuser Position = 21000
Filter Wheel Positon = 2
Capturing Image 1…
Capturing Image 10…
Captured Image Successfully!
Focuser Position = 21500
Filter Wheel Positon = -1
Capturing Image 1…
Captured Image Successfully!
Capturing Image 10…
Captured Image Successfully!
Parking telescope…
Telescope parked!
Disconnecting from all devices…
All devices disconnected
Process Completed Successfully!

Does this Mean ASCOM Is Cross-platform?

No, unfortunately, ASCOM is not cross-platform. ASCOM relies heavily on the Windows registry and COM. The choice to use COM was made so that developers could code in just about any language they choose. This choice also heavily couples the platform to Microsoft-land. If you want an alternative to ASCOM that is making an attempt to be truly cross-platform check out TheSkyX. TheSky is cross-platform but I believe it requires you to write drivers using C++ and it not nearly as widely used as ASCOM at this point.