This project uses an e-ink triple colour display on a Raspberry Pi Zero, to show the current location of the International Space Station (ISS), programmed in Python

SpaceStation Tracker Raspberry Pi Zero E ink Display 

The Raspberry Pi computers have been used for many great educational projects. One of which was in 2016 when two Raspberry Pi's were sent into space to run experiments on the International Space Station while the UK astronaut Tim Peake was on board . These two Pi's of the Astro Pi's project, run code and experiments that schools, children and teenagers had written for the project.

In 2019 the Raspberry Pi Foundation and ESA have teamed up again to get more code for a future mission on the ISS. This a great opportunity for children and teenagers to get directly involved in space, science and programming in one project.

ISS Space

I'm interested in astronomy and thought it would be a fun project to build a ISS real time tracker using a Raspberry Pi Zero W and a Triple e-ink display.
The project is not only to show where the space station is but also it's a good geography learning project for kids. As they check in from time to time to see where space station is they get to learn where the Cities and Countries of the world are.

The display I have used is the Seeed Studio 2.13 inch Triple colour e-ink display. My guide to the screen and how to program it is here.

Information about the ISS's current location is available via a api from NASA. So you can get the Longitude and Latitude right now. Then using a list of the Longitude and Latitude locations of 1000's of Towns and Cities around the world supplied in a text file. A python program can find out what is the closest location on earth and the display it on the e-ink display.

E-ink displays only use power to change the image unlike LCD displays that need power at all times. So the Rpi Zero w and the e-ink display are both low powered devices meaning they can happily run on a battery for days if required.

The ISS location is available in a python library called starfield available in pip or via a few api's online. I choose an api at http://open-notify.org/Open-Notify-API/ISS-Location-Now/ which creates a json script that can be read in python.

Using the longitude and latitude co-ordinates in a list of co-ordinates for locations around the world the python script determines the closest location.
This could be over 1000 km away when the ISS is over an ocean.

The location is then plotted on a map of the world so you can see where it is.

As the e-ink display is not designed to updated constantly it refreshes every 3 minutes. The ISS takes about 90 minutes to complete one orbit so in 3 minutes it travels about 1,386 km or 864 miles. A trail of dots show the path it has taken across the world.

ISS Tracker Raspberry Pi e ink display

Python Programming Guide to Tracking the ISS

 To make a similar tracker the steps are:

enter all the libraries needed.

import json, urllib.request, time
from haversine import haversine, Unit
import csv
from PIL import Image, ImageDraw,ImageFont
import epd2in13b,traceback
  • The first row is required to capture the ISS's location
  • The second row is used to calculate this distance between two global locations
  • The third line is used to import the world location list
  • The forth row is a graphics library to create screen images
  • The fith is the e-ink screen driver

 This next bit of code is a function to connects to the website and requests the current location of the ISS and stores them in the lat and lon variables

def iss_long_lat():
    """Get the ISS data from the tracking API"""
    url = "http://api.open-notify.org/iss-now.json"
    details = urllib.request.urlopen(url)
    result = json.loads(details.read())
    loc =result["iss_position"]
    lat = loc["latitude"]
    lon = loc["longitude"]
    return lon,lat

The information returned looks like this

latitude "36.2418"
longitude "-125.1467"

Now we can get the current ISS location, next we want to compare it to a list of longitude and latitude locations around the world held in a csv file and load it to an list. This uses two functions, one to load the csv file into a list and one to find the closest destination.

def getGeoData():
    """Load city long lat list"""
    with open('./worldcities_lonlat.csv','r') as f:
        l = csv.reader(f)
        cities = list(l)
    return cities

The csv file is loaded and converted line by line into a list of four columns. Longitude, Latitude, Town/City, Country

11.0409   35.4839    Mahdia       Tunisia
11.04      12.8705    Gashua       Nigeria
11.03      50.9701    Erfurt          Germany
11.027    -2.857      Tchibanga    Gabon
11.0167  -1.8662     Mouila         Gabon
11          49.47        Furth          Germany
10.99     45.4404     Verona        Italy

 

def dist(start,end):
    x = haversine(start,end)
    return x

This function uses the haversine library which is used to calculate the distance between two positions on the earth. This is required for the next function. You will need to install the haversine python3 library via pip

sudo pip3 install haversine

