Saturday, February 10, 2018

DIY Electric Focuser For Celestron 127 SLT Maksutov/Cassegrain

It's been really cloudy. For eight weeks. So I've been working on scope projects - at some point, one day, I'll be able to see the night sky again. :-)

One of the tradeoffs with the mid-price Celestron 127SLT is that it shakes rather badly when you touch it, and takes several seconds to settle down. I'm focusing using a Bahtinov mask and Sharpcap's Bahtinov aid, which gives a numeric indication of the accuracy of your focus. It can be a slow process to focus it since you tweak, wait, and tweak again.

I decided that a remotely controlled electric focuser would allow me to do a much finer focus than I could achieve by hand, since it would not require touching the scope.

This design doesn't require removal of the focuser knob - only the rubber sleeve that covers it. This sleeve slips off by hand.

If you do pursue a different design that requires removal of the focuser knob, be warned that you must not tip the telescope down toward the table with the focuser knob removed - the knob is all that prevents the main mirror from falling forward and smacking into the corrector lens, which requires a teardown and cleaning to fix. This design does not risk that situation.

It took some head scratching to figure out how to mount the motor to the optical tube assembly (OTA). It finally dawned on me that I could remove the dovetail mount, and install a bracket under it. Aluminum stock which is 1" wide fits nicely in the gap that the dovetail leaves between it and the OTA. I then made a simple motor mount out of 1.5" x 1.5" right angle aluminum stock.

I cut some slots in the motor mount so that the stepper motor could be moved back and forth a bit, to set tension on the belt. I don't have a mill, so just drilled some holes next to each other and then filed the slot to open it up.

The focuser knob is 15 mm, and the stepper motor shaft was 5 mm. I used the following parts:

Aluminum 50 tooth MXL pulley with 15 mm bore
Aluminum 30 tooth MXL pulley with 5 mm bore

Based on some belt length calculators I found online, I first tried an 8" MXL closed loop belt, which worked, but had the motor run out to the end of it's range of adjustment. I wanted the motor closer to the OTA, so I found a 7.6" MXL belt from McMaster-Carr, part number 7887k78.

The 15 mm bore pulley was a perfect fit. The 5 mm bore pulley needed to be bored out to 5.2 mm to fit the shaft.

Here's a test run, with the original 8" belt.

The stepper motor can be controlled any number of ways, depending on what you want to do. There are a number of ASCOM compliant stepper controllers driven by microprocessors. My goal was to make my scope remotely controllable across a network, so I used a Raspberry Pi Zero W and an EasyDriver , along with a 5V regulator.

For a simple web browser interface, I used information from this excellent Flask tutorial to make a very simple web app to allow coarse, medium, and fine motor control. Source code will be in a separate article once it is a bit more polished.

Everything was buttoned up in a box with strain reliefs - it runs off the same 12V jump start battery I use to power the scope. Initial tests have been very positive - I can't wait for some clear skies to test it! It focuses very smoothly with no shaking - you can see clearly through the scope during the entire focusing process. Additionally, with the stepper motor, you can move the focuser in much smaller increments than by hand.

Friday, February 9, 2018

Switching Between Client Mode and Access Point Mode on Raspberry Pi

I'm working on a project to network my telescope. I have a Raspberry Pi Zero that is controlling a power focuser, and will eventually control scope motion by sending serial commands to the hand controller.

If I'm at home, I'd like to connect to my home network. If I'm away from home, I want it to act as an access point (AP) so that I can control the scope. This would also be really useful on a network controlled robot.

This was tested on a Pi Zero W, and should work on a Pi 3 as well. It assumes the Pi is currently configured to automatically connect to your home network. Pi's that use different USB wireless NICS will need a modification to the hostapd configuration file to reflect the driver name in use.

I'm using hostapd and the ISC DHCPD server, so first:

sudo apt-get install hostapd isc-dhcp-server

My /etc/hostapd/hostapd.conf file looks like this:

# WifI interface and driver to be used

# WiFi settings

# Use WPA authentication and a pre-shared key

# Network Name
# Network password

I appended the following network definition to /etc/dhcp/dhcpd.conf to define the range of addresses that it will hand out to clients when acting as an AP:

