Weekend Update…

This is just a short set of updates for my weekend projects, meant to keep me in the habit. I’ll write up a more complete version of these projects going forward.

First of all, a new acquisition. My Anet A8 3D printer has proven to be, well, not the most reliable of gadgets. I still haven’t gotten around to fixing its heated bed, but should get to it shortly. But as it happens, a small windfall came my way, so I decided to get a Creality CR-10 which I caught on a good price for less than $400. Unlike the laser-cut, hours-to-assemble Anet A8, the Creality is mostly assembled. It has a 300mmx300mm bed, a much taller range of travel, and is constructed by aluminum extrusions that make for a nice, stiff, and easy to assemble printer. I mostly assembled it in a little less than an hour, but ran out of steam and decided to go to a movie with Carmen and then watch the Superbowl. I’m hoping to print a Benchy when I get home tonight: the only thing I have left to do is get the bed leveled.

I also spent some more time on the ISS clock. I added a new display mode that shows details of upcoming passes, including a diagram of the pass, showing its path across the sky and its maximum elevation. I also updated the epoch that the Plan 13 code was using so that it would be more accurate, and now it compares to within about a degree or so with what other, more sophisticated models have. There are still a couple of lingering glitches. Occasionally it looks for the next pass before the current pass is complete. I suspect that is because I used a number of global variables to communicate between processes, and something in the logic isn’t quite right. But as they say, any program worth writing is worth rewriting. I’ll try to get a version of the code up on github tonight, even though it’s kind of embarrassing.

Stay tuned for CR-10 print experiences and more on the ISS clock.

More progress on the ISS clock project…

I haven’t posted an update here recently, but I am (mostly) living up to my New Year’s resolution to spend at least 30 minutes a day working on a project. This has taken the form of some stupid but necessary chores (like fixing the broken pull cord on my lawnmower) but has mostly taken the form of additional incremental additions and improvements to my ISS clock project. This has mostly taken the form of noting a small deficiency, and then working until that deficiency is ameliorated.

The ESP32/Arduino environment still has access to the FreeRTOS operating system below to create tasks, semaphores and queues. I decided that I would implement the program as a set of tasks of different priorities. One task’s job is to compute the current latitude, longitude, altitude and azimuth for the satellite. Another lower priority task scans forward in time, and finds the next time when the satellite rises and sets. When it finds those, it pauses until the current time is beyond the found set time, and then continues searching for the next rise/set pair. The final task runs the display, showing all the current information. If the time of the next satellite rise time (or AOS — Acquisition of Signal) is set, then the display process will give a countdown in hours/minutes/seconds.

This was all working reasonably well, except that I noticed that the time display would frequently “jump” a second, skipping from 42 seconds to 40 seconds in countdown, and sometimes even in the clock display jumping from 10 to 12 seconds for example. I didn’t think any critical path in the code would account for anywhere near that amount of delay in switching between processes, but tried reorganizing the code in various ways to be sure.

In the end, I realized that it had nothing to do with multitasking or the like. I had written code in two different places to convert an internal representation of time used by my Plan 13 satellite library into H:M:S format, and had committed the same error in both locations.

The thing that I realized is that unlike normal Unix time calls, internally my code represents the current time of day as a double which represents fraction of a day. To step the time forward by one hour, I increment that value by 1/24. I have a function whose purpose is to convert this internal representation separate hours, minutes, and seconds.

Here is a (slightly edited) version of my original code.

DateTime::gettime(int &h, int &m, double &s)
    double t = TN ;  // TN is the time in fractions of a day
    t *= 24. ;
    h = (int) t ;
    t -= h ;
    t *= 60 ;
    m = (int) t ;
    t -= m ;
    t *= 60 ;
    s = t ;

The problem appears to be rounding interacting with the timing of the display process. Staring at this code, I’m not sure what I was thinking. I rewrote this code in the following way:

DateTime::gettime(int &h, int &m, double &s)
    double t = TN ;  // TN is the time in fractions of a day
    int ts = (int) round(t * 24. * 60. * 60.) ;
    s = ts % 60 ;
    ts /= 60 ;
    m = ts % 60 ;
    ts /= 60 ;
    h = ts % 24 ;

This code works much better. I had coded a similar routine in new code which had the same error. I must admit that I’m still a little fuzzy about how the timing of the display process interacts with this rounding issue to introduce the errors that I observed, but I suspect it has to do with the fact that I inserted delays in the display process to keep that process from updating somewhere around two times a second. I think that the prediction task (which runs for about twenty seconds usually) may be starving the low priority display task just often enough to cause the rounding error to manifest.

I’ll ponder it more later.

Things that are now percolating to the top of my todo list are to understand the event handling of the NtpClientLib library. Occasionally it will toss errors from the WiFiUdp.cpp file, and error handling is not appropriate (sometimes it doesn’t set time properly). I suspect I’ll have to dig into the networking code in the library a bit deeper.

Addendum: The display on this thing is one of those tiny 0.91″ OLED displays. It’s really at the limits of what I can comfortably read, and I’d like to have more space (and potentially color). I have some small 1.8″ LCD boards, but I stumbled across the M5Stack board which appears to be available on banggood.com. It looks like a cool little box.

I ordered it from this link on banggood for about $33. Besides having an ESP32 module, it includes a case, a 320×240 2″ color LCD panel, and three buttons. The larger display size and buttons are a great fit for my ISS project. When it arrives, I’ll quickly work on a port of this code to that.

If that doesn’t work, I’ll probably work on adapting it to some of the ST7735 modules I have lying around that I ordered from icstation.com.

Stay tuned for more progress, and eventually links to the git repository for the code when I am not embarrassed by the code.

Brief Update on my ESP32 ISS Clock

Several days ago, I cobbled together a short bit of code to make an NTP enabled clock out of an ESP32/OLED module. I had previously used an ESP8266 and a separate module to make a little demo that predicted the location of the ISS. I thought that the ESP32 would make a better development platform, for a number of reasons:

  • It’s faster.
  • It is dual core.
  • It includes a small real time operating system that allows you to create communicating tasks.

