Use Python to retrieve and display images from space


Next, the job of the download function (lines 47-99) is to download everything needed to get the latest images, starting with the current time, a list of files, and the files themselves. Line 52 requests the time from the time server, and line 53 decodes it into local time and stores it in today. The root location of SDO images on the NASA server is organized into many subfolders on the basis of date; format in line 61 adds the day's date to the address. (See the "SDO File Structure and Naming" box for more information.)

SDO File Structure and Naming

SDO data is organized by date. Each year has subfolders for each month, which has subfolders for each day. The time each image was generated is encoded as part of the file name. Folders created all the way to 2017 are slowly populated with time.

The file name of each image is a large string of mostly numbers. The first four digits are the year followed by two for the month and two for the day; this is followed by an underscore and the time as two digits each for hour, minute, and second. After another underscore, the size of this image (from 512 to 4096 pixels) is included and, after a final underscore, the ID of the channel that this image is part of (Figure 4).

Figure 4: An image of the Sun provided by the Solar Dynamics Observatory. Courtesy of NASA/SDO and the AIA, EVE, and HMI science teams.

Line 63 defines filter, the channel ID, which is changed manually on each display. Lines 65-68 check the date, reset self.images and self.imageNames if it has changed, and set self.oldDate to today. The rest of the function deals with fetching the images provided in the directory listing. Apache (the web server) generates this list automatically; it's not an actual HTML file that exists on the server. Each file is listed as a row in a table, so I do a lot of string parsing to find the file name.

Line 77 splits the HTML every time it finds <tr>, so now I have a list of each table row in a file. Line 80 checks whether this line contains a link (an image). If it does, then line 81 finds the location of <a in the string and stores it in index.

On line 82, I use index: as a starting point. The colon says include the rest of the string rather than stop after a single character. Then I split the string on double quotes. That will return a list of sections, but here I just use it in place. The first string will be everything from index to the first double quote. The second string will just be the file name because it's terminated by another double quote. The third string is everything after the second double quote, so I request index [ 1 ] (remember that list indices start at [ 0 ]).

Line 83 checks for both filter (the channel I'm looking for) and "1024" (the size I want) in the file name. If both of those are present, then this is an image I want to download. Line 85 checks whether the file name is already in self.imageNames. If it is, then it has already been downloaded.

Line 87 initializes a square surface that matches my screen size. Line 89 downloads the sun image and uses StringIO to avoid having to write it to disk. Line 90 then loads the image with pygame.image.load. The "a.JPG" tells load to treat the image as a JPEG file, because as a file object, not a file, PyGame doesn't know what type of file it is.

Once I have the image, line 91 resizes it to match the screen size. Then, lines 92 and 93 add the image itself and its file name to self.images and self.imageNames that keep up with everything. The try/except block in lines 88-95 makes sure the program moves on to the next image if an image can't be downloaded or converted (Figure 5).

Figure 5: The downlink from the spacecraft doesn't always go perfectly. Incomplete frames or missing data sometimes show up in the downloaded images. Image courtesy of NASA/SDO and the AIA, EVE, and HMI science teams.


The slides function (lines 101-123) contains the PyGame event loop, which handles all of the timing of slides and downloads. The image currently being displayed is imgIndex, and a timer initiates a PyGame event every 100 milliseconds (0.1 seconds). Next, downloadCounter on line 104 determines when to attempt to fetch new images.

Starting on line 106, the main event loop processes all of the PyGame events. The most common will be pygame.USEREVENT, which is the 100ms timer. Line 115 checks to see if downloadCounter has reached 3000 (30 seconds) or if there aren't any images (len ( self.images ) == 0). If either of those conditions are true, the program displays the "please standby" graphic and calls to get new images. Lines 119-123 reset imgIndex and downloadCounter to 0 so everything starts fresh once the download finishes, look for a keypress to occur, and, if so, shut down PyGame (pygame.quit) and exit the main loop (break). The last four lines of code initialize pygame.display to start all of the graphics systems, set up the screen resolution, create an instance of the imageDownload class, and run everything.

The following scripts comprise an interactive version of the sun displays. In this case, interactive means being able to select any of the 18 channels the spacecraft generates for viewing.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Raspberry Pi Geek

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content