subnet netmask {
 option broadcast-address;
 option routers;
 default-lease-time 600;
 max-lease-time 7200;
 option domain-name "local";
 option domain-name-servers,;

The following script, when executed, will disconnect from any AP that the Pi is associated with and fire up the access point, setting a static IP for the Pi in the process:

sudo killall wpa_supplicant
sudo ifconfig wlan0 down
sudo ifconfig wlan0 netmask up
sudo service isc-dhcp-server start
sudo hostapd /etc/hostapd/hostapd.conf &

Then we just need to determine if we are connected or not when the Pi boots up.

First, I added an entry in /etc/rc.local to call this script, which checks to see if I'm connected to my home AP after a brief delay:

sleep 10
OUTPUT=$(iwconfig wlan0 | grep -c MyHomeSSID)

if [ "$OUTPUT" = "0" ]
        echo "Not associated to home AP, starting local AP"
        logger "Home AP not found, starting local AP"
        sudo /home/pi/startAP
        echo "Associated to home AP, no action taken"
        logger "Home AP found, not starting local AP"

Monday, November 13, 2017

From One Gun Guy To Another: A Proposal To Reduce Victims In Mass Shootings

Like all of my blog posts, this article gives my personal views and does not represent the views of my employers, past or present. 

Like a lot of folks in America, I grew up around guns. My grandfather and dad taught me to shoot in the clay pits of south Alabama, with a strong focus on safety and responsibility. As I got older, I realized that it was not only a great deal of fun, but a method of effectively protecting myself and my family against folks who might try to do us harm. I take that right and responsibility quite seriously. I practice regularly and have done a lot of reading to educate myself on the implications of using a weapon in self defense. I had a concealed carry permit and carried regularly for years, both at home and when backpacking. I'm a gun guy. I enjoy using them, and I stay ready in case I ever have to use one in defense.

Like a lot of gun guys over the last few years, I've been watching the growing number of mass shootings and struggling with it. Yes, I believe guns in the hands of good guys are a good thing, but how do we balance that against folks who are mentally ill killing or injuring dozens at a time? A lot of gun folks insist it's a people problem, not a gun problem. I argue that it's both. Clearly, mentally ill people must not have access to firearms, to the greatest extent possible. But we all know how hard that is. The background check system fails sometimes, or an ill person simply has someone buy the weapon for them. Sometimes they take a family member's that is not well secured.

So we have a certain number of mentally ill folks that are going to have access to guns. How can we limit the amount of damage they can do without dramatically reducing the freedoms of millions of lawful gun owners? 

We should treat high capacity magazines (>10 rounds) the same way we treat sound suppressors ("silencers"). Existing high capacity magazines should be bought back at fair market value, or the owner acquire the tax stamp and background check. Like suppressors, the penalty for unlawful possession must have serious teeth.

To buy a suppressor, you are subjected to an extensive background check, more so than buying a firearm. For each suppressor you buy, you have to buy a tax stamp, at a cost that is affordable if someone really wants or needs it, but high enough to make it painful to buy a lot of them.  The current cost for the stamp, per suppressor, is $200.

Here are the reasons why this is an effective strategy.

1) It will dramatically reduce the number available to an attacker. Only people who really need them will keep them, and those folks will pass a more stringent background check. They WILL be available to those who need them, though, in modest number.

2) Your kids are probably receiving some form of armed intruder response training in school. I've gotten two variants of it in the workplace. They teach you to run, hide, and as a last option, to wait until the attacker is reloading, and fight. You are trained to throw things, from books to fire extinguishers, and to gang up on the attacker to immobilize him.

If an attacker has a collection of 30 round magazines, pauses to reload don't happen very often. If the magazine capacity is limited to ten rounds, the victims get two additional windows of opportunity per magazine in which to flee or fight. 

3) If someone is planning an attack and tries to accumulate a lot of them, it will be noticed.

4) It effectively limits the number of rounds an attacker can easily carry which are ready to fire.

The existing magazines that were being registered would require serialization. A system would need to be created to allow a worn magazine to be exchanged for a new one without requiring another stamp, and the worn one destroyed.

Undoubtedly, you are raising concerns. They are probably the reactions that I tend to have to any proposed gun control too. 

"The bad guys won't turn theirs in, so it won't help." 