My early example didn’t make any use of that, it was just a clock. So today I spent a little more than an hour to expand the basic code outline to include some new features:

  • It now links in my C++ library that implements the Plan 13 algorithm for doing satellite prediction.
  • The previous NTPClient library wasn’t really very good: it didn’t include any way of accessing day/month/year. I switched it to use NTPClientLibinstead, which is much better.
  • I experimented with using the xTaskCreate call to create different tasks, one of who updates the current satellite position, and the other which updates the display. This proved to not be difficult and works remarkably well. I anticipate that this will make my final version of the code easier to write. I can implement one process whose job it is to scan forward in time, looking for the next satellite pass. When it finds one, it can use a queue to send it to the display task, and can continue. This makes the program easier to write and more modular.
  • I also did some work to fetch orbital elements directly from celestrak.com so that the display will always be up to date.

The code is not in a state to share yet but I think it is going to be pretty neat when I’m done. The gadget could easily be mounted in a small case and carried into the field, where it could be entirely battery powered.

Stay tuned for updates.

pinMode v. digitalWrite: what’s with this use (abuse?) of internal pullups?

This post is the kind of round about post you get first thing in the morning.   I’ll get to the title question at the end.

This morning, I was interested in doing a tiny bit of tinkering. I had found one of these 4 digit 7 segment LED displays while digging around for something else. You can get these modules for about $2.50 from banggood, and potentially for as little as a buck on eBay.

A little over a week ago, I was tinkering with a WeMOS D1 Mini board that I had acquired, and decided to port my Plan-13 library to it. Long (long long) time readers may remember some years ago that I wrote this code to predict the position of satellites, first in Python, but then ported it to C and had it running on the Arduino as part of my ANGST (Arduino ‘n Gameduino Satellite Tracker) project. But that project had a number of shortcomings:

  • It was fairly complicated, and required a number of additional add-ons to work.  I used the Gameduino to generate graphics, had an external RTC and EEPROM to store orbital data, and used an optical shaft encoder to move around.  Yes, it was attractive and fun, but not cost effective.
  • It had no easy way to update orbital elements.  You had to connect it to a PC and reprogram it with new orbital elements each time you needed an update.

But these WeMOS boards are built around the ESP8266 modules, which have a number of advantages:

  • They are based around an 80Mhz 32 bit processor, instead of a 16Mhz 8 bit processor.
  • They have 4Mbytes of flash memory.  You can easily configure some small part of that to serve as a miniature file system, ample to store orbital elements without any additional hardware.
  • They are cheap: around $5.
  • And most intriguingly: they have WiFi. 

So, I worked on the spiritual descendent of ANGST.   Using the platformio system I moved a copy of my Plan13 library to a new project, and started tinkering.   As of now, the project:

  • Wakes up and connects to my local WiFi network
  • Establishes a connection to an NTP server, so I can get the time
  • Connects to Celestrak and downloads the latest orbital elements for the ISS
  • Predicts the next three passes from my home location
  • And then enters a loop, predicting the sub-satellite latitude and longitude, its location relative to some major world cities, and if it is above the horizon, then the azimuth and altitude of the satellite.

Here is the current data as I write this:

But the one thing it did not have until this morning was an external display.  Hence, the TM1637, and a little tinkering.

Time is in GMT, but eventually I will also have it have the option of using local time (it is more convenient to use GMT for the satellite computations.)

To get this to work, I could have dusted off the work to drive the similar TM1640 based displays that I used in another project, but I thought that I’d use a standard library instead.   The platformio system has a pretty nifty library manager, so I searched for a library, found one (cool!) and it claimed compatibility with the ESP8266.  Neat.

So I read some of the example code, patched it to output the time in the main loop(), and tried it.

It didn’t work.

I double-checked connections.

It didn’t work.

I tried their example.

It didn’t work.  The displays remained dark.

Grumble.  I opened up the code and tried to see what was going on.   It didn’t take me too long to figure out what was going on:

Here is a link to the code, and a screen grab of the offending lines:

Link to the source on github

It’s not exactly my first rodeo, so I was immediately suspicious when I saw that the initializer set the mode for the two pins to INPUT, rather than OUTPUT, but then immediately wrote to them.  That doesn’t seem right, does it?

Well, this isn’t even my second rodeo.  I know that Arduino’s ATMega328 processor has internal pull up resistors, and I’ve seen odd code which used pinMode to swap back and forth between high and low, not using pinMode.

But the ESP8266 (thought I) doesn’t have internal pull ups.   I’m pretty sure that’s true, but I never got to verify it, because all of a sudden, with no change whatsoever, my program started working.

hate it when that happens. 

It’s been that kind of a week.

But it made me think about how this works.  Here is my guess, and I’d appreciate corrections.

During initialization, it sets pinMode to INPUT, and writes a LOW value to the data pin.   Even though we’ve written a LOW, the pin is pulled HIGH by the action of the (external) pull up resistor.

When the driver wishes to shift the output pin to LOW, it uses pinMode() to shift the type for the pin to OUTPUT.   When it wants it to be high, it sets pinMode() to INPUT.

I find this odd in a couple of ways:

  • It is not any shorter or more efficient than using digitalWrite.
  • It is certainly no clearer.

Is there something I’m missing about why you would code this stuff this way?

Of course, I wouldn’t have dug into this code at all had I not had a mysterious (and fleeting) failure at the beginning, so I am not sure how it really matters.

Addendum:  While I was typing this, the ISS did a 65 degree elevation pass which was captured thusly.  I only then noticed that it was producing duplicate messages for each five second message.  Must have goofed when I installed the code to blink the clock’s “colon” segments.  I’ll get it sorted later.


How making an ISS clock turns into researching an optimization problem involving spherical Voronoi diagrams…

A few years ago, I got seriously interested in amateur radio and satellites. I would often haul out a small Yagi antenna and my trusty Kenwood TH-D7A and try to talk to other hams by bouncing signals off the various satellites which acted as amateur radio repeaters. During that time, I wrote a Python version of G3RUH’s Plan-13 algorithm for predicting satellite locations. Eventually, I converted that code to C++ and made an Arduino/Gameduino version that I called “Angst”.

But there were a number of problems with it as a general project. Most notably, it utilized some special hardware which made it more expensive than I would like, and it required an external display. I suppose I could have found a small composite display, but one serious issue remained: there was no simple way to update the orbital elements that are used to predict the satellite locations. The ISS is particularly problematic, since it is in low earth orbit and frequently fires its engines to boost it to a higher orbit, which is not something that can be accounted for statically.

But in the years since I did that project, the ESP8266 has taken the world by storm. Not only is it cheap (I got some WeMos D1 Mini clones for less than $5 shipped from China) but it is faster (an 80Mhz 32 bit processor) and most importantly includes WiFi.

