4
4
--------
5
5
>>> from pslab import PowerSupply
6
6
>>> ps = PowerSupply()
7
- >>> ps.pv1.voltage = 4.5
8
- >>> ps.pv1.voltage
7
+ >>> ps.pv1 = 4.5
8
+ >>> ps.pv1
9
9
4.499389499389499
10
10
11
- >>> ps.pcs.current = 2e-3
12
- >>> ps.pcs.current
11
+ >>> ps.pcs = 2e-3
12
+ >>> ps.pcs
13
13
0.00200014652014652
14
14
"""
15
15
import numpy as np
@@ -41,31 +41,38 @@ def __init__(self, device: SerialHandler = None):
41
41
self ._pv1 = VoltageSource (self ._mcp4728 , "PV1" )
42
42
self ._pv2 = VoltageSource (self ._mcp4728 , "PV2" )
43
43
self ._pv3 = VoltageSource (self ._mcp4728 , "PV3" )
44
- self ._voltage_sources = {
45
- "PV1" : self ._pv1 ,
46
- "PV2" : self ._pv2 ,
47
- "PV3" : self ._pv3 ,
48
- }
49
44
self ._pcs = CurrentSource (self ._mcp4728 )
50
45
51
46
@property
52
47
def pv1 (self ):
53
- """VoltageSource: Control the voltage on PV1 between -5 V and 5 V."""
54
- return self ._pv1
48
+ """float: Voltage on PV1; range [-5, 5] V."""
49
+ return self ._pv1 .voltage
50
+
51
+ @pv1 .setter
52
+ def pv1 (self , value : float ):
53
+ self ._pv1 .voltage = value
55
54
56
55
@property
57
56
def pv2 (self ):
58
- """VoltageSource: Control the voltage on PV2 between -3.3 V and 3.3 V."""
59
- return self ._pv2
57
+ """float: Voltage on PV2; range [-3.3, 3.3] V."""
58
+ return self ._pv2 .voltage
59
+
60
+ @pv2 .setter
61
+ def pv2 (self , value : float ):
62
+ self ._pv2 .voltage = value
60
63
61
64
@property
62
65
def pv3 (self ):
63
- """VoltageSource: Control the voltage on PV3 between 0 V and 3.3 V."""
64
- return self ._pv3
66
+ """float: Voltage on PV3; range [0, 3.3] V."""
67
+ return self ._pv3 .voltage
68
+
69
+ @pv3 .setter
70
+ def pv3 (self , value : float ):
71
+ self ._pv3 .voltage = value
65
72
66
73
@property
67
74
def pcs (self ):
68
- """CurrentSource: Control the current on PCS between 0 and 3.3 mA .
75
+ """float: Current on PCS; range [0, 3.3e-3] A .
69
76
70
77
Notes
71
78
-----
@@ -76,7 +83,7 @@ def pcs(self):
76
83
77
84
For example, the maximum current that can be driven across a 100 Ω load
78
85
is 3.3 V / 1.1 kΩ = 3 mA. If the load is 10 kΩ, the maximum current is
79
- only 3.3 V / 11 kΩ = 300µA .
86
+ only 3.3 V / 11 kΩ = 300 µA .
80
87
81
88
Be careful to not set a current higher than available for a given load.
82
89
If a current greater than the maximum for a certain load is requested,
@@ -85,56 +92,86 @@ def pcs(self):
85
92
current will be only a few hundred µA instead of the maximum available
86
93
1.65 mA.
87
94
"""
88
- return self ._pcs
95
+ return self ._pcs .current
96
+
97
+ @pcs .setter
98
+ def pcs (self , value : float ):
99
+ self ._pcs .current = value
89
100
90
101
@property
91
102
def _registers (self ):
92
103
"""Return the contents of the MCP4728's input registers and EEPROM."""
93
104
return self ._mcp4728 .read (24 )
94
105
95
106
96
- class _Source :
97
- RANGE = {
107
+ class Source :
108
+ """Base class for voltage/current/power sources."""
109
+
110
+ _RANGE = {
98
111
"PV1" : (- 5 , 5 ),
99
112
"PV2" : (- 3.3 , 3.3 ),
100
113
"PV3" : (0 , 3.3 ),
101
114
"PCS" : (3.3e-3 , 0 ),
102
115
}
103
- CHANNEL_NUMBER = {
116
+ _CHANNEL_NUMBER = {
104
117
"PV1" : 3 ,
105
118
"PV2" : 2 ,
106
119
"PV3" : 1 ,
107
120
"PCS" : 0 ,
108
121
}
109
- RESOLUTION = 2 ** 12 - 1
110
- MULTI_WRITE = 0b01000000
122
+ _RESOLUTION = 2 ** 12 - 1
123
+ _MULTI_WRITE = 0b01000000
111
124
112
125
def __init__ (self , mcp4728 : I2CSlave , name : str ):
113
126
self ._mcp4728 = mcp4728
114
127
self .name = name
115
- self .channel_number = self .CHANNEL_NUMBER [self .name ]
116
- slope = self .RANGE [self .name ][1 ] - self .RANGE [self .name ][0 ]
117
- intercept = self .RANGE [self .name ][0 ]
128
+ self .channel_number = self ._CHANNEL_NUMBER [self .name ]
129
+ slope = self ._RANGE [self .name ][1 ] - self ._RANGE [self .name ][0 ]
130
+ intercept = self ._RANGE [self .name ][0 ]
118
131
self ._unscale = np .poly1d (
119
- [self .RESOLUTION / slope , - self .RESOLUTION * intercept / slope ]
132
+ [self ._RESOLUTION / slope , - self ._RESOLUTION * intercept / slope ]
120
133
)
121
- self ._scale = np .poly1d ([slope / self .RESOLUTION , intercept ])
134
+ self ._scale = np .poly1d ([slope / self ._RESOLUTION , intercept ])
135
+
136
+ def unscale (self , voltage : float ) -> int :
137
+ """Return integer representation of a voltage.
122
138
123
- def unscale (self , current : float ):
124
- return int (round (self ._unscale (current )))
139
+ Parameters
140
+ ----------
141
+ voltage : float
142
+ Voltage in Volt.
125
143
126
- def scale (self , raw : int ):
144
+ Returns
145
+ -------
146
+ raw : int
147
+ Integer represention of the voltage.
148
+ """
149
+ return int (round (self ._unscale (voltage )))
150
+
151
+ def scale (self , raw : int ) -> float :
152
+ """Convert an integer value to a voltage value.
153
+
154
+ Parameters
155
+ ----------
156
+ raw : int
157
+ Integer representation of a voltage value.
158
+
159
+ Returns
160
+ -------
161
+ voltage : float
162
+ Voltage in Volt.
163
+ """
127
164
return self ._scale (raw )
128
165
129
166
def _multi_write (self , raw : int ):
130
167
channel_select = self .channel_number << 1
131
- command_byte = self .MULTI_WRITE | channel_select
168
+ command_byte = self ._MULTI_WRITE | channel_select
132
169
data_byte1 = (raw >> 8 ) & 0x0F
133
170
data_byte2 = raw & 0xFF
134
171
self ._mcp4728 .write ([data_byte1 , data_byte2 ], register_address = command_byte )
135
172
136
173
137
- class VoltageSource (_Source ):
174
+ class VoltageSource (Source ):
138
175
"""Helper class for interfacing with PV1, PV2, and PV3."""
139
176
140
177
def __init__ (self , mcp4728 : I2CSlave , name : str ):
@@ -152,12 +189,12 @@ def voltage(self):
152
189
@voltage .setter
153
190
def voltage (self , value : float ):
154
191
raw = self .unscale (value )
155
- raw = int (np .clip (raw , 0 , self .RESOLUTION ))
192
+ raw = int (np .clip (raw , 0 , self ._RESOLUTION ))
156
193
self ._multi_write (raw )
157
194
self ._voltage = self .scale (raw )
158
195
159
196
160
- class CurrentSource (_Source ):
197
+ class CurrentSource (Source ):
161
198
"""Helper class for interfacing with PCS."""
162
199
163
200
def __init__ (self , mcp4728 : I2CSlave ):
@@ -175,6 +212,6 @@ def current(self):
175
212
@current .setter
176
213
def current (self , value : float ):
177
214
raw = 0 if value == 0 else self .unscale (value )
178
- raw = int (np .clip (raw , 0 , self .RESOLUTION ))
215
+ raw = int (np .clip (raw , 0 , self ._RESOLUTION ))
179
216
self ._multi_write (raw )
180
217
self ._current = self .scale (raw )
0 commit comments