A lot of guns used in mass shootings are obtained legally by the shooter or through family members. Making high capacity magazines harder to obtain will make it more likely that a shooter won't have a pile of those magazines on them when they commit their crime. 

"I can change a magazine really quickly. It won't make a difference."

For some people who are extensively trained to use guns under pressure- military, for example - that's probably reasonably true. For those of us who spend our days in offices, though, changing a magazine at the range doesn't translate to doing it while people are pelting you with books or whacking you over the head with a fire extinguisher. That's hard. That requires more focus than the vast majority of civilians have. 

Besides - if there is no advantage to them, why do you want one? You know that argument doesn't hold water. If they didn't have a significant advantage in an offensive situation, you wouldn't see them issued to infantry around the world.

"If we give an inch, they will take a mile. They want to ban all guns!"

If we refuse to acknowledge that the gun is a force multiplier, and do not genuinely try to find a solution that reduces harm to society, we are far more likely to lose our gun rights. Casually replying that guns don't kill people in response to a tragedy where one mentally ill person has killed dozens of innocents is callous, and counterproductive. 

Decades ago, we as a nation decided that machine guns, suppressors, high explosives, and heavy weapons should be heavily controlled because they place unreasonable power to do harm in the hands of a single person. Our gun rights have not been eroded as a result. This should be extended to high capacity magazines to limit the harm a single ill person can do.

"The second amendment isn't about hunting. We must be ready to repel a foreign invader or a hostile government."

You can do that with a good scoped bolt action hunting rifle. You can do that with 10 round magazines. If you feel really, really strongly about it, obey the law, pass the background check, and buy yourself some high capacity magazines. Then lock them up.

"I need high capacity magazines to defend myself or my family."

High capacity magazines in a pistol are certainly potentially useful in an extended altercation on the street. If you really feel the need, it would still be available. It just would be a bit harder to do. It should be noted, though, that a review of the NRA's Armed Citizen covering 5 years of incidents involving the use of firearms by civilians for self defense indicated the following:

"As might be expected, the majority of incidents (52%) took place in the home. Next most common locale (32%) was in a business. Incidents took place in public places in 9% of reports and 7% occurred in or around vehicles.

The most common initial crimes were armed robbery (32%), home invasion (30%), and burglary (18%). Overall, shots were fired by the defender in 72% of incidents. The average and median number of shots fired was 2. When more than 2 shots were fired, it generally appeared that the defender’s initial response was to fire until empty." (emphasis mine)

Now, if you are talking about your AR, consider that carefully. If you live in a typical city or suburban area and are firing 30 rounds of .223 at your attacker, those rounds will go through walls and stand a high likelihood of injuring someone other than your attacker. 

"High capacity magazines are fun."

They sure are fun. But that is an absolutely inadequate reason to keep them so readily available when they make it easier for one mentally ill person to kill a couple dozen people. 

"The solution is to have more armed law abiding citizens."

That is an entirely separate potential solution that deserves careful study. It doesn't invalidate any of the arguments presented here.

Making high capacity magazines harder for an attacker to get will give victims under attack more time to flee, hide, or fight. 

We, as pro-gun people, need to acknowledge that the weapons we keep DO play a part in these tragedies. T-shirt slogans aren't good enough. We need to propose effective approaches to reducing the death toll from these terrible incidents.

Friday, November 3, 2017

Automated meteor/aircraft/satellite detection for sky camera in Python


In a previous post,  I described the construction of a simple networked sky camera built with a Raspberry Pi. I was pleased with how well it worked, but quickly figured out that manually reviewing the more than 5000 frames it generated per night was a drag. 

The following describes a Python script that makes uses calls to ImageMagick on a Linux computer to identify frames that potentially contain something interesting. In a typical night, it reduces the frames for manual review from 5000+ to a dozen or so, and it picks up lighter streaks than I tend to see rapidly skimming through by hand. There are certainly more sophisticated ways to do that, but I was surprised at how well this works. Specific drawbacks are mentioned below.

Things you are likely to catch with a sky camera

My sky camera exposes for 10 seconds per frame. As a result, a bright moving object will leave a line. You can interpret the line to determine what it is.

 1) Airplanes. Lots of airplanes. You'll be surprised at how many airplanes fly over your house or observatory.