For the last couple of days, I’ve been tinkering my old Plan-13 code together with some new code written especially for the ESP8266 to create a “clock” that will automatically tell me the position of the ISS and details about the next pass. The nice thing is that since the ESP8266 has WiFi, it can update itself automatically. Right now, I have code running that contacts celestrak.com, finds the latest orbital elements, predicts the next three passes, and then enters a loop where it shows the current position of the ISS every five seconds. Right now, it just echoes that information out on the serial line, but eventually I’ll get a tiny TFT or LED display to display the live information:

But just seeing the latitude and longitude of the “sub satellite” point didn’t really help me know where the satellite was in its orbit. Sure, I could (and probably will) make a little graphical display for the gadget sooner or later, but I thought it might be nice if I could have it tell me when it was passing close to certain major cities. To give this a try, I found a list of world cities and their coordinates and population, and selected the top fifty cities of the world and within the United States (make one hundred cities total) and then when I do a prediction, quickly compute the distance and bearing to each of the cities and display the closest one. And that works, and doesn’t take too much time. You can see in the run above that the closest city was Honolulu.

But, I noticed a problem.

If you imagine that the earth is a sphere, and imagine that the each city is a point on the surface which you can think of as a seed. If you collect all points on the sphere that are closest (in the great circle) sense to that point than any other seed, you end up with the globe being divided up into a number of regions, each bounded by arcs of a great circle. If you took the same computer and math classes as me, you can recognize this as a “spherical voronoi diagram”. You can read about them here where I swiped the illustration from. Here is the Voronoi diagram of the earth with seeds at each of the World Airports:

You’ll notice something: the regions are widely varying in size. If I had loaded my ISS code with these locations, it would spend a lot of time talking about how it is thousands of miles from some remote airport in the middle of the Pacific, but when it was flying over the United States, it would rapidly shift from region to region. Indeed, because population centers are often close, it might be fairly rare for it to be closest to New York City, even though it is perhaps an obvious landmark, more obvious than the smaller ones that surround it. It might also be the case that if you have two population centers which are fairly close to one another, that approaching from one side would be masked by the other city.

Ideally, we’d like a collection of cities which:

  • are well known
  • divide the globe into a number of Voronoi regions which are nearly the same size
  • and where shape of the Voronoi regions are not too lopsided, so the cities are near the center of a roughly circular region

I haven’t figured out how to do this yet, but I’m betting that I am going to use some kind of genetic algorithm/simulated annealing approach. Stay tuned.

Exciting AMSAT-NA opportunity…

I read this today:

AMSAT is excited to announce that we have accepted an opportunity to participate in a potential rideshare as a hosted payload on a geostationary satellite planned for launch in 2017. An amateur radio payload, operating in the Amateur Satellite Service, will fly on a spacecraft which Millennium Space Systems (MSS) of El Segundo, CA is contracted to design, launch, and operate for the US government based on their Aquila M8 Series Satellite Structure.

Complete Press Release Here

This would be very exciting. The only satellites I’ve worked have really worked with any regularity have been the FM satellites in low earth orbit. I’d love to have an opportunity to develop a station to work a bird in geostationary orbit. I’ll be watching this closely, and probably kicking in some donations to AMSAT-NA to grease the skids.

SSTV from the ISS…

Well, it’s not pretty, but I was just using a 17″ whip antenna on my VX-8GR, recorded it with Audacity, and then decoded it with MultiScan on my Macbook. The first bit of the recording is pretty rocky, so I had to start the sync myself. I’ve bean meaning to do some experiments with bad audio and sync recovery, now I have more data.

Oh, in case this was all gibberish to you, the Russians have been running “events” from the International Space Station to honor their cosmonauts by transmitting pictures via slow scan television (SSTV). I received this picture using what most people would call a walkie talkie, a whip antenna, and a laptop.

As decoded by Multiscan:


I thought a second image would have begun later in the pass, but didn’t hear it.

I think an antenna with a little more gain, and/or a preamplifier would help a lot. You really need pretty noise free audio to make a good picture. Still, a fun experiment. I might try the 12:30AM pass tonight.

Addendum: The second pass was also a little rocky. Got the tail end of one transmission fairly cleanly, but the three minute gap to the next one meant it was low. This is what I got.

second pass

Slow Scan Television from the ISS this weekend…

Note: This post was adapted by an email that I sent out to our ham radio club.

If anyone is interested in a fun little ham radio related activitytonight, you can try to receive slow scan television from the International Space Station this weekend. I haven’t done this in a while,but I think I’ll give it a try and see what I can come up with.

You can read about this event here:

AMSAT UK on the upcoming ISS event

They will be on 145.800Mhz (in the 2m band).

The way I usually “work” these is to use one of my HTs. A better antenna than the stock one is usually good (a longer whip, or even a yagi) but you might just see what you can here with the stock antenna. The ISS transmits with 25 watts of power, which is usually pretty easy to hear. I have a set of earphones that I hook with a splitter. One half goes to my earbuds, the other to a small digital audio recorder I have. Turn the squelch on your radio off so you can here the signal when it is weak. You may find that moving your antenna round will help a bit, so monitor with your earphones. Don’t be shocked if you don’t hear the ISS right at the rise time: it has 3 minutes of dead time between transmissions, which take about 3 minutes to send. It sounds a bit like a ticking of a clock, with a whistle in between, if you click this link, you can hear what it sounds like:

I like to record the audio, then play it back into my Windows PC and use the MMSSTV program, but you can actually go completely low tech and try an inexpensive iphone app, held up to the speaker of your HT. I use

Black Cat System’s SSTV program for the iPhone/Ipad

which works okay, not amazing. If you are out doors in a windy or noisy location, your image won’t be as good this way: the bg noise will cause interference.

To help out, I computed a set of rise/set/max elevation tables centered on San Francisco. If you live close, you can probably use these times. If you live in other parts of the country, you might try looking at the Heaven’s Above website. Select “Passes to include” to be all, and enter your location in the upper right. The table below was calculated by my own software.

Rise time           Azi    Max Elev Time        Elev  Set time             Azi
2015/04/11 16:24:33 178.90 2015/04/11 16:28:52   9.27 2015/04/11 16:33:10  74.10 (Local Time)
2015/04/11 23:24:34 178.90 2015/04/11 23:28:52   9.27 2015/04/11 23:33:11  74.10 (UTC)

