Saturday, March 30, 2013

Google Earth Satellite Tracker - Footprints Update

I added support for satellite footprints using a spherical earth model approximation.  I added the bare-minimum code and configuration options required to do this.  You can control footprint visibility and color using two new configuration options: show_footprints and footprint_color.


When show_footprints is true, each satellites footprint is rendered.  Below is a snapshot of SRMSat with footprints enabled.



Ground station line of site indicators can be rendered simultaneously.  Below you can observe two example ground stations within the footprint of SRMSat.  Both ground stations are displaying their line of sight indicators.


After a few seconds, I took another shot from the top.  Notice how the ground stations are right near the edge of the footprint and line of sight indicators are still enabled (as expected).


As the footprint moves away from the ground stations, we would expect to lose line of sight.  Below, you can observe an example ground station right on the edge of the footprint.


Eventually, the ground station will sit right outside of the footprint and line of sight will be lost.


Now that basic footprint functionality is complete, we have many more possibilities.  The next step could be to plot collections of ground station networks and plan telemetry collection schedules.  But before doing this, I am going to finish implementing the same type of visualization but for ground station visibility.


Friday, March 29, 2013

Google Earth Satellite Tracker - Caching Update

Implementing caching of keplerian elements was reasonably straightforward.  I added two new options within the keps section; 'cache' and 'use_cache'.  The first option specifies if keps should be cached or not.  The second option specifies if cached keps should be used.


Cached keps are stored on disk in a file named after the source.  The files are given a .tle extension.



You will notice a few new runtime messages related to caching.  The image below shows what some of these messages look like when you first run the tool.


If use_cache is enabled and a cache filename is found on disk, the tool will use the cached keps.


Besides creating a cache file on disk and automatically loading it, no other serious changes needed to be made.

Sunday, March 24, 2013

Google Earth Satellite Tracker - Line of Sight Update

I hacked at the ground station implementation for a few tonight.  My first goal was to cast a line-of-sight trace from the ground station to the satellite once it becomes visible to the ground station.  In order to support this, I added two more options to the ground station configuration:


The first option is a boolean that allows you to toggle line of site visibility.  The second option allows you to set the line color.  These options apply equally to all ground stations and satellites.  This means that any sat that is visible from a particular ground station will have a line of sight drawn towards it.  I set up a few sample ground stations and tested it out.  Here is a shot of one ground station with the ISS passing overhead.


Here is an example of two satellites having line of sight toward SRMSAT.


After waiting a few, we still see the two sample stations tracking SRMSAT. 


Let's hang out at the horizon to make sure the trace goes away when we lose line of sight...  Here we are getting closer to the horizon.


And closer to the horizon...


Until... it's over!  No more line of sight!


Putting this together was fun but now I am brainstorming better ways to visualize this, without having a bland line.  I was thinking that maybe a elliptical fan with a transparency covering the life of the pass would look cleaner.  I'll likely implement this then post-back some screenshots for opinions.

Saturday, March 23, 2013

Google Earth Satellite Tracker - Ground Stations Update

This evening, I added ground station support to my satellite tracker.  The current implementation does nothing more than generate labeled placemarks with icons for each ground station.  I plan on adding  fields of view and line-of-sight indicators but first things first.  Take a look at these example placemarks.  These are what you get when you un-comment the examples in the default configuration file:

Even after elevating the eye-point, placemarks are still clearly visible.


Deciding how to implement ground station data using a configuration file was tricky and I am not quite sure if this is the best answer.  I added a new 'ground' section that lets you define a station_icon and a list of stations.  The list of stations contains multiple stations definitions, each with a station name longitude and latitude.


I like the way it reads, 'ground station icon' and 'ground stations' but i'm not sure how I feel about the stations list.  In the code, it boils down to doing an eval which results in a list object:
stations = eval(config.get('ground','stations'))
This was definitely an easy approach but what if I start adding more parameters to the ground stations?  Maybe users will want to associate metadata with each ground station, like RF capabilities or maybe something cosmetic, like a custom station icon.  The list could go on and on.  By taking this approach, we would ultimately end up with a list containing a bunch of nameless values, making the configuration file harder to maintain.  For now, it is what it is and I will make it more complicated when the time comes.