def CalcLoc(data,sz,iss):
    """Go through city list to find the closest destination to the ISS"""
    country = ""
    city = ""
    citydist = 0
    mindist = 99999999
    for i in range(sz):
        d = dist((float(data[i][0]),float(data[i][1])),(float(iss[0]),float(iss[1])))
        if d < mindist:
            city = data[i][2]
            country = data[i][3]
            citydist = d
            mindist = d
    return (country,city,round(citydist,2))

 This will work out what is the closest location to the ISS's position. It takes three inputs

  • Data - the list of locations generated from the CSV file
  • sz - a count of the total rows in the Data list
  • iss - the current iss long lat locations

This function looks at each longitude and latitude of all the 12000+ locations in the list created from the csv file and compares that to the ISS's location using the 'dist' function.

if the returned distance is lower than the distance set in the variable 'mindist' then it records the 'country', 'city' and 'citydist' and the new 'mindist'

Once the whole list has been compared the final value for those varibles will be the closest destination to the ISS.

The line:  d = dist((float(data[i][0]),float(data[i][1])),(float(iss[0]),float(iss[1]))) contains the Long and Lat from the csv file and the long Lat from the current ISS location.

float(data[i][0]) = data list at [current row][column 0]

As both sets of data come from a text file they are represented as a string, so the float command converts them to a floating point number for use in the distance calculation.

So now we know the ISS's location and the closest destination with the distance in Km so the next step is to display this information on the e-ink display.

Mini Map ISS location

The Mini map on the display has a red dot to show the ISS's current location. For this to work the map has to be a certain size.

 The longitude and latitude lines split up the world in even sections. Longitude goes from the international date line in the Pacific Ocean near China at -180 to the UK at zero and back to the international date line at +180. That is a total of 360 degrees, not surprising being that the earth is round. The latitude lines go from the North pole to the South Pole from +90 to -90. A total of 180.

Earth Long Lat

If you think of each pixel in the map of the earth being a longitude and latitude line then you want an image that is 360 pixels by 180 pixels or a mutiplication of that size.

World Map 360 180 image

Longitude position:

What you want to do is, if you get a ISS location of 51 longitude and 0 latitude,  which is in Southern England, you then plot the dot at 55 pixels from the left and 23 pixels from the top and your done. Unfortunately not. The problem is the negative numbers of the longitude and latitude will mean your point will be in the wrong location. The dot will appear in the arctic above Alaska.

So for your longitude position you need to +180 to the ISS longitude value. This means your value will always be between 0 and 360 and the dot will appear in the correct left to right pixel on your image.

Latitude Position

For the Latitude this is a little more tricky. The same principle applies but as the range is -90 to +90 then you just add 90 to you latitude value.World Map latitude

But this will mean the south pole will have a value of zero and the North Pole a value of 180. The coordinates of the image are 0 at the top and 180 at the bottom. So your dot will appear on the wrong half of the world.

To flip it, just minus the value from 180. So with a latitude of -50,  + 90 to make it +40. Then 180-40 to make it 140. Your dot will now be in the bottom half of the picture 140 pixels from the top.

Get the right world image

The image is also very important. If you don't use a good image of the earth your countries will be in the wrong places for the pixel positions. Though these two maps of the world look similar. The first one will show the iss position in the wrong place as the countries are straighter. The second one will give the correct position and looks more stretch from left to right. If you look at South America, it is more tilted than the first image.

 world map badworld map good

The best place to get images is from Nasa. They are free and at the correct size to be scaled correctly. Such as this image https://visibleearth.nasa.gov/images/73963/bathymetry

For the e-ink display the map had to be scaled down to a 3rd of the size to 120 pixels by 60 pixels. So all longitude and latitude calculations need to be divided by 3 as well so the red dot can be located correctly.

 So if you followed that, then the function to position the red dot will make sense. Simple as that!

self[0] and self[1] are the longitude and latitude of the iss. Float converts a string to a floating point number.

def mapdot(self):
#position of red dot on mini map
#scale long lat coords to 120 x 60 image
x = round((float(self[0])+180)/3)
y = round((180-(float(self[1])+90))/3)
return [x,y]

Updating the e-ink display

 The next function is the biggest and uses the elments from the previous functions to build the image for the display. I will break this down into smaller section and explain what is happening.

 def updateeink(issdetails,mapdot,trail):
     """update location text and map to the e-ink screen"""
     issx = int(mapdot[0])
     issy = int(mapdot[1])
     epd = epd2in13b.EPD()
     epd.init() #Initiate e-ink screen ready for image update

This function receives the iss's locations (issdetails), the position of the red dot (mapdot) and trail is a list of previous locations the iss has been at.