2015/04/11 17:59:18 232.14 2015/04/11 18:04:47  76.70 2015/04/11 18:10:17  49.52 (Local Time) [1]
2015/04/12 00:59:18 232.14 2015/04/12 01:04:48  76.70 2015/04/12 01:10:17  49.52 (UTC)

2015/04/11 19:36:48 276.47 2015/04/11 19:41:38  13.93 2015/04/11 19:46:28  40.34 (Local Time)
2015/04/12 02:36:48 276.47 2015/04/12 02:41:38  13.93 2015/04/12 02:46:28  40.34 (UTC)

2015/04/11 21:15:06 309.66 2015/04/11 21:19:13   7.29 2015/04/11 21:23:21  47.92 (Local Time)
2015/04/12 04:15:06 309.66 2015/04/12 04:19:14   7.29 2015/04/12 04:23:21  47.92 (UTC)

2015/04/11 22:52:10 319.85 2015/04/11 22:56:52  12.34 2015/04/11 23:01:34  78.97 (Local Time) [2]
2015/04/12 05:52:10 319.85 2015/04/12 05:56:53  12.34 2015/04/12 06:01:35  78.97 (UTC)

2015/04/12 00:28:22 312.09 2015/04/12 00:33:48  58.58 2015/04/12 00:39:14 122.75 (Local Time) [3]
2015/04/12 07:28:22 312.09 2015/04/12 07:33:49  58.58 2015/04/12 07:39:15 122.75 (UTC)

2015/04/12 02:05:15 289.69 2015/04/12 02:09:49  11.95 2015/04/12 02:14:23 174.60 (Local Time)
2015/04/12 09:05:16 289.69 2015/04/12 09:09:50  11.95 2015/04/12 09:14:24 174.60 (UTC)

[1] Probably the easiest pass, the ISS passes almost straight overhead,
should be loud and easy.

[2] A low night time pass, but the ISS should be visible to the naked eye.

[3] Another night time pass, but too late for the ISS to catch any
sun. 58 degrees is a good pass, the second one.

If I get any good images, I’ll send them out next week.

Increasing pyephem’s accuracy for satellite rise/set calculations…

A few years ago, I created my own Python implementation of the Plan13 satellite prediction code written by James Miller (G3RUH). The Plan13 algorithm isn’t very complicated: you can easily run it on processors like the Arduino (in fact, I used it for my ANGST satellite tracker) But somehow, I managed to misplace the source code for the Python version, probably on a hard drive for a computer that died, and so when I wanted to do some ISS calculations, I decided that I’d go ahead and use PyEphem, a much more complete package that can do all sorts of astronomical calculations, and which includes an implementation of the fairly standard SGP4 model.

It’s fairly simple to write some code to predict ISS passes from any position on earth. Here’s a quick example, with the TLEs for the ISS hard coded, as well as an observer position:

#!/usr/bin/env python

from math import degrees
import ephem

# create an observer
obs = ephem.Observer()
obs.lat = '38.0'
obs.lon = '-122'
obs.elevation = 100.

# create the iss object...

iss = ephem.readtle("ISS (ZARYA)             ",
    "1 25544U 98067A   15067.48441091  .00017347  00000-0  26069-3 0  9998",
    "2 25544  51.6448 227.0292 0008846  83.1031  17.2346 15.54929647932364")

# figure out the next pass...
# only interested in rt (rise time), tt (transit time) and st (set time)

rt, razi, tt, televation, st, sazi = obs.next_pass(iss)

print "Rise Time:   ", rt
print "Transit Time:", tt
print "Set Time:    ", st

But I was somewhat chagrined that if I ran this little snippet multiple times, I got different answers. Not by a lot, but by several seconds:

pi@blueberrypi ~ $ !.
Rise Time:    2015/3/8 22:30:12
Transit Time: 2015/3/8 21:00:52
Set Time:     2015/3/8 21:04:09
pi@blueberrypi ~ $ !!
Rise Time:    2015/3/8 22:30:12
Transit Time: 2015/3/8 21:00:53
Set Time:     2015/3/8 21:04:10
pi@blueberrypi ~ $ !!
Rise Time:    2015/3/8 22:30:13
Transit Time: 2015/3/8 21:00:53
Set Time:     2015/3/8 21:04:10
pi@blueberrypi ~ $ !!
Rise Time:    2015/3/8 22:30:03
Transit Time: 2015/3/8 21:00:49
Set Time:     2015/3/8 21:04:11

Digging in the code, it’s not hard to see what’s going on. The code is stepping forward by intervals of about 1 minute, trying to catch the satellite as it peaks over the horizon. When it does, it then uses twenty iterations of the secant method to find the point where the satellite crosses zero altitude. But there is an early out: if the interval drops below ten seconds, it goes ahead and exists. Similarly, it only locates the time of the transit to within 15 seconds. But these can be fixed with some quick, and probably undisciplined changes to the code will make things behave better.

Both edits are in the file riset_cir.c which is part of libastro-3.7.5 (your version might change, but will probably be the same). Near the top you’ll find the declaration for TMACC

#define TMACC   (10./3600./24.0)        /* convergence accuracy, days */

Change this to 0.5 seconds instead.

#define TMACC   (0.5/3600./24.0)        /* convergence accuracy, days */

You might want to increase MAXPASSES in the find_0alt function, but I had no difficulty with leaving it at 20.

Similarly, we need to change the error in find_transit:

static int
find_transit (double dt, Now *np, Obj *op)
#define MAXLOOPS        10
#define MAXERR          (0.25/60.)              /* hours */

The number of iterations is pretty low (just 10) and the MAXERR was 15 seconds. I decided to add some iterations to ensure convergence, and to set the MAXERR to be just one second.

static int
find_transit (double dt, Now *np, Obj *op)
#define MAXLOOPS        20
#define MAXERR          (1./3600.)              /* hours */

With these changes in place, you get times which are accurate to around one second. Note: I do not mean to imply that these numbers are somehow absolutely better. It is known that the SGP4 model can only locate satellites to errors of around 1km per day at best, so trying to converge them to insane levels of accuracy isn’t meaningful. But for my application, I want to count down to transits, and having the estimate jump around by a few seconds made the count down look funny. Of course, I could have just computed the times once, and used that, but I still think it’s kind of odd that the estimates vary by a human discernable amount depending solely on what time you decide to try to compute from. This additional accuracy makes the program a very tiny bit slower, but is worth it for my application.