Friday, March 22, 2013

Google Earth Satellite Tracker

I started a Python-Powered Google Earth satellite tracking project a few days ago.  Many have demanded screenshots and additional information on it's configuration and usage, so here tis!  You start off by setting a few options in getrack.cfg:  


For now the two major configuration items are the username and password.  This is subject to change in the near future, once I implement querying Keps from multiple data sources.  That should happen tonight, so I will have to blog again tomorrow evening, woot!  Once configured, running getrack is trivial, all you have to do is execute: python getrack.py.



Once the server is running, all you need to do is drag and drop satellites.kml into Google Earth.  After dragging it in, you should see satellites.kml under Temporary Places.  Each satellite will have a unique entry that can be enabled and disabled.

Within the geographic viewer, you should see one orbit for each configured satellite.  If you zoom into the view below, you will see the ISS making a pass over the Sahara.


Once you have it up and running, have some fun and zoom in on a satellite or two.  Notice how you can modify your eye-point by panning and tilting, allowing you to view multiple perspectives:


Finally, observe how each orbits lines are styled according to their eclipse state.  When a satellite is in daylight, the line is given a yellow tint (by default).  An eclipsed satellite is identified by a green tint (by default).


If you are not a fan of the default colors, pop open the config file and set your own.

Sunday, March 17, 2013

Maidenhead Grid Square KML Generator

Someone recently asked me to generate maidenhead grid square KML for Google Earth.  Being a GIS hacker and amateur radio enthusiast, I just could not resist.  For this effort, I did not go any further than the second tile level (though it would not be difficult to do so).  I first needed to store grid field characters, grid square characters and the number of degrees for a field and square.

field_characters = 'ABCDEFGHIJKLMNOPQR'

square_characters = '0123456789'

field_lon_degrees = 20
field_lat_degrees = 10

square_lon_degrees = 2
square_lat_degrees = 1

That was easy!  With these utility variables defined, we have everything that is needed to generate the grid square bounding boxes.  By the end of the for-loops below, our field_grid_squares list will contain the grid name, centroid and bounding box for each 1st level grid square.

field_grid_squares = []

for lon in xrange(-180, 180, field_lon_degrees):
  for lat in xrange(-90, 90, field_lat_degrees):

    field_lon_character = field_characters[ int((lon + 180) / field_lon_degrees) ]
    field_lat_character = field_characters[ int((lat + 90) / field_lat_degrees) ]

    field_grid_box = [
      (lon, lat),
      (lon + field_lon_degrees, lat),
      (lon + field_lon_degrees, lat + field_lat_degrees),
      (lon, lat + field_lat_degrees)
    ]
   
    field_grid_centroid = (lon + field_lon_degrees/2.0, lat + field_lat_degrees/2.0)


    field = '%s%s' % (field_lon_character, field_lat_character)



    field_grid_squares.append( (field, field_grid_centroid, field_grid_box) )



Grid square bounding boxes were generated in a similar fashion.  Placemark KML was generated for all geometries by using the template below.  Because grid squares cover the entire earth, we need to make sure the data loads efficiently.  Loading data efficiently in Google Earth via KML is as much of an art as it is science.  It is what I like to call, a balancing act!  In this case, we're talking simple squares so it's easy stuff.  We just need to be sure to apply proper LOD / bounding box parameters to prevent all of the grid squares from being loaded at any point in time.  

<Placemark>
<name>[NAME]</name>
<Region>
<Lod>
<minLodPixels>128</minLodPixels>
</Lod>
<LatLonAltBox>
<north>[NORTH]</north>
<south>[SOUTH]</south>
<east>[EAST]</east>
<west>[WEST]</west>
<minAltitude>[MIN_ALT]</minAltitude>
<maxAltitude>[MAX_ALT]</maxAltitude>
<altitudeMode>absolute</altitudeMode>
</LatLonAltBox>
</Region>
<LineString>
<coordinates>
[COORDS]
</coordinates>
</LineString>
<styleUrl>#s</styleUrl>
</Placemark>