Airplanes that fly at night are required to have strobe lights on the wing tips and vertical stabilizer. As a result, they produce a distictive dot-dash-dot-dash pattern as the fly across the sky during a long exposure, which is easy for a person to identify.

Also, they cross from one side of the frame to the other, and take several frames to do it - typically three to four. They don't appear in the middle of the sky and then disappear after short distances.


2) Satellites. Fewer than airplanes, and they are fairly easily distinguished. First, they don't have strobes, so they appear as a fairly consistent brightness that travels in a straight line. They also usually take 3-4 frames to cross from one side to the other. If it's a straight, non-strobing line that lasts more than two frames, it's not a meteor, since it is travelling too slowly across the sky.

A satellite - 1 frame of 3

3) Meteors. If it appears in no more than 2 frames (since it's possible for it to occur at the end of one frame and the start of another) and it doesn't cross the whole frame, congratulations - you've probably captured a rock from space! An airplane or satellite is not expected to start in the middle of the sky, but a meteor can.

Orionid meteor - Oct 2017. Stack of two sequential images that the meteor was on, cropped

Finding frames with possible objects in them

If you are running your sky camera on a clear night, detection of things moving can be as easy as identifying frames that are significantly different from the frame before it. Since the Earth is rotating, we expect it to change a little, so a "fuzz" factor is applied - a small amount of change is ignored. If that threshold is exceeded, though, we should set the frame aside for a person to look at. This simple operation sifts through thousands of frames and pulls out the ones that have something happening remarkably well. 

Of course, if you have trees or clouds in your frame, it's likely that motion of those will trigger it too - a false positive. Clouds can be identified reasonably well by simply dropping frames that exceed a certain brightness threshold, but that increases computing time and you might miss a frame that has something interesting in it that also has a cloud. It's a tradeoff. At the moment, I only capture images on pretty clear nights, so I leave the brightness thresholding turned off to speed processing. If you want the function enabled, you can just uncomment the line in main() that calls it.

Running the program

The program currently runs on a Linux computer with Python and ImageMagick installed. Most distros have these already. An example command looks like this. The first argument is the directory containing the images you captured, and the second is your desired output directory. Frames that the program determines may be interesting are copied to the output folder.

./ 20-10-2017 20-10-2017-output/

You must assign the program file execute privileges on your Linux box - usually, this will do the trick:

chmod u+x

Program listing and download

Listing follows... please let me know if you find it useful. 

#!/usr/bin/env python

import sys
import os
import subprocess
import shutil

def removeBrightImages(images, threshold):
 #The average graylevel of an image may be found using the string format "%[mean]"
 #convert image -format "%[mean]" info:
 count = 0
 darkFrames = []
 for image in images:
  if count > 0:

   command = 'convert ' + image + ' -format "%[mean]" info:'
    val = subprocess.check_output(command,shell=True) 

   except subprocess.CalledProcessError as e:

    val = e.output

   if float(val) < threshold:
    print "Adding " + image + ": brightness under threshold, queued for comparison"
    print "Rejecting " + image + ": brightness exceeds threshold"
  count = count + 1
 print darkFrames
 return darkFrames

def findChanges(images, fuzz, threshold):
 #fuzz is currently hardcoded below
 count = 0
 changedFrames = []
 commandList = []
 for image in images:
  if count > 0:
   #Trying to do this the better way with arguments and no shell=True results in the conversion
   #of the output to an int failing below, and I have not figured out why.

   #print "Comparing " + lastImage + " to " + image

   command = "compare -metric ae -fuzz 15% " +  lastImage + " " + image + " null: 2>&1"
    val = subprocess.check_output(command,shell=True) 

   #The compare program returns 2 on error otherwise 0 if the images are similar or 1 if they are dissimilar.
   except subprocess.CalledProcessError as e:

    val = e.output

        if e.returncode == 2:
     print "Error in image comparison routine."

   if int(val) > threshold:
    print image + ": " + val + ", item found"
    print "Checking: " + image 

  lastImage = image
  count = count + 1

 return changedFrames

def saveImages(images):
 for image in images:
  shutil.copy2(image, sys.argv[2]) 

def genImageList():
 imageList = []
 for file in os.listdir(sys.argv[1]):
      if file.endswith(".jpg"):
          imageList.append(os.path.join(sys.argv[1], file))
 return imageList

def checkArgs():
 if len(sys.argv) != 3:
  print "Usage: ./  "

 if os.path.isdir(sys.argv[2]):
  print "Output directory exists already, refusing to overwrite it. Exiting."
  print "Creating output directory"

def main():

 #if brightness thresholding is enabled, set threshold. Higher is brighter.
 brightnessThreshold = 2000

 #frame difference comparison senstivity. A higher value reduces sensitivity. 0-100.
 fuzzFactor = 15

 #the number of pixels that must be different between frames in order to flag it as interesting.
 diffThreshold = 100

 images = genImageList()
 #if you want to enable brightness thresholding, uncomment the line below
 #images = removeBrightImages(images, float(brightnessThreshold))
 changedFrames = findChanges(images, fuzzFactor, diffThreshold)

if __name__ == '__main__':

Thursday, October 12, 2017

Raspberry Pi Skycam w/ NoIR V2 Camera and Light Pollution Filter

NASA seems to assign any scientific instrument a contrived acronym. This camera detects rocks from space. Therefore, may I present WHAMMO - the Wadsworth Hillbilly Automated Meteor/Meterology Observatory.

Update: I have a working detection script running on the Linux box storing the images - full article is here.


I am interested in building a sky camera for capturing meteors, and also for checking sky conditions. I had a Raspberry Pi and camera. Initial tests were done with a normal V1 camera, which is limited to a 6 second exposure. I switched to a No-IR V2 camera, which allows 10 second exposures and doesn't have an infrared filter. The V2 camera produced much better results.

I added a simple cell phone medium-wide angle lens and found through experimentation that a light pollution filter normally used for observing improved the contrast considerably.

The camera is weather resistant and mounts a shared directory on a network computer, rather than writing to the Pi's flash card. This is to improve reliability, since the Pi's flash would wear fairly rapidly due to the high rate of writes during capture.

This post documents the development of this system and outlines next steps.

Example Video

The camera captures 10 second frames all night long. The frames can easily be stitched together with ffmpeg into a video. The command line I used was:

ffmpeg -thread_queue_size 512 -r 30 -f image2 -i sky_%04d.jpg -vcodec libx264 -preset slow -crf 22 -profile:v baseline -pix_fmt yuv420p test.mp4

The video is best viewed in full resolution rather than a small window - the stars are single pixels, in many cases. The MP4 is available for download here or you can follow the link below to Vimeo and full-screen it there.

Raspberry Pi Skycam w/ NOIR v2 Camera and Light Pollution Filter from Jason Bowling on Vimeo.

The camera was in my back yard in Wadsworth, a bright orange band on the dark sky finder map.

Still Frame + Star Chart

A full resolution copy of this image is here

The image is the result of stacking a set of 25 images in Deep Sky Stacker with the default settings. A single raw frame is available for comparison here.

You can then take that image and do a simple level adjustment to improve contrast - at that point there are far more stars in the image that I can see by eye alone. The Pleides are easily resolved into multiple stars in the image, and it's only visible at my house with some effort.

Full resolution version of the stacked and level-adjusted image is here.

V1 vs V2 camera

I tried the V1 camera first, since I had one, but wasn't very happy with the results. I found the results with the V2 NoIR camera to be much better in terms of sensitivity and noise. The V1 did work reasonably well with the saturation increased. Example video from the V1 camera is here.

Wide Lens

I used a cell phone lens kit similar to this one - the exact kit is no longer available on Amazon. I used the medium-wide lens - the widest resulted in significant distortion and I have too many trees in the back yard to make much use of a wider view anyway. I just tacked the lens to the 3d printed camera case with hot glue, since it's easily removable. I considered cyanoacrylate glue but didn't want to fog the lens.

Capture method

The command below captures the images in timelapse mode, at a resolution selected because it uses 2x2 binning to effectively increase pixel size. This improves low light sensitivity and signal to noise ratio.

#1640x1232 is 2x2 binning mode with v2 cam

mkdir /home/pi/storage/"$(date +"%d-%m-%Y")"

#note: this command is all one line
raspistill -o /home/pi/storage/"$(date +"%d-%m-%Y")"/sky_%04d.jpg -t 500000000 -tl 0 -ss 9900000 -ISO 800 -bm --nopreview -w 1640 -h 1232 &

Light pollution filter 

I found that the addition of a light pollution filter significantly improved contrast for images taken from my home. The filter works reasonably well for visual observation, but the improvement is much more noticeable with a photograph. A comparison is given below. This image is best viewed at full resolution. 

The mount was just a disk of foam board friction fit to the wide angle lens body. The filter is again secured to the mount with hot glue.

The filter does cause a bit of vignetting. I think it's worth it for my purposes.

Mounting A Filesystem On Another Computer

To avoid wear and tear on the flash, and since the Pi wouldn't be up to the image processing tasks I had in mind, I chose to use SSHFS to mount a directory on a Linux server in the house over WiFi.

The command below, all one line, has worked well for me. Without the included options, I occasionally had the mount point drop out between writes sometimes - it has been very reliable since the keep-alive options were included.

sshfs -o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3 jbowling@ storage

If you prefer, you can also mount a share on a Windows computer.

Black Out Those LEDS...

I turned off the board LEDS in software, and blacked out the LED on my WiFi adapter with primer to avoid leaking light up into the dome.

Mechanical Construction

I chose a 6x6x4 junction box from Home Depot (marked Item 10030 on the display case). It doesn't have an IP rating I could find, but has a nice gasket and I've found it held up well under the garden hose.

I chose this acrylic dome and this gasket material.

I drilled holes to mount the Pi on standoffs, and also a radial pattern of larger holes in order to dump heat from the box up into the dome, to reduce dew. 5W isn't a lot of heat, but it might as well get used. A slot is also cut for the camera cable.

I traced the dome with an ink pen and cut it out with scissors, with a final trim by a razor hobby knife.

A shingle nail worked well to punch holes to pass the screws through the gasket. This passed several tests with the garden hose. I am going to use cable glands to waterproof the power line when done.  This design has not been tested long term in heavy weather. 


So far, the camera has just been powered from a 12V jump start battery's USB output. I considered several options for when the camera is mounted permanently.

I considered running an AC power cord into the box and using a standard USB power block to power it, but then I have 110AC in my junction box, and that's not something I want. I decided the way to go was to run 12V over a an extension cord with the ends removed - that way it's nice, heavy double insulated wire being fed by an approved 12V power supply kept nice and dry in the garage. That also gives me spare capacity to install some resistors to reduce ice and dew on the dome, if I choose to. I can install an appropriate low amperate fuse in the line too, in case some water does make it's way in.

If you happen to have a Power Over Ethernet (POE) switch, there are converters that can power the Pi from POE. In my case, that wasn't economical. 

Next Steps

1) The next interesting part will be software to sift through the many thousands of frames and find the meteors and airplanes. That will be a neat challenge.

