This repository has been archived by the owner on Feb 4, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
eps.py
251 lines (230 loc) · 14.8 KB
/
eps.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
from smbus2 import SMBus
import time
import math
class EPS:
"""
Class for EPS
"""
def __init__(self):
self.bus = SMBus(1)
self.addr = 0x2b
self.bitsToTelem = [None, ("VSW1", "ISW1"), ("VSW2", "ISW2"), ("VSW3", "ISW3"), ("VSW4", "ISW4"),
("VSW5", "ISW5"), ("VSW6", "ISW6"), ("VSW7", "ISW7"), ("VSW8", "ISW8"), ("VSW9", "ISW9"),
("VSW10", "ISW10")]
# Refer to EPS manual pages 40-50 for info on EPS commands
# Format: self.eps.commands["COMMAND"](ARGS)
self.commands = {
# Board info commands: Basic board info
"Board Status": lambda: self.request(0x01, [0x00], 2),
# Reads and returns board status
"Last Error": lambda: self.request(0x03, [0x00], 2), # Reads and returns last error
"Firmware Version": lambda: self.request(0x04, [0x00], 2),
# Reads and returns firmware version
"Checksum": lambda: self.request(0x05, [0x00], 2),
# Reads and returns generated checksum of ROM contents
"Firmware Revision": lambda: self.request(0x06, [0x00], 2),
# Reads and returns firmware revision number
# Watchdog commands: Watchdog will reset the EPS after a period of time (default 4 minutes)
# with no commands received.
"Watchdog Period": lambda: self.request(0x20, [0x00], 2),
# Reads and returns current watchdog period
"Reset Watchdog": lambda: self.command(0x22, [0x00]), # Resets communications watchdog timer
# Any command will reset the timer, this command can be used if no action from the EPS is needed
"Set Watchdog Period": lambda period: self.command(0x21, period),
# Sets communications timeout watchdog period, minimum 1 minute maximum 90 minutes
# Reset count commands: EPS will be reset under various conditions,
# these functions check how many times have been caused by each condition.
# Counts roll over from 255 to 0.
"Brownout Resets": lambda: self.request(0x31, [0x00], 2),
# Reads and returns number of brownout resets
"Software Resets": lambda: self.request(0x32, [0x00], 2),
# Reads and returns number of software resets
"Manual Resets": lambda: self.request(0x33, [0x00], 2),
# Reads and returns number of manual resets
"Watchdog Resets": lambda: self.request(0x34, [0x00], 2),
# Reads and returns number of watchdog resets
# PDM Control: Get information about PDMs and switch PDMs on and off to power on or off components
"All Actual States": lambda: self.request(0x42, [0x00], 4),
# Reads and returns actual state of all PDMs in byte form
# PDMs may be shut off due to protections, and this command shows the actual state of all PDMs
"All Expected States": lambda: self.request(0x43, [0x00], 4),
# Reads and returns expected state of all PDMs in byte form
# These depend on whether they have been commanded on or off, regardless of protection trips
"All Initial States": lambda: self.request(0x44, [0x00], 4),
# Reads and returns initial states of all PDMs in byte form
# These are the states the PDMs will be in after a reset
"Pin Actual State": lambda component: self.request(0x54, [component], 2)[1],
# Reads and returns actual state of one PDM
"All On": lambda: self.command(0x40, [0x00]), # Turn all PDMs on
"All Off": lambda: self.command(0x41, [0x00]), # Turn all PDMs off
"Set All Initial": lambda: self.command(0x45, [0x00]), # Set all PDMs to their initial state
"Pin On": lambda component: self.command(0x50, [component]), # Enable component
"Pin Off": lambda component: self.command(0x51, [component]), # Disable component
"Pin Init On": lambda component: self.command(0x52, [component]),
# Set initial state of component to "on"
"Pin Init Off": lambda component: self.command(0x53, [component]),
# Set initial state of component to "off"
# PDM Timers: When enabled with timer restrictions, a PDM will remain on for only a set period of time.
# By default each PDM does not have restrictions
"PDM Timer Limit": lambda component: self.request(0x61, [component], 2),
# Reads and returns timer limit for given PDM
"PDM Timer Value": lambda component: self.request(0x62, [component], 2),
# Reads and returns passed time since PDM timer was enabled
"Set Timer Limit": lambda period, component: self.command(0x60, [period[0], [component][0]]),
# Sets timer limit for given PDM
# PCM bus control
"Bus Reset": lambda pcm: self.command(0x70, [sum([self.pcm_busses[i][0] for i in pcm])]),
# Manual reset
"Manual Reset": lambda: self.command(0x80, [0x00]),
# Manually resets EPS to initial state, and increments manual reset counter
}
# Format: self.eps.telemetry["REQUESTED TELEMETRY"]()
# Refer to EPS Manual Table 11.8-10
self.telemetry = {
"IBCROUT": lambda: self.telemetry_request([0xE2, 0x84], 14.662757), # BCR Output current in mA
"VBCROUT": lambda: self.telemetry_request([0xE2, 0x80], 0.008993157), # BCR Output voltage in V
"I3V3DRW": lambda: self.telemetry_request([0xE2, 0x05], 0.001327547), # 3V3 Current draw of EPS in A
"I5VDRW": lambda: self.telemetry_request([0xE2, 0x15], 0.001327547), # 5V Current draw of EPS in A
"I12VBUS": lambda: self.telemetry_request([0xE2, 0x34], 0.00207), # 12V Bus output current in A
"V12VBUS": lambda: self.telemetry_request([0xE2, 0x30], 0.01349), # 12V Bus output voltage in V
"IBATBUS": lambda: self.telemetry_request([0xE2, 0x24], 0.005237), # Batt Bus output current in A
"VBATBUS": lambda: self.telemetry_request([0xE2, 0x20], 0.008978), # Batt Bus output voltage in V
"I5VBUS": lambda: self.telemetry_request([0xE2, 0x14], 0.005237), # 5V Bus output current in A
"V5VBUS": lambda: self.telemetry_request([0xE2, 0x10], 0.005865), # 5V Bus output voltage in V
"I3V3BUS": lambda: self.telemetry_request([0xE2, 0x04], 0.005237), # 3V3 Bus output current in A
"V3V3BUS": lambda: self.telemetry_request([0xE2, 0x00], 0.004311), # 3V3 Bus output voltage in V
"VSW1": lambda: self.telemetry_request([0xE4, 0x10], 0.01349), # SW1 output voltage in V
"ISW1": lambda: self.telemetry_request([0xE4, 0x14], 0.001328), # SW1 output current in A
"VSW2": lambda: self.telemetry_request([0xE4, 0x20], 0.01349), # SW2 output voltage in V
"ISW2": lambda: self.telemetry_request([0xE4, 0x24], 0.001328), # SW2 output current in A
"VSW3": lambda: self.telemetry_request([0xE4, 0x30], 0.008993), # SW3 output voltage in V
"ISW3": lambda: self.telemetry_request([0xE4, 0x34], 0.006239), # SW3 output current in A
"VSW4": lambda: self.telemetry_request([0xE4, 0x40], 0.008993), # SW4 output voltage in V
"ISW4": lambda: self.telemetry_request([0xE4, 0x44], 0.006239), # SW4 output current in A
"VSW5": lambda: self.telemetry_request([0xE4, 0x50], 0.005865), # SW5 output voltage in V
"ISW5": lambda: self.telemetry_request([0xE4, 0x54], 0.001328), # SW5 output current in A
"VSW6": lambda: self.telemetry_request([0xE4, 0x60], 0.005865), # SW6 output voltage in V
"ISW6": lambda: self.telemetry_request([0xE4, 0x64], 0.001328), # SW6 output current in A
"VSW7": lambda: self.telemetry_request([0xE4, 0x70], 0.005865), # SW7 output voltage in V
"ISW7": lambda: self.telemetry_request([0xE4, 0x74], 0.001328), # SW7 output current in A
"VSW8": lambda: self.telemetry_request([0xE4, 0x80], 0.004311), # SW8 output voltage in V
"ISW8": lambda: self.telemetry_request([0xE4, 0x84], 0.001328), # SW8 output current in A
"VSW9": lambda: self.telemetry_request([0xE4, 0x90], 0.004311), # SW9 output voltage in V
"ISW9": lambda: self.telemetry_request([0xE4, 0x94], 0.001328), # SW9 output current in A
"VSW10": lambda: self.telemetry_request([0xE4, 0xA0], 0.004311), # SW10 output voltage in V
"ISW10": lambda: self.telemetry_request([0xE4, 0xA4], 0.001328), # SW10 output current in A
"TBRD": lambda: self.telemetry_request([0xE3, 0x08], 0.372434), # Motherboard temperature in K
# Telemetry unique to 25-02452 and 01-02453 (CHECK THIS LATER)
"VBCR1": lambda: self.telemetry_request([0xE1, 0x10], 0.0322581), # Voltage feeding BCR1 in V
"IBCR1A": lambda: self.telemetry_request([0xE1, 0x14], 0.0009775), # Current BCR1 connector SA1A in A
"IBCR1B": lambda: self.telemetry_request([0xE1, 0x15], 0.0009775), # Current BCR1 connector SA1B in B
"TBCR1A": lambda: self.telemetry_request([0xE1, 0x18], 0.4963), # Array temperature connector SA1A in K
"TBCR1B": lambda: self.telemetry_request([0xE1, 0x19], 0.4963), # Array temperature connector SA1B in K
"SDBCR1A": lambda: self.telemetry_request([0xE1, 0x1C], 1.59725), # Sun detector connector SA1A in W/m^2
"SDBCR1B": lambda: self.telemetry_request([0xE1, 0x1D], 1.59725), # Sun detector connector SA1B in W/m^2
"VBCR2": lambda: self.telemetry_request([0xE1, 0x20], 0.0322581), # Voltage feeding BCR2 in V
"IBCR2A": lambda: self.telemetry_request([0xE1, 0x24], 0.0009775), # Current BCR2 connector SA2A in A
"IBCR2B": lambda: self.telemetry_request([0xE1, 0x25], 0.0009775), # Current BCR2 connector SA2B in B
"TBCR2A": lambda: self.telemetry_request([0xE1, 0x28], 0.4963), # Array temperature connector SA2A in K
"TBCR2B": lambda: self.telemetry_request([0xE1, 0x29], 0.4963), # Array temperature connector SA2B in K
"SDBCR2A": lambda: self.telemetry_request([0xE1, 0x2C], 1.59725), # Sun detector connector SA2A in W/m^2
"SDBCR2B": lambda: self.telemetry_request([0xE1, 0x2D], 1.59725), # Sun detector connector SA2B in W/m^2
"VBCR3": lambda: self.telemetry_request([0xE1, 0x30], 0.0099706), # Voltage feeding BCR3 in V,
# can also be used to monitor input voltage from 5V USB CHG
"IBCR3A": lambda: self.telemetry_request([0xE1, 0x34], 0.0009775), # Current BCR3 connector SA3A in A,
# can also be used to monitor input current from 5V USB CHG
"IBCR3B": lambda: self.telemetry_request([0xE1, 0x35], 0.0009775), # Current BCR3 connector SA3B in B
"TBCR3A": lambda: self.telemetry_request([0xE1, 0x38], 0.4963), # Array temperature connector SA3A in K
"TBCR3B": lambda: self.telemetry_request([0xE1, 0x39], 0.4963), # Array temperature connector SA3B in K
"SDBCR3A": lambda: self.telemetry_request([0xE1, 0x3C], 1.59725), # Sun detector connector SA3A in W/m^2
"SDBCR3B": lambda: self.telemetry_request([0xE1, 0x3D], 1.59725), # Sun detector connector SA3B in W/m^2
}
# PCM busses for the Bus Reset command
# Combine as needed to reset multiple buses, e.g. 0x03 resets Battery and 5V
self.pcm_busses = {
"Battery": [0x01],
"5V": [0x02],
"3.3V": [0x04],
"12V": [0x08],
}
def request(self, register, data, length) -> bytes:
"""
Requests and returns uninterpreted bytes object
:param register: register
:param data: data
:param length: number of bytes to read
:return: (byte) response from EPS
"""
self.bus.write_i2c_block_data(self.addr, register, data)
time.sleep(.25)
result = self.bus.read_i2c_block_data(self.addr, 0, length)
time.sleep(.25)
return result
def command(self, register, data) -> bool:
"""
Sends command to EPS
:param register: register
:param data: data
:return: (bool) whether command was successful
"""
result = self.bus.write_i2c_block_data(self.addr, register, data)
time.sleep(.25)
return result
def telemetry_request(self, tle, multiplier) -> float:
"""
Requests and returns interpreted telemetry data
:param tle: TLE code
:parm multiplier: = multiplier
:return: (float) telemetry value
"""
result = []
for i in range(5): #avg filter
raw = self.request(0x10, tle, 2)
result.append((raw[0] << 8 | raw[1]) * multiplier)
return sum(result)/len(result)
def bus_power(self) -> float:
"""
Returns total bus power draw
:return: (float) total bus power draw
"""
return self.telemetry["I12VBUS"]() * self.telemetry["V12VBUS"]() + \
self.telemetry["IBATBUS"]() * self.telemetry["VBATBUS"]() + \
self.telemetry["I5VBUS"]() * self.telemetry["V5VBUS"]() + \
self.telemetry["I3V3BUS"]() * self.telemetry["V3V3BUS"]()
def raw_pdm_draw(self) -> tuple:
"""
Returns which pdms are on, power draw of each pdm
:return: (tuple) 10-element list of which pdms are on, 10-element list of power draws per pdm
"""
raw = self.commands["All Actual States"]()
actual_on = raw[2] << 8 | raw[3]
ls = []
for i in range(1, 11):
b = actual_on & int(math.pow(2, i)) >> i
if b:
ls.append(self.telemetry[self.bitsToTelem[i][0]]() * self.telemetry[self.bitsToTelem[i][1]]())
else:
ls.append(0)
pdm_states = []
for pdm in range(1, 11):
pdm_states.append(actual_on & int(math.pow(2, pdm)) >> pdm)
return pdm_states, ls
def raw_solar_gen(self) -> list:
"""
Returns solar generation of all three busses
:return: (list) 3-elements: BCR1, BCR2, BCR3 power input
"""
return [self.telemetry["VBCR" + str(i)]() * max([self.telemetry["IBCR" + str(i) + j]()
for j in ["A", "B"]]) for i in range(1, 4)]
def solar_power(self) -> float:
"""
Returns net solar power gain
:return: (float) power gain in W
"""
return sum(self.raw_solar_gen())
def sun_detected(self) -> bool:
"""
:return: (bool) Whether sun is detected
"""
return self.telemetry["SDBCR1A"]() + self.telemetry["SDBCR2A"]() + self.telemetry["SDBCR1B"]() + \
self.telemetry["SDBCR2B"]() > EPS.SUN_DETECTION_THRESHOLD