Addendum: A few more lines of code make the code a bit more useful. This now outputs the next pass expressed in the localtime rather than UTC, and gives you a bit of a countdown.

#!/usr/bin/env python

from math import degrees
import ephem
import datetime

# create an observer
obs = ephem.Observer()
obs.lat = '38.0'
obs.lon = '-122'
obs.elevation = 100.

# create the iss object...

iss = ephem.readtle("ISS (ZARYA)             ",
    "1 25544U 98067A   15067.48441091  .00017347  00000-0  26069-3 0  9998",
    "2 25544  51.6448 227.0292 0008846  83.1031  17.2346 15.54929647932364")

# figure out the next pass...
# only interested in rt (rise time), tt (transit time) and st (set time)

n = ephem.now()

rt, razi, tt, televation, st, sazi = obs.next_pass(iss)

print "%d" % round((rt - n) * 3600 * 24), "seconds until the next pass."

rt = ephem.localtime(rt).strftime("%x %X")
tt = ephem.localtime(tt).strftime("%x %X")
st = ephem.localtime(st).strftime("%x %X")

print "Rise Time:   ", rt, "Azimuth:   %5.1f" % degrees(razi)
print "Transit Time:", tt, "Elevation: %5.1f" % degrees(televation)
print "Set Time:    ", st, "Azimuth:   %5.1f" % degrees(sazi)

Addendum2: A few more lines of code fetches a current set of orbital elements from celestrak.com. A future revision of this will probably cache a local copy of these orbital elements so you don’t unnecessarily hammer their server.

#!/usr/bin/env python

from math import degrees
import ephem
import datetime
import urllib2

req = urllib2.Request("http://celestrak.com/NORAD/elements/amateur.txt")
response = urllib2.urlopen(req)
data = response.read().splitlines() 

objs = []

for idx in range(0, len(data), 3):
    objs.append(ephem.readtle(data[idx], data[idx+1], data[idx+2]))

# create an observer
obs = ephem.Observer()
obs.lat = '38.0'
obs.lon = '-122'
obs.elevation = 100.

# create the iss object...

iss = ephem.readtle("ISS (ZARYA)             ",
    "1 25544U 98067A   15067.48441091  .00017347  00000-0  26069-3 0  9998",
    "2 25544  51.6448 227.0292 0008846  83.1031  17.2346 15.54929647932364")

# figure out the next pass...
# only interested in rt (rise time), tt (transit time) and st (set time)

tlist = []

for obj in objs:
    r = obs.next_pass(obj)
    tlist.append((r[0], obj, r))


for t, obj, r in tlist:

    n = ephem.now()

    rt, razi, tt, televation, st, sazi = r

    print obj.name
    print "%d" % round((rt-n) * 3600 * 24), "seconds until the next pass."

    rt = ephem.localtime(rt).strftime("%x %X")
    tt = ephem.localtime(tt).strftime("%x %X")
    st = ephem.localtime(st).strftime("%x %X")

    print "Rise Time:   ", rt, "Azimuth:   %5.1f" % degrees(razi)
    print "Transit Time:", tt, "Elevation: %5.1f" % degrees(televation)
    print "Set Time:    ", st, "Azimuth:   %5.1f" % degrees(sazi)


The output is now a list of all the amateur satellites that it fetched, sorted in order of the passes which are soonest appear first.

410 seconds until the next pass.
Rise Time:    03/08/15 18:33:03 Azimuth:   272.2
Transit Time: 03/08/15 18:37:54 Elevation:   8.1
Set Time:     03/08/15 18:42:45 Azimuth:     9.4

526 seconds until the next pass.
Rise Time:    03/08/15 18:34:59 Azimuth:    31.0
Transit Time: 03/08/15 18:41:08 Elevation:  16.4
Set Time:     03/08/15 18:47:16 Azimuth:   153.1

1228 seconds until the next pass.
Rise Time:    03/08/15 18:46:41 Azimuth:    91.6
Transit Time: 03/08/15 18:53:13 Elevation:   8.5
Set Time:     03/08/15 18:59:45 Azimuth:     6.5

OSCAR 7 (AO-7)
1251 seconds until the next pass.
Rise Time:    03/08/15 18:47:04 Azimuth:   229.2
Transit Time: 03/08/15 18:54:42 Elevation:   8.5
Set Time:     03/08/15 19:02:20 Azimuth:   317.9

2080 seconds until the next pass.
Rise Time:    03/08/15 19:00:54 Azimuth:    29.7
Transit Time: 03/08/15 19:07:39 Elevation:  19.3
Set Time:     03/08/15 19:14:25 Azimuth:   157.0

2515 seconds until the next pass.
Rise Time:    03/08/15 19:08:08 Azimuth:   173.2
Transit Time: 03/08/15 19:15:24 Elevation:  24.3
Set Time:     03/08/15 19:22:39 Azimuth:    41.1

CUTE-1.7+APD II (CO-65)
2940 seconds until the next pass.
Rise Time:    03/08/15 19:15:14 Azimuth:    81.8
Transit Time: 03/08/15 19:17:56 Elevation:   1.9
Set Time:     03/08/15 19:20:37 Azimuth:    31.8

JAS-2 (FO-29)
2956 seconds until the next pass.
Rise Time:    03/08/15 19:15:30 Azimuth:   255.5
Transit Time: 03/08/15 19:19:08 Elevation:   2.2
Set Time:     03/08/15 19:22:47 Azimuth:   307.5

3635 seconds until the next pass.
Rise Time:    03/08/15 19:26:49 Azimuth:   354.1
Transit Time: 03/08/15 19:32:20 Elevation:  12.4
Set Time:     03/08/15 19:37:52 Azimuth:   247.0

3940 seconds until the next pass.
Rise Time:    03/08/15 19:31:54 Azimuth:   347.2
Transit Time: 03/08/15 19:37:10 Elevation:   9.9
Set Time:     03/08/15 19:42:26 Azimuth:   248.6

4134 seconds until the next pass.
Rise Time:    03/08/15 19:35:08 Azimuth:   194.6
Transit Time: 03/08/15 19:42:31 Elevation:  27.0
Set Time:     03/08/15 19:49:53 Azimuth:   334.6

4222 seconds until the next pass.
Rise Time:    03/08/15 19:36:36 Azimuth:   235.6
Transit Time: 03/08/15 19:40:29 Elevation:   4.5
Set Time:     03/08/15 19:44:22 Azimuth:   310.9

