camrasdevices.py 4.69 KB
Newer Older
1
2
3
4
5
"""HPIB wrappers for devices in the Dwingeloo telescope

This module contains some classes for conveniently working with the devices in the Dwingeloo
Telescope that communicate over GPIB (General Purpuse Interface Bus) or HPIB (presumably the
HP variant of GPIB). Currently, three of these devices exist, on different HPIB addresses:
6
7
8
9

- Receiver (address 1)
- Local Oscillator (address 28), used for mixing signals to downconvert them
- Clock Generator (address 14), used as the clock for the FPGA
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

For each of the three devices a class exists. Setting and getting the frequency of these
devices is done by using their `frequency` attribute.

Example:
    >>> import camrasdevices
    >>> lo = LocalOscillator()
    >>> lo.frequency
    <Quantity 1.0 GHz>
    >>> lo.frequency = 1.1e9
    >>> lo.frequency
    <Quantity 1.1 GHz>

By default, the setter method of `frequency` checks if setting the device succeeded. This is a
blocking operation, but the HPIB bus is quite fast.
25

26
27
28
    >>> lo.frequency = 1.e18
    Traceback (most recent call last):
    RuntimeError: Setting frequency failed: tried to set to 1e+18 Hz, it is now 1100000000.0 Hz
29

30
This checking can be disabled:
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
    >>> non_checking_lo = camrasdevices.LocalOscillator(check_frequency = False)
    >>> non_checking_lo.frequency = 1.e18
    >>> non_checking_lo.frequency
    <Quantity 1.1 GHz>

The CamrasHpibDevice class spawns one thread (shared with all instances) to perform the HPIB
commands. This is to make sure that instances living in separate threads do not interfere with
each other. The thread is a daemon, so it will be killed on program exit.

On initialization of the first CamrasDevice (or subclass), the command thread is initialized, and
some initialization commands are sent to the HPIB bus to tell it to use the appropriate line
endings etc.
"""

46
47
import astropy.units as u
from astropy.units import Quantity
48

49
50
import hpib
import serial.threaded
51

52
53
54

class CamrasHpibDevice(object):
    """Wrapper around HPIB commands"""
55

Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
56
57
    command_thread = None

58
    def __init__(self, address, check_frequency=True):
59
        self.hpib_address = address
60
        self._check_frequency = check_frequency
61

Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
62
        if not CamrasHpibDevice.command_thread:
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
63
            serial_port = serial.Serial("/dev/ttyUSB0")
64
65
            CamrasHpibDevice.command_thread = serial.threaded.ReaderThread(serial_port,
                                                                           hpib.GPIBProtocol)
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
66
            CamrasHpibDevice.command_thread.start()
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
67
            CamrasHpibDevice.command_thread._connection_made.wait()
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
68
            CamrasHpibDevice.command_thread.protocol.init_hpib()
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
69

70
71
    @property
    def frequency(self):
72
73
74
75
76
77
78
79
80
81
82
83
84
        """Frequency of the device as an astropy Quantity.

        Returns:
            Quantity: frequency (in Hz) of the device

        Raises:
            RuntimeError: If the device does not respond
        """
        freq_str = CamrasHpibDevice.command_thread.protocol.command("freq?",
                                                                    address=self.hpib_address)
        if not freq_str or len(freq_str) == 0:
            raise RuntimeError("Camras device at address {} is not responding".format(
                self.hpib_address))
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
85
        return int(float(freq_str)) * u.Hz
86
87
88

    @frequency.setter
    def frequency(self, freq):
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
89
90
91
92
        """Set the device to the specified frequency. Throws a RuntimeError if failed

        Args:
            freq (Union[float, Quantity]): new frequency. If no unit present, assume Hz
93
94
95
96

        Raises:
            RuntimeError: If the device sticks to a different frequency (e.g. if the specified
                frequency is outside the accepted range for the device)
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
97
        """
98
99
        if isinstance(freq, Quantity):
            freq = freq.to(u.Hz).value
100
101
        CamrasHpibDevice.command_thread.protocol.command("freq {:d} Hz".format(int(freq)),
                                                         address=self.hpib_address)
102
        new_freq = self.frequency
103
        if self._check_frequency and new_freq.to(u.Hz).value != int(freq):
104
            raise RuntimeError("Setting frequency failed: tried to set to {}, it is now {}".format(
105
106
                freq, new_freq))

107
108
109
110

class Receiver(CamrasHpibDevice):
    """Wrapper around HPIB commands for the Rohde & Schwartz receiver"""

111
    def __init__(self, **kwargs):
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
112
        super(Receiver, self).__init__(1)
113

114

115
class LocalOscillator(CamrasHpibDevice):
116
117
    """Wrapper around HPIB commands for the local oscillator"""

118
    def __init__(self, **kwargs):
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
119
        super(LocalOscillator, self).__init__(28)
120

121

122
123
class ClockGenerator(CamrasHpibDevice):
    """Wrapper around HPIB commands for the clock generator (should be at 140MHz)"""
124

125
    def __init__(self, **kwargs):
Tammo Jan Dijkema's avatar
Tammo Jan Dijkema committed
126
        super(ClockGenerator, self).__init__(14)