Minimal working setup for pyobs inside a Jupyter notebook

Introduction

This is a short introduction to using pyobs inside a Jupyter notebook. If you are already familiar with pyobs, you will find that the only big difference is the setup process and how you interact with and manage your own module. If you are new to pyobs, this notebook should give you a good first introduction for writing code which interacts with pyobs.

Setup

Preparations

The following code (and pyobs in general) depends on the asyncio event loop running. If the following code returns False you will need to upgrade your Jupyter version, as older versions don’t support asyncio.

import asyncio
asyncio.get_event_loop().is_running()

Before we start with the actual setup, we have to redirect the loggin output to stdout, so that is displayed inside the cell output. If you want to disable error/warning messages, you can skip this step.

import logging
import sys
logging.basicConfig(stream=sys.stdout)

Credentials

This is not specific to pyobs itself, but is general advice: NEVER commit credentials to a git repository. In the case of the normal pyobs yaml configuration files pyobs allows other config files config files to be imported, in which the credentials can be stored, while the config file containing the credentials can be added to the .gitignore file. In the case of Jupyter notebooks I recommend using .env files, which can be simmilarly excluded from the repo by adding them to the .gitignore file. This also allow you to share the credentials between multiple notebooks in the same probject.

For this to work, simply add a .env file to your project root and add the following lines:

COMM_JID = "[USERNAME]@iag50srv.astro.physik.uni-goettingen.de"
COMM_PWD = "[PASSWORD]"

You want to replace the username and password with your credentials and if you are not at the IAG, replace the address after the @ with the one of your institute. After adding this, the load_dotenv() below, shoud return True

import os
from dotenv import load_dotenv

load_dotenv()
COMM_JID = os.getenv('COMM_JID')
COMM_PWD = os.getenv('COMM_PWD')

Comm

For our pyobs module to work, it needs a valid comm module to communicate with other modules (telescopes, cameras, etc.). In a production environment, XMPP is used for this communication, so a XmppComm module is created with the credentials, that where previously loaded.

from pyobs.comm.xmpp import XmppComm
comm = XmppComm(jid=COMM_JID, password=COMM_PWD, use_tls=True)

Open Comms

Open a channel, lieutenant.

By opening the comm module, we connect it to the pyobs network, so that we can communicate with the other modules on the network.

await comm.open()

If everything works and if other modules are connected to the network, the following command should return the names of these modules:

comm.clients

Closing Comms

At the end of a session, the comm module should be closed again. This signals to the rest of the network, that the module is not longer available.

await comm.close()

Virtual File System (VFS)

If you want to work with a camera, the module also needs access to the pyobs virtual filesystem. Again if you are not at the IAG, you will need to replace the download address below, with your own address.

from pyobs.vfs import VirtualFileSystem
vfs = VirtualFileSystem(
    roots={
        "cache":
        {
            "class": "pyobs.vfs.HttpFile",
            "download": "https://iag50srv.astro.physik.uni-goettingen.de/pyobs/filecache/"
    }})

Usage

Telescope

The module can now be used to control other modules on the network. First we create a proxy object for a telescope. The proxy object is a local representation of the remote module, but can be controlled using its usual methods. The proxy method needs the “username” of the module which it is proxying, in this case, the name of the telescope.

from pyobs.interfaces import ITelescope

TELESCOPE_NAME = "telescope"
telescope = await comm.proxy(TELESCOPE_NAME, ITelescope)

The proxy telescope then can be used to get the orientation of the telescope…

await telescope.get_radec(), await telescope.get_altaz()

and to move it in altaz coordinates…

await telescope.move_altaz(alt=60, az=180)

or radec coordiantes (both in degrees).

await telescope.move_radec(ra=60, dec=25)

Camera

A camera can be used in the same way, as a telescope. First, we create a proxy for a module with the “username” "sbig6303e" as the camera.

from pyobs.interfaces import ICamera

CAMERA_NAME = "sbig6303e"
camera = await comm.proxy(CAMERA_NAME, ICamera)

With the proxy object, we then can set the exposure time and image type.

from pyobs.interfaces import IExposureTime
from pyobs.interfaces import IImageType
from pyobs.utils.enums import ImageType

if isinstance(camera, IExposureTime):
    await camera.set_exposure_time(2)

if isinstance(camera, IImageType):
    await camera.set_image_type(ImageType.OBJECT)

grab_data then starts the exposure and returns the path to the image in the virtual filesystem. This path is then supplied to the vfs module to retrieve the image.

image_name = await camera.grab_data(broadcast=False)
img = await vfs.read_image(image_name)

Now we can look at the header…

img.header

and at the image itself.

import matplotlib.pyplot as plt

plt.imshow(img.data, cmap="gray")
plt.show()

We can also save the image as a file.

img.writeto("image_test.fits")