4468 seconds until the next pass.
Rise Time:    03/08/15 19:40:41 Azimuth:    13.2
Transit Time: 03/08/15 19:47:14 Elevation:  82.2
Set Time:     03/08/15 19:53:46 Azimuth:   192.3

CUTE-1 (CO-55)
4562 seconds until the next pass.
Rise Time:    03/08/15 19:42:16 Azimuth:   198.2
Transit Time: 03/08/15 19:49:30 Elevation:  23.6
Set Time:     03/08/15 19:56:44 Azimuth:   332.9

4714 seconds until the next pass.
Rise Time:    03/08/15 19:44:47 Azimuth:    18.5
Transit Time: 03/08/15 19:52:18 Elevation:  49.9
Set Time:     03/08/15 19:59:48 Azimuth:   181.1

4722 seconds until the next pass.
Rise Time:    03/08/15 19:44:55 Azimuth:   342.9
Transit Time: 03/08/15 19:44:56 Elevation:  -0.0
Set Time:     03/08/15 19:44:56 Azimuth:   342.9

5115 seconds until the next pass.
Rise Time:    03/08/15 19:51:28 Azimuth:   279.5
Transit Time: 03/08/15 19:57:25 Elevation:   3.7
Set Time:     03/08/15 20:03:21 Azimuth:   342.8

5411 seconds until the next pass.
Rise Time:    03/08/15 19:56:24 Azimuth:    92.7
Transit Time: 03/08/15 20:00:09 Elevation:   3.9
Set Time:     03/08/15 20:03:54 Azimuth:    25.1

UOSAT 2 (UO-11)
6509 seconds until the next pass.
Rise Time:    03/08/15 20:14:43 Azimuth:    95.0
Transit Time: 03/08/15 20:18:33 Elevation:   4.5
Set Time:     03/08/15 20:22:23 Azimuth:    22.8

6557 seconds until the next pass.
Rise Time:    03/08/15 20:15:30 Azimuth:   127.3
Transit Time: 03/08/15 20:21:07 Elevation:  16.4
Set Time:     03/08/15 20:26:43 Azimuth:     6.7

6694 seconds until the next pass.
Rise Time:    03/08/15 20:17:48 Azimuth:    76.8
Transit Time: 03/08/15 20:20:03 Elevation:   1.3
Set Time:     03/08/15 20:22:18 Azimuth:    35.9

7326 seconds until the next pass.
Rise Time:    03/08/15 20:28:20 Azimuth:   128.2
Transit Time: 03/08/15 20:36:14 Elevation:   6.0
Set Time:     03/08/15 20:44:09 Azimuth:    56.8

7721 seconds until the next pass.
Rise Time:    03/08/15 20:34:55 Azimuth:   139.6
Transit Time: 03/08/15 20:41:44 Elevation:  28.3
Set Time:     03/08/15 20:48:33 Azimuth:     0.7

9556 seconds until the next pass.
Rise Time:    03/08/15 21:05:29 Azimuth:   126.2
Transit Time: 03/08/15 21:11:12 Elevation:  15.9
Set Time:     03/08/15 21:16:56 Azimuth:     7.5

9914 seconds until the next pass.
Rise Time:    03/08/15 21:11:28 Azimuth:   125.2
Transit Time: 03/08/15 21:17:05 Elevation:  15.3
Set Time:     03/08/15 21:22:43 Azimuth:     8.0

9936 seconds until the next pass.
Rise Time:    03/08/15 21:11:49 Azimuth:   118.8
Transit Time: 03/08/15 21:17:04 Elevation:  11.9
Set Time:     03/08/15 21:22:20 Azimuth:    11.1

10153 seconds until the next pass.
Rise Time:    03/08/15 21:15:26 Azimuth:   121.9
Transit Time: 03/08/15 21:20:52 Elevation:  13.4
Set Time:     03/08/15 21:26:17 Azimuth:     9.6

DELFI-C3 (DO-64)
11107 seconds until the next pass.
Rise Time:    03/08/15 21:31:21 Azimuth:   142.9
Transit Time: 03/08/15 21:37:20 Elevation:  29.2
Set Time:     03/08/15 21:43:19 Azimuth:   359.6

12799 seconds until the next pass.
Rise Time:    03/08/15 21:59:32 Azimuth:   133.5
Transit Time: 03/08/15 22:05:21 Elevation:  20.4
Set Time:     03/08/15 22:11:09 Azimuth:     4.0

13942 seconds until the next pass.
Rise Time:    03/08/15 22:18:36 Azimuth:   136.2
Transit Time: 03/08/15 22:24:17 Elevation:  22.0
Set Time:     03/08/15 22:29:58 Azimuth:     3.0

15152 seconds until the next pass.
Rise Time:    03/08/15 22:38:45 Azimuth:   129.1
Transit Time: 03/08/15 22:44:45 Elevation:  18.7
Set Time:     03/08/15 22:50:44 Azimuth:     4.6

15611 seconds until the next pass.
Rise Time:    03/08/15 22:46:24 Azimuth:    92.6
Transit Time: 03/08/15 22:50:05 Elevation:   4.0
Set Time:     03/08/15 22:53:46 Azimuth:    24.0

16144 seconds until the next pass.
Rise Time:    03/08/15 22:55:18 Azimuth:   204.1
Transit Time: 03/08/15 23:00:40 Elevation:   6.1
Set Time:     03/08/15 23:06:02 Azimuth:   124.2

16575 seconds until the next pass.
Rise Time:    03/08/15 23:02:29 Azimuth:    35.0
Transit Time: 03/08/15 23:07:53 Elevation:  12.7
Set Time:     03/08/15 23:13:17 Azimuth:   148.7

17209 seconds until the next pass.
Rise Time:    03/08/15 23:13:03 Azimuth:   176.4
Transit Time: 03/08/15 23:15:40 Elevation:   1.1
Set Time:     03/08/15 23:18:17 Azimuth:   139.8

18892 seconds until the next pass.
Rise Time:    03/08/15 23:41:05 Azimuth:    73.7
Transit Time: 03/08/15 23:43:37 Elevation:   1.4
Set Time:     03/08/15 23:46:09 Azimuth:    31.4

19177 seconds until the next pass.
Rise Time:    03/08/15 23:45:51 Azimuth:   170.5
Transit Time: 03/08/15 23:52:00 Elevation:  17.7
Set Time:     03/08/15 23:58:10 Azimuth:    47.9