#Create an image to show the black sections of the display
HBlackimage = Image.new('1', (epd2in13b.EPD_HEIGHT, epd2in13b.EPD_WIDTH), 255) # 212x104
#Create an image to show the red sections of the display
HRedimage = Image.new('1', (epd2in13b.EPD_HEIGHT, epd2in13b.EPD_WIDTH), 255) #212x104

 #assign the new black and red images to varibles
drawblack = ImageDraw.Draw(HBlackimage) drawred = ImageDraw.Draw(HRedimage)

#assign the fonts and sizes to variables
font14 = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf', 14)
font20 = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf', 20)

#show text at 130 pixels from the left of the screen and 0 & 18 pixels from the top

#using font20
drawblack.text((130,0), 'The ISS', font = font20, fill=0)

drawblack.text((130,18), 'is near:', font = font20,fill=0)

#This line shows the Country the ISS is nearest to

drawred.text((5,52), issdetails[0], font = fontsize(issdetails[0]), fill = 0)

#This line shows the closest City or Twon to the ISS

drawred.text((5,70), issdetails[1], font = fontsize(issdetails[1]), fill = 0)

#This line shows the distance between the ISS and the City or Town as if it was on the ground.

drawblack.text((5,88), 'about ' + str(round(issdetails[2])) + ' Km away', font = font14, fill = 0)

#Load the image of the world. this is 120 pixels by 60 pixels
mapblk = Image.open('small-world-map.bmp')
#Position the map on the black image
HBlackimage.paste(mapblk,(0,0))

#calculate the position for the red dot marking the ISS's location
isspos = (int(mapdot[0])-4,int(mapdot[1])-4,int(mapdot[0])+4,int(mapdot[1])+4)

#Draw the red dot on the Red image
drawred.ellipse(isspos,fill = 0)
#Draw the trail of the last 60 positions using the point command.
drawred.point(trail,fill = 0)

#Update Display with the black and red images
epd.display(epd.getbuffer(HBlackimage.rotate(180)), epd.getbuffer(HRedimage.rotate(180)))
#send the display to sleep
epd.sleep() #end update

 This section is the main program which calls the various functions to collect the required data and then finally sends them to the "updateink" function.

def main():
    geodata = getGeoData() #Global city Longitude & Latidude list
    geosz = len(geodata) #get total rows in list
    trail = []        #list to display trail
    while True:
        iss_lola = iss_long_lat() #Current Iss location
        loc = CalcLoc(geodata,geosz,iss_lola) #Closest City/Town
        mapxy = mapdot(iss_lola) #Red dot pixel position
        if len(trail) >= 60: #length of trail in points
            trail.pop(0)  #if trail list 60 elements long then remove the first one to add a new one on the end
        #add new red dot coords to trail list.
        trail.append((int(mapxy[0]),int(mapxy[1])))
        updateeink(loc,mapxy,trail)
        time.sleep(180) #wait 3 minutes

 The full program is available for download here

This version has a few extras; the map switches between the left and right side and the text size changes depending on the amount of letters being displayed. iss-tracker-e-ink-display

Complete archive of Seeed Studio modified e-ink drivers, python scripts, map image and world location list iss-tracker-eink.zip 

 

 The e-ink display is available from www.seeedstudio.com

Waveshare e-ink infor and drivers (need pin assignments modifying to use with Seeed Studio e-ink): https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT_B

Live ISS tracking API: http://open-notify.org/Open-Notify-API/ISS-Location-Now/

Live Online ISS Tracking: http://www.isstracker.com/

Python Haversine library: https://pypi.org/project/haversine/

 List of global cities and towns with coordinates https://simplemaps.com/data/world-cities

 

Also see my guide for this ISS Tracker on the Raspberry Pi Pico  Raspberry Pi Pico with WiFi used as an International Space Station Tracker (ISS) 

Seeed is the IoT hardware enabler providing services over 10 years that empower makers to realize their projects and products. Seeed offers a wide array of hardware platforms and sensor modules ready to be integrated with existing IoT platforms and one-stop PCB production and cheap pcb assembly. Seeed Studio provides a wide selection of electronic parts including ArduinoRaspberry Pi and many different development board platforms. Especially the Grove Sytsem help engineers and makers to avoid jumper wires problems. Seeed Studio has developed more than 280 Grove modules covering a wide range of applications that can fulfill a variety of needs. 

Add comment

Comments  
Could please tell me what is the world map projection you use (the second one) ?
Thanks
Hi testa

The second image map was created from the image in the link below it.
https://visibleearth.nasa.gov/images/73963/bathymetry
I used the the image that has dimensions 3600 x 1800 as the base image.

The zip file towards the bottom of the article contains the map used in the project.