+1 (315) 557-6473 

Telescope and Astronomy with Python Homework Solution


The Astronomy Web Store

Scenario

You are starting a new business on the web! BeginnerAstro.ca, a website dedicated to beginner astronomy, but also serving intermediate and advanced users, needs a “backend” design (that is, the logic that runs the website) that will allow a package to be built (that is, a combination of parts) by the customer, and ultimately, the system will check after each item is added to be sure that it is actually usable. No pesky returns from customers who bought the wrong thing!

Initially, you’re going to deal only with a generic telescope OTA (optical tube assembly) and EP (eyepiece). Later, we’ll get into the different types of telescopes, and add more accessories and options.

Class: OTA

This class will represent an Optical Tube Assembly, the body of the telescope, and the part that is the optical instrument.

An OTA has the following characteristics:

  • An aperture (the opening at the front of the telescope that points towards the object you want to view). It is given as a diameter, measured in millimeters (mm).
  • A focal length (the distance from where light enters the telescope, to where it reaches focus after passing through lenses or bouncing off of parabolic mirrors). This distance is also measured in millimeters.
  • A type. For our purposes, we will use a very limited subset of the wide range of types available. We will use letter codes for the type, as follows:

o G – Galilean (Refractor)

o N – Newtonian (Reflector)

o S – Schmidt-Cassegrain (Catadioptric)

o M – Maksutov-Cassegrain (Catadioptric)

You are given a data file, otas.txt, that contains records of the format:

type, aperture, focal length

Example:

G,70,350

So, the file will contain a number of lines, each one corresponding to a telescope design and parameters.

You will need to read this data, line by line, and for each line, create an instance of the OTA class, which you must design.

Your OTA class should have a constructor that accepts these three parameters and stores them. Additionally, it should have accessors and mutators for each of these parameters, so that they can be altered or updated if needed. Your OTA class should be stored in a file called OTA.py

Class: EP

This class will represent an eyepiece, a lens unit that is inserted into the viewport of an OTA, which allows the user to vary the field of view and effective magnification of what is being viewed. (Later on, we’ll learn how to make sure that an EP suits a particular OTA.)