19303 seconds until the next pass.
Rise Time:    03/08/15 23:47:57 Azimuth:    77.8
Transit Time: 03/08/15 23:50:51 Elevation:   2.0
Set Time:     03/08/15 23:53:45 Azimuth:    28.8

19890 seconds until the next pass.
Rise Time:    03/08/15 23:57:44 Azimuth:   141.2
Transit Time: 03/09/15 00:03:57 Elevation:  28.7
Set Time:     03/09/15 00:10:10 Azimuth:   359.5

21056 seconds until the next pass.
Rise Time:    03/09/15 00:17:09 Azimuth:   104.3
Transit Time: 03/09/15 00:22:11 Elevation:   8.1
Set Time:     03/09/15 00:27:12 Azimuth:    14.2

24703 seconds until the next pass.
Rise Time:    03/09/15 01:17:56 Azimuth:    96.4
Transit Time: 03/09/15 01:22:04 Elevation:   5.2
Set Time:     03/09/15 01:26:12 Azimuth:    20.4

25871 seconds until the next pass.
Rise Time:    03/09/15 01:37:24 Azimuth:    29.2
Transit Time: 03/09/15 01:44:13 Elevation:  20.0
Set Time:     03/09/15 01:51:01 Azimuth:   157.9

26776 seconds until the next pass.
Rise Time:    03/09/15 01:52:30 Azimuth:    42.8
Transit Time: 03/09/15 01:57:50 Elevation:   7.6
Set Time:     03/09/15 02:03:10 Azimuth:   133.0

27579 seconds until the next pass.
Rise Time:    03/09/15 02:05:52 Azimuth:    41.3
Transit Time: 03/09/15 02:11:26 Elevation:   8.3
Set Time:     03/09/15 02:17:00 Azimuth:   134.4

31680 seconds until the next pass.
Rise Time:    03/09/15 03:14:13 Azimuth:    41.7
Transit Time: 03/09/15 03:19:13 Elevation:   8.3
Set Time:     03/09/15 03:24:13 Azimuth:   137.6

38130 seconds until the next pass.
Rise Time:    03/09/15 05:01:43 Azimuth:   115.2
Transit Time: 03/09/15 04:15:25 Elevation: -74.9
Set Time:     03/09/15 05:01:43 Azimuth:   115.2

Addendum3: Hmm. If you look carefully, the last entry in the test output is bogus: the time of maximum elevation does not fall between the rise and set time, and the elevation is negative. Staring at the code some more, I’m now worried about the logic of the root finder. I’ll have to ponder it some more.

Digital ATV resources…

I’ve been doing a bunch of reading about digital ATV operations lately. I was originally motivated by hearing about the HamTV project aboard the ISS. Back in 2007, I got re-energized into ham radio by learning that for the 50th anniversary of Sputnik, the amateur satellite AO-51 would broadcast a cool message that I heard with a simple HT. I’m wondering if I’m having that kind of a moment now: the idea of creating a station to broadcast digital television seems challenging but doable.

While reading up on the necessary bits for a HamTV downlink station, I found that this little satellite receiver which sells for less than twenty-five dollars could be used to decode signals from the ISS. It receives DVB-S signals, which are used by direct satellite broadcasters like Dish Network. But in thinking about how to go forward with the project, it seemed to me like trying to aim directly for a satellite downlink station was likely to be a frustrating endeavor. It requires a number of different subsystem to work together, but trying to receive the DVB-S from the ISS (given an intermittent schedule) would be difficult to test together. So, I started looking for resources that I could use to build a similar terrestrial station, including both a transmitter and receiver.

A couple of cool links:

The DATV-Express board is a $300 exciter board that is in limited production. It seems very cool. Reading the Tech Talks on this site yielded a lot of good information, I’m particularly pondering the information in this one, about designing a digital TV station.

Another similar project, but available more as a board/kit is the Digilite project. An interesting variation of this project is the DigiliteZL project, which makes for a compact and interesting setup.

I also like the CQ-DATV magazine. It’s got lots of cool information, published in a series of magazines available in a variety of e-book formats. They also have a broad collection of interesting articles on the Digilite project, which I’m currently reading over.

I’ll probably stick to more experimentation with SSTV, but this stuff fascinates me, and I may have the opportunity to do something interesting with it in the future.

Building a distributed satellite ground station network (or not…)

My twitter intro says that I am an “enthusiast for enthusiasm”. When I wrote that, it was simply because there are some questions that I really think aren’t helpful at all. Questions like:

  • Why didn’t you just buy X instead of building your own?
  • Didn’t somebody do that years ago? Why are you playing with that old technology?
  • Why are you writing a program to do that, when you could just use Y, some program/framework/application that I use.

I hate questions like this because they aren’t really questions at all: they are simply trying to tell you that what you are doing is stupid or pointless. Here’s the thing: I mostly understand why I do the projects that I do, and I’m perfectly okay with you not understanding my rationale, or agreeing with it even if you do understand. The proper answer is “why climb a mountain?” isn’t “because it was there”. It’s not even “because no one has before”. The proper answer is “because I’ve not done it, and I enjoy mountain climbing.” Interestingly, most people won’t try to convince you that you shouldn’t like mountain climbing, but all sorts of people will try to tell you that your technical projects are a waste of time. This kind of conversation actually irritates me.

And with all this introduction, I’m going to now criticize a project, which is going to seem a bit hypocritical. Hang with me to the end, and I’ll try to resolve the apparent hypocrisy, at least partially.

The topic is the plethora of news stories about a talk given at the Chaos Communication Congress (28c3) recently held in Berlin. Some links to news stories:

Hackers aim to launch Internet satellite network, moon mission
Hacker satellite grid to counter Internet censorship??
Hackers Plan Satellite Network to Fight Internet Censorship

So, what’s my beef? After all, any reader to this blog knows that I’m interested in amateur satellite and communications, surely this is right up my alley?

And indeed it is. But the motivation is just… well… it’s stupid. Not stupid because fighting censorship is a bad idea: it’s a very, very good idea. Even essential. But the idea that satellites constructed by amateurs can play any role (much less any significant role) in fighting censorship is fantasy.