2) Dew heater. I'll probably add a small network of power resistors under the dome to improve resistance to dew and to melt light ice and snow. The existing setup usually doesn't dew up, but I've had it happen once.

Saturday, September 9, 2017

Sunspots AR 2679, AR 2675, AR 2673 on 09-Sept-2017

Sunspots AR 2679, AR 2675, AR 2673 on 09-Sept-2017 w/ 127SLT and ASI290MC at prime focus.

Given the unusual sunspot activity over the last few days, I made a Scheiner mask to aid in focusing, since you can't use a Bahtinov mask without a star to focus on. The laptop was set up in the back of our SUV to shield it from glare and I used my DIY solar filter mount w/ Thousand Oaks film on the 127SLT. Best 10% of frames from 1 minute of video.

Tuesday, August 29, 2017

4k wallpaper - Partial Moon

Want some high resolution moon wallpaper? This image is free for noncommercial use.

This picture is a composite from video, taken with a 127SLT and ZWO ASI290MC taken at prime focus. The video was composited with Microsoft's Image Composite Editor (ICE), edited in Gimp to place it on a black background of the right proportions, sharpened with Registax Wavelets, and level adjusted in Gimp.

Download the full resolution image from my Dropbox. In the upper right corner, you'll find a button with two dots. Click it and a dropdown box will open - an option there will let you download the full resolution file.

If you want a similar shot with a fuller moon, I have one here. I personally prefer this one, since it shows features in 3D a bit better due to the more oblique lighting.