An EP has the following characteristics:

  • An apparent field of view (a range of the sky that will appear in
  • A focal length (the distance from the focal point of the eyepiece to the lens assembly of the eyepiece), measured in millimeters. The operation of using a focuser on a telescope is to move either the eyepiece position or the lenses or mirrors of the telescope, to match the focal point of the telescope to the focal point of the eyepiece.
  • A type. For our purposes, we will again use a very limited subset of the wide range of types available. We will use letter codes for the type, as follows:

o K – Kellner (Achromat)

o P – Plössl (Symmetric)

o O – Orthoscopic (Abbe)

o N – Nagler (a complex, but excellent, multielement design)

You are given a data file, eps.txt, that contains records of the format:

type, afov, focal length

Example:

N,110,35

So, the file will contain a number of lines, each one corresponding to an eyepiece design and associated parameters.

You will need to read this data, line by line, and for each line, create an instance of the EP class, which you must design.

Your EP class should have a constructor that accepts these three parameters and stores them. Additionally, it should have accessors and mutators for each of these parameters, so that they can be altered or updated if needed. Your EP class should be stored in a file called EP.py.

Dunder Method: _str_

You will write a python assignment str dunder method in each of the preceding two classes, which will provide the data from that class in a readable format. For example:

OTA

           Type: Newtonian

           Aperture: 200mm

Focal Length: 1200mm

EP

Type: Plössl

AFOV: 60deg Focal Length: 25mm

Use the skills you learned in CS1910 to format the output so it is in a pleasing alignment, similar to what is shown.

Class: OpticalToolbox

We are also going to create another type of class which is known in many languages as a “toolbox” class – that is, it has a number of functions. These are often referred to in other languages as “static methods” – that is, methods that can be accessed without creating an instance of the class they are contained in. The static methods (i.e. functions) you will create will be called from other classes by using OpticalToolbox.function_name(parameters) – obviously, each function will have its own name. Typically, these toolbox functions return values to some other part of the program; they never store values internally (like classes do).

We need to implement the following functions in this toolbox:

  • effective_magnification(OTA, ep) – this function takes an OTA object, and an EP object, and computes and returns the effective magnification, which is found by dividing the focal length of the OTA by the focal length of the EP. This value should be rounded to one decimal place – but if that decimal place is 0, it should be omitted. (Your code must handle this.) Therefore, it might return an integer, or afloat. You, as the programmer, make this happen (this is CS1910 stuff).
  • f_ratio(OTA) – this function takes an OTA object and returns its f/ratio, which is computed by dividing its focal length by its aperture. This value should be also be rounded to one decimal place – but if that decimal place is 0, it should be omitted, as in the preceding function.
  • eyepiece_usability(OTA, ep) – this is related to the effective_magnification concept, and we are looking at the resolving power of the telescope in a given configuration. This procedure requires you to first compute the effective magnification (using the function you’ve already created as a helper function), then compare that value to double the aperture in mm of the OTA. For example, if an OTA and EP combination has an effective magnification of 180X, and the telescope’s aperture is 70mm, then 180 > 2*70, and thus the magnification is beyond the resolving power of the telescope (and that eyepiece is unusable with that telescope.) Only if the effective magnification is less than or equal to double the aperture in mm is the eyepiece usable with that telescope. Thus, this is a Boolean return value.

true_fov(OTA, ep) – the true field of view is found by first computing the effective magnification – then, dividing the eyepiece apparent field of view (AFOV) by this number to get the FOV (the true field of view of the system), which will be in degrees. This tells you the actual angle from a centreline that the telescope will be able to display in the sky (remember, the sky can be considered as three concentric circles, mutually orthogonal, providing a “spherical” view of the sky. The telescope’s axis would point to a location on this sphere, and the FOV would therefore describe an apparent circle of view at some arbitrary distance.) Again, the true FOV should be expressed to a maximum of 1 decimal place, as above.

Putting it all together – your “driver” or unit test

You will write a driver, which is the “main” code of your program. This will be responsible, in this assignment, for opening the files specified, and for making instances of the objects in the files, which will be stored in list structures.

The starting lines of each file are comments – each starts with #, and allows us to show a user what the file format is or explain encodings. You should read and discard any line that starts with # as its first character. For example, for our otas.txt file:

# FORMAT:

# type, aperture, focal length

# where: type is G (Galilean), N (Newtonian),

# S (Schmidt-Cassegrain), and M (Maksutov-Cassegrain) # and aperture and focal length are given in millimeters (mm) G, 70, 350

.

.

.

Read the data from the file, as specified, and generate a list of OTA objects from it. Do the same for the eyepieces.

Program Output

First, print out the two lists (via the dunder method str )

(For each part, use appropriate “titles” so that people know what you’re printing.)

Second, use this set of tuples that specify OTA and EP combinations, by the index within each file.

(0,2),(2,18),(2,19),(0,5),(3,0),(7,18),(3,16),(0,12)

Using this list, in the order specified, print out the following:

  • effective magnification of the system
  • f-ratio of the OTA
  • the true FOV of the system

Third, check all OTA/EP combinations that are UNUSABLE. To do this, take an OTA, and check every eyepiece with it for usability. Do this for every OTA on the list. When you find an unusable eyepiece, add its focal length to a list. Sort the list using the built-in list sort function – for some lists, you use lst. sort(). Print the list (no fancy format required). This would be used to advise sales staff to watch out when someone purchases one of these eyepieces, to check what telescope they’re using it with because it could create an unusable configuration.

Thus, your printout would look like the following pages (just showing the first entry from each of these three categories, of course – you have to show them all). I’ve added color to distinguish the two sections of the first part, the second part, and the third part – you won’t have any color in your printout, of course. Your printout should resemble this, but it does not have to replicate it down to the character.

Telescope 1

Telescope 2

Solution:

OTA:

# This class will represent an Optical Tube Assembly, # the body of the telescope and the part that # is the optical instrument. class OTA: # The opening at the front of the telescope that points towards the object you want to view _aperture: int # The distance from where light enters the telescope, to where it reaches focus # after passing through lenses, or bouncing off of parabolic mirrors _focal_length: int # Type of Lens _lens_type: str # Dictionary for holding these codes and text explanations _type_dict = dict(G='Gallilean (Refractor)', N='Newtonian (Reflector)', S='Schmidt-Cassegrain (Catadioptric)', M='Maksutov-Cassegrain (Catadioptric)') def __init__(self, lens_type: str, aperture: int, focal_length: int): self._aperture = aperture self._focal_length = focal_length self._lens_type = lens_type def __str__(self): return "OTA\n" + \ " Type: " + self.get_lens_type() + "\n" + \ " Aperture: " + str(self.get_aperture()) + "mm\n" + \ " Focal Length: " + str(self.get_focal_length()) + "mm\n" def get_aperture(self) -> int: return self._aperture def set_aperture(self, aperture: int) -> None: self._aperture = aperture def get_focal_length(self) -> int: return self._focal_length def set_focal_length(self, focal_length: int) -> None: self._focal_length = focal_length def get_lens_type(self) -> str: return self._type_dict[self._lens_type] def set_lens_type(self, lens_type: str) -> None: self._lens_type = lens_type

EP:

# This class will represent an eyepiece, a lens unit that is inserted into the viewport of an OTA, and # which allows the user to vary the field of view and effective magnification of what is being viewed. class EP: # A range of the sky that will appear in an Eyepiece _afov: int # The distance from the focal point of the eyepiece to the lens assembly of the eyepiece), measured in millimetres _focal_length: int # Type of Eyepiece _ep_type: str # Dictionary for holding these codes and text explanations _type_dict = dict(K='Kellner', P='Plössl', O='Orthoscopic', N='Nagler') def __init__(self, ep_type: str, afov: int, focal_length: int): self._afov = afov self._focal_length = focal_length self._ep_type = ep_type def __str__(self): return "EP\n" + \ " Type: " + self.get_ep_type() + "\n" + \ " AFOV: " + str(self.get_afov()) + "deg\n" + \ " Focal Length: " + str(self.get_focal_length()) + "mm\n" def get_afov(self) -> int: return self._afov def set_afov(self, afov: int) -> None: self._afov = afov def get_focal_length(self) -> int: return self._focal_length def set_focal_length(self, focal_length: int) -> None: self._focal_length = focal_length def get_ep_type(self) -> str: return self._type_dict[self._ep_type] def set_ep_type(self, ep_type: str) -> None: self._ep_type = ep_type

Optical toolbox:

import OTA import EP class OpticalToolbox: @staticmethod def __round_max_one_dp(val: float): result = round(val, 1) if (result * 10) % 10 == 0: return int(result) else: return result # This function takes an OTA object, and an EP object, and computes and returns the effective magnification, # which is found by dividing the focal length of the OTA by the focal length of the EP. @staticmethod def effective_magnification(ota: OTA, ep: EP): return OpticalToolbox.__round_max_one_dp(int(ota.get_focal_length()) / int(ep.get_focal_length())) # This function takes an OTA object and returns its f/ratio, # which is computed by dividing its focal length by its aperture. @staticmethod def f_ratio(ota: OTA): return OpticalToolbox.__round_max_one_dp(int(ota.get_focal_length()) / int(ota.get_aperture())) # This is related to the effective_magnification concept, and # we are looking at the resolving power of the telescope in a given configuration @staticmethod def eyepiece_usability(ota: OTA, ep: EP) -> bool: return OpticalToolbox.effective_magnification(ota, ep) <= (2 * ota.get_aperture()) # The true field of view is found by first computing the effective magnification # then, divide the eyepiece apparent field of view (AFOV) by this number to get # the FOV (the true field of view of the system), which will be in degrees. @staticmethod def true_fov(ota: OTA, ep: EP): return OpticalToolbox.__round_max_one_dp(int(ep.get_afov()) / int(OpticalToolbox.effective_magnification(ota, ep)))

Driver:

from OpticalToolbox import OpticalToolbox from OTA import OTA from EP import EP def task_1(list_otas, list_eps): print("List of OTAs Available:") print("=======================") for i in list_otas: print(i) print("List of EPs Available:") print("=======================") for i in list_eps: print(i) def task_2(list_otas, list_eps, tuples): print("Displaying selected combinations:") print("=================================") print("") for i, j in tuples: print("***SYSTEM***") print("============") ota = list_otas[i] ep = list_eps[j] print(ota) print(ep) print(f"Eff Mag : {OpticalToolbox.effective_magnification(ota, ep)}") print(f"F-ratio : f / {OpticalToolbox.f_ratio(ota)}") print(f"True FOV: {OpticalToolbox.true_fov(ota, ep)} deg") print("") def task_3(list_otas, list_eps): print("Problematic Eyepiece Focal Lengths:") print("===================================") lst = [] for x in list_otas: for y in list_eps: if not OpticalToolbox.eyepiece_usability(x, y): z = y.get_focal_length() if z not in lst: lst.append(z) lst.sort() print(f"Focal Length List (mm): {lst}") def read_data(): file_otas = open("otas.txt", "r") file_eps = open("eps.txt", "r") list_otas = [] list_eps = [] for line in file_otas.readlines(): if line[0] == '#': continue params = line.strip('\r\n').split(',') list_otas.append(OTA(params[0], int(params[1]), int(params[2]))) for line in file_eps.readlines(): if line[0] == '#': continue params = line.strip('\r\n').split(',') list_eps.append(EP(params[0], int(params[1]), int(params[2]))) file_otas.close() file_eps.close() return list_eps, list_otas def main(): list_eps, list_otas = read_data() # Task 1: Print out the two lists task_1(list_otas, list_eps) # Task 2: Use the given set of tuples which specify OTA and EP combinations, by index within each file. tuples = [(0, 2), (2, 18), (2, 19), (0, 5), (3, 0), (7, 18), (3, 16), (0, 12)] task_2(list_otas, list_eps, tuples) # Task 3: Check all OTA/EP combinations that are UNUSABLE. task_3(list_otas, list_eps) if __name__ == '__main__': main()