First of all, launching satellites is expensive. Really expensive. While the hardware of cubesats can be constructed quite economically, launches have costs which are multiples of ten thousand dollars, for masses which are less than 1kg launched into low earth orbit. Currently AMSAT has a project called FOX to develop a communication satellite that fits the cubesat form factor, but it’s capacity and power are very limited, mostly by the physical size and weight limitations imposed by available launch opportunities. To launch a satellite into MEO or HEO would require costs measured in the millions of dollars.

Secondly, you can’t get spectrum to operate a satellite network like they imagine. Amateur radio frequencies are subject to regulation and treaties just like any other spectrum, and the uses of such frequency are dictated by regulation and treaty. The governing international body is the IARU (the International Amateur Radio Union) and member nations enact local regulations to enforce treaty restrictions to comply with the regulations of the IARU. The purpose of amateur radio satellites must be to “(1) provide communication for the general amateur radio community and/or (2) self training and technical investigations relating to radio technique”. While these topics are fairly broad, they are not broad enough to provide a general replacement for the Internet. In fact, in the U.S. amateur radio is specifically prohibited from carrying “communications, on a regular basis, which could reasonably be furnished alternatively through other radio services.” You can’t get the frequency allocations. Nobody will launch a satellite without frequency coordination.

Third, if your concern is to bypass the censorship of governments, it seems odd to do it by launching a satellite, because governments tend to have very strict and tight controls over satellites and satellite technology. For instance, in the U.S. ITAR regulations essential prohibit the transfer of dual use technologies to other countries, even to our allies. This isn’t just a theoretical concern: American participation in the amateur satellite projects of other countries have been significantly stifled But even more basic than these issues are the fact that access to space is currently under the control of the very governments we are concerned about. While increasing commercialization is eroding that to a certain degree, we cannot rely on commercial entities to operate in defiance of the governments of the countries in which they operate. There is some possibility that an organization such as Copenhagen Suborbitals might be able provide launches, but these operations must operate within the regulations of the countries from which they operate as well, so I think the idea of access to space independent of governmental interference is a fantasy.

Here’s the bottom line though: if your goal is to prevent government censorship, every dollar that you spend could do orders of magnitude more benefit using more conventional earth-bound technology. Funding projects like the FreedomBox Foundation, HTTPS Anywhere or The Tor Project, or working to generate a mesh based Wifi capability in your area are much, much stronger ways to work to combat Internet censorship.

If you want to build satellites, it’s a perfectly reasonable thing to do. But to try to sell the idea by saying that it provides a way to combat Internet censorship? That’s misleading at best.

Addendum: Here’s the actual talk at the Chaos Communication Conference. It’s actually got some cute stuff in it, mostly because it leaves behind the fantasy that a satellite communication network will provide a hedge against censorship in the first five minutes.

ANGST: the Arduino n’ Gameduino Satellite Tracker

I’ve stopped hacking on my Arduino/Gameduino satellite tracker for now. Here’s the final video demonstrating it running:

I’m currently working on the final schematic which will be posted on this permanent page. The code will be available github.com, for right now, it includes the library that I wrote that does the satellite prediction. I’ll be updating the code sometime later this week.

If you take it and use it, let me know: it will motivate me to do more with the code.

What’s Up: A Visual Database of Satellites and Debris

I’ve pondered creating something like this a bunch of times: a way to visualize all the satellites currently in Earth orbit. Somebody beat me to it, using Google Earth and a .kml file. It’s pretty astounding just how packed low earth orbit is, and it’s cool to check out the band of geostationary satellites as well.

Check it out!

What’s Up: A Visual Database of Satellites and Debris.

Still more on my Arduino/Gameduino Satellite Tracker…

I need to think up a better name for this project. Calling it the “Arduino/Gameduino Satellite Tracker” is just too damned cumbersome for words.

Progress was slow today. I woke up around 4:00AM with a sore throat and a miserable cough. A quick trip to the urgent care clinic when it opened reassured me that I didn’t have strep throat, but I do have some kind of upper respiratory infection going. Lots of rest today. I did spend a couple of hours listlessly programming and watching the Tigers/Rangers game though, the main result being I now plot the position of the sun on the map:

While the text at the bottom still says “ISS”, the satellite being tracked is actually AO-51. As you can see, it is a polar orbiting satellite, and reaches much greater latitudes than the ISS.

I’ve been trying to remodularize the original Plan 13 code in BASIC, and that has made me go through the code and restructure it a bit. The solar calculations in Plan 13 are actually a bit odd: it goes through the trouble of converting the sun’s orbital elements into the same epoch as the satellite you are watching, mostly so you can reuse the “T” variable. It’s actually quite a bit simpler to just use the sun’s orbital elements directly, and that means the sun can be a completely independent object.

I’ve also got a bit more hardware on the way. I decided I wanted to store a fairly large number of satellite elements, so I ordered some AT24C1024 serial EEPROMS with a capacity of 128Kbytes. I can easily chain up to 4 of those together, to store 512K which should be more than enough.

I’ve also got some Zigbee radios coming, so I can send new orbital elements and synchronize the time easily from my desktop. (It’s mostly an excuse to play with Zigbee, but it will make for a nice setup).

After the clinic this morning, I stopped by Fry’s in Walnut Creek and found that they carry OSEPP’s clones of the Arduino. I’d rather spend my money at Sparkfun, but it’s nice to have a local supplier for those moments where immediate gratification is essential. I bought one of their OSEPP FIO boards, which have sockets in place for Zigbee modules. What I didn’t realize is that these boards aren’t programmable over USB: I’ll have to get one of those FTDI serial boards. For now, it will sit in its plastic case until I have need of it. It is cool that the FIO is actually cheaper than the Zigbee shield that I ordered. I’ll figure out some reason to use one.

I’ll shoot another video once I get multiple satellites loaded and some improvements in the user interface.

Addendum: It dawned on me that one way to test my solar calculations was to run them at the same time every day and see if the sun traced out an Analemma, the figure-eight shaped curve that you might have seen on a globe and given very little thought to. What’s cool is my simulation shows this effect pretty clearly.

Scrappy introduces my Arduino/Gameduino Satellite Tracker

My cat Scrappy decided it was time to film a brief progress video of my Arduino/Gameduino satellite tracker. I completed the basic port and testing of my Plan13 implementation to C++ for the Arduino, and got it running pretty well. It doesn’t seem to be much more compact than Bruce Robertson’s qrpTracker code, but it isn’t any worse, and I like the way I modularized and call it a bit better. Eventually all this code will be released on this website for anyone crazy enough to want to build one for themselves.

More progress as it occurs.