A couple of for loops and swaps later and we are ready to fly our grid squares!  The image below shows a shot of our grid squares from an eye-point altitude of 28000 km.





The first observation you should make is that there are no visible grid squares!  This is by-design and quite a good thing.  Let's zoom in to an eye-point of 20000 km to see what happens.






There they are!  You can now clearly identify 20 grid squares.  Let's see what happens when we lower our eye-point to 10000 km.





Now we can see many more grid squares across the Earth.  Observe how many squares are missing along the edges.  This is a natural / desirable artifact of the culling process.  By lowering the  eye-point to 5000 km, the labels become visible.




This is where things start to get interesting.  From the image above, you can tell that if we continue to zoom there will not be much left to see.  Naturally, this would be an excellent opportunity to load in more detailed squares.  Let's zoom in to an eye-point of 2000 km.



Notice how the curvature of Earth is more apparent.  Square names can now be clearly identified from the map.  And why not, one final shot from 760km.



Saturday, March 16, 2013

Python Driven - Google Earth Satellite Tracker

I finally created an open source project to promote Google Earth Satellite Tracking, see ge-satellite-tracker.  The goal of this project is to provide a simple, python-based solution for tracking satellites.  The front-end of course, is Google Earth.  This project is going to force me into formalizing all of the code I wrote to analyze ARISSat-1.  You will likely see a few blog posts about the implementation soon.  If you're interested in joining the project, let me know!

Sunday, March 10, 2013

Programming Othello / Reversi - Part 1

I dug an old dusty box out of my closet about a week ago.  While sifting through it, I realized that I had stumbled upon several ancient treasures of the past.  One of these treasures was the Nintendo Entertainment System (NES) and the other was the game Othello.  After playing a couple games against Level 4 I had defeated it, but only to be presented with the sinister level 5.  Once you make it to level 5,  there is no time limit, which means the computer has all day to figure out how to beat you.  Conversely, you have all day to sit around and watch yourself lose.  Fun, fun, fun.

Bottom line: I do not like to lose.  So this morning I decided to embark on a journey to a strange new world, to seek out new moves and new predictions, to boldly create an Othello core, like i've never done before!  Did you catch that Star Trek reference?  Of course you did... Nerd.

I decided to rapidly prototype Othello using Python.  The first thing I needed to do was create a board.  A list of lists struck me as the most obvious way to do this:

_black = 0
_white = 1
_board_size = 8

def get_new_board():

    board = []
    for i in xrange(_board_size):
        board.append([None] * _board_size)

    board[3][3] = board[4][4] = _white
    board[3][4] = board[4][3] = _black

    return board

Alright, so I have a board.  I think?  It would be nice if I could see it.

def display_board(board):

    for line in board:
        output_line = ''
        for item in line:
            if item == None:
                output_line += ' '
            else:
                output_line += str(item)
        print output_line

My first attempt at display_board felt awkward and dirty.  Using None as the default cell value turned out to be a bad decision, that made the display function a bit more cumbersome than I had hoped for.  If I forced the default cell value to 0 and made the player values 1 and 2, I could then simplify 'for item in line' and the print statement to a simple print using ''.join.  This means our initial code block will look like this:

_black = 1
_white = 2

...

board.append([0] * _board_size)

Let's see how this feels in terms of display_board:

def display_board(board):

    for line in board:
        print ''.join([str(i) for i in line])

That's a bit cleaner.  So what does the output look like?

00000000
00000000
00000000
00021000
00012000
00000000
00000000
00000000

It's not exactly a hypertexture enhanced scene but I can live with that.  I now have a board that I can see, wouldn't it be nice if I could place a piece on it?  Before I can place a piece for a particular player I will need to check if a single move is valid or get a list of all valid moves.  Because I have a personal interest in being able to enumerate over all possible moves for a particular player / board state, I will enumerate a list of all possible moves for a player.  I am not sure how all the internals will work right now but that's ok, let's just write it out.  We will generate a list of all possible positions, iterate over it and if a move is valid, store the coordinates somewhere:

def get_valid_moves(player, board):

    valid_moves = []

    # enumerate all possible positions
    positions = []
    for i in xrange(0, _board_size):
        for j in xrange(0, _board_size):
            positions.append((i,j))

    # for each position, if the move is valid, store it
    for position in positions:
        if move_is_valid(player, position, board):
            valid_moves.append(position)


It is a brute force approach and all of it should work once move_is_valid is implemented.  Our valid move coordinates are stored in the valid_moves list.  At a glance we can observe that the positions list is really unnecessary overhead, so let's get rid of it.

def get_valid_moves(player, board):

    valid_moves = []

    for i in xrange(0, _board_size):
        for j in xrange(0, _board_size):
            position = (i,j)
            if move_is_valid(player, position, board):
                valid_moves.append(position)

    return valid_moves

Now we need to implement move_is_valid.  I will assume that the reader is familiar with the game and understands what a valid is (don't you?).  Ultimately, we will need to check in 8 different directions from the target position to see if a single move is valid.  For this function, we will need to know which player is playing, the position of interest and the current board.  Let's stub this out:

def move_is_valid(player_piece, position, board):

    if board[position[1]][position[0]] != 2:
        return False

    if has_vertical_up_play(player_piece, position, board):
        return True

    if has_vertical_down_play(player_piece, position, board):
        return True

    if has_horizontal_left_play(player_piece, position, board):
        return True

    if has_horizontal_right_play(player_piece, position, board):
        return True

    if has_diagonal_up_left_play(player_piece, position, board):
        return True

    if has_diagonal_up_right_play(player_piece, position, board):
        return True

    if has_diagonal_down_left_play(player_piece, position, board):
        return True

    if has_diagonal_down_right_play(player_piece, position, board):
        return True

    return False

The first test verifies that the requested position on the board is empty.  The next statements will perform the individual horizontal, vertical and diagonal play tests.  The parameters to each of the has_..._play functions are reasonably redundant and they don't look pretty at all.  However, in this case I prefer to be explicit.  I could have easily created a tuple and stored all the parameters in it, like this:

parameters = (player_piece, position, board)

if has_vertical_up_play(parameters):
        return True

But I feel that would make the has_..._play calls more cryptic, because now you have to look back into the caller to see what was being packed into the list:

# I decided against this
def has_vertical_up_play(parameters):

    player_piece = parameters[0]
    position = parameters[1]
    board = parameters[2]

Now what will our has_vertical_up_play function look like?  This specific function is interested in examining the upwards path from the target position to determine if the play is valid.  We are only interested in walking upwards as long as we encounter opposing pieces.  If we found opposing pieces and did not run off the end of the board and the final piece was our own, then we found a valid play.

def has_vertical_up_play(player_piece, position, board):

    opposing_piece = _opposing_pieces[player_piece] 

    # consume all immediate opposing pieces
    # in the vertical up direction
    found_opposing_piece = False
    for i in xrange(position[1]-1, -1, -1):
        if board[i][position[0]] == opposing_piece:
            found_opposing_piece = True
        else:
            break

    if found_opposing_piece and i >= 0:
        if board[i][position[0]] == player_piece:
            return True 

    return False

Each of the has_..._play functions works in a similar fashion.  I finished implementing all of the has_..._play functions above, so we are ready to start dumping some output.  Let's take our initial board and determine the possible valid moves for white and black:

print 'creating new board'
board = get_new_board()

print 'displaying board'
display_board(board)

print 'valid white moves'
for valid_move in get_valid_moves(_white, board):
    print valid_move
    
print 'valid black moves'
for valid_move in get_valid_moves(_black, board):
    print valid_move

That was pretty easy! But what does our output look like?

creating new board
displaying board
00000000
00000000
00000000
00021000
00012000
00000000
00000000
00000000
valid white moves
(2, 4)
(3, 5)
(4, 2)
(5, 3)
valid black moves
(2, 3)
(3, 2)
(4, 5)
(5, 4)

Hooray, we are beginning to see things come together!  Now, it's time to make some moves.  For each of our has_..._play functions, let's write some new make_..._play functions that will modify the board.  Each of these functions will be expected to return the board that results from the move.  Here is an example of make_vertical_up_play:

def make_vertical_up_play(player_piece, position, board):

    opposing_piece = _opposing_pieces[player_piece] 

    for i in xrange(position[1]-1, -1, -1):
        if board[i][position[0]] == opposing_piece:
            board[i][position[0]] = player_piece
        else:
            break

    return board

For each of these functions, I assumed the play was valid (since I plan to check in advance).  Now we need to glue all of these make_..._play functions together.  I decided to do this in a generic move function.  After all, there should be one (and preferably only one) way to do things, right?

def move(player_piece, position, board):
    ''' Makes the move for the provided player at the provided position on the board.'''

    if has_vertical_up_play(player_piece, position, board):
        board = make_vertical_up_play(player_piece, position, board)

    if has_vertical_down_play(player_piece, position, board):
        board = make_vertical_down_play(player_piece, position, board)

    if has_horizontal_left_play(player_piece, position, board):
        board = make_horizontal_left_play(player_piece, position, board)

    if has_horizontal_right_play(player_piece, position, board):
        board = make_horizontal_right_play(player_piece, position, board)

    if has_diagonal_up_left_play(player_piece, position, board):
        board = make_diagonal_up_left_play(player_piece, position, board)

    if has_diagonal_up_right_play(player_piece, position, board):
        board = make_diagonal_up_right_play(player_piece, position, board)

    if has_diagonal_down_left_play(player_piece, position, board):
        board = make_diagonal_down_left_play(player_piece, position, board)

    if has_diagonal_down_right_play(player_piece, position, board):
        board = make_diagonal_down_right_play(player_piece, position, board)

    board[position[1]][position[0]] = player_piece

    return board

After I implemented each of these functions, it was time to have some fun!  How about enumerating all of the opening moves for black?

print 'creating new board'
board = get_new_board()

print 'displaying board'
display_board(board)

print 'displaying valid black moves'
for valid_move in get_valid_moves(_black, board):
    temp_board = copy.deepcopy(board)
    print 'making move: ', valid_move
    temp_board = move(_black, valid_move, temp_board)
    display_board(temp_board)

A deep copy is required because I am re-using the board between iterations.  During each iteration, I display the move being made and the resulting board.  Let's see what it looks like:

creating new board
displaying board
00000000
00000000
00000000
00021000
00012000
00000000
00000000
00000000
displaying valid black moves
making move:  (2, 3)
00000000
00000000
00010000
00011000
00012000
00000000
00000000
00000000
making move:  (3, 2)
00000000
00000000
00000000
00111000
00012000
00000000
00000000
00000000
making move:  (4, 5)
00000000
00000000
00000000
00021000
00011100
00000000
00000000
00000000
making move:  (5, 4)
00000000
00000000
00000000
00021000
00011000
00001000
00000000
00000000

Now, let's take it to the next level.  Why don't we give our second player a chance?  We will make the first valid black move and then the first valid white move.  Between each move we will display our board.

print 'creating new board'
board = get_new_board()

print 'displaying board'
display_board(board)

# get the first valid black move
black_move = get_valid_moves(_black, board)[0]
temp_board = copy.deepcopy(board)
print 'making black move: ', black_move
temp_board = move(_black, black_move, temp_board)
display_board(temp_board)

# get the first valid white move
white_move = get_valid_moves(_white, temp_board)[0]
print 'making white move: ', white_move
temp_board = move(_white, white_move, temp_board)
display_board(temp_board)

Let's take a look at the results.

creating new board
displaying board
00000000
00000000
00000000
00021000
00012000
00000000
00000000
00000000
making black move:  (2, 3)
00000000
00000000
00010000
00011000
00012000
00000000
00000000
00000000
making white move:  (2, 2)
00000000
00000000
00210000
00021000
00012000
00000000
00000000
00000000

There is still some work left todo in terms of getting an actual game going, but not much.  In part 2, we'll take a look at our first end-to-end game.