Skip to content

Commit 70b4c28

Browse files
Updated Transducer Definition and Interface for #380 and #381
Reworking Transducer Def for #380 and #381 Minor fixes for inverting TX polarity. Add module invert to logger printout New TransducerArray made TransformedTransducer subclass of Transducer Update Elements format, database loading add backwards compatibilty, database write Update db_dvc.dvc bugfix in transducer, upload test script new tests Fixes for Pytest Update test_watertank.py Update test_watertank_simulation.py Update test_watertank.py remove `module_invert` from LIFUInterface the `module_invert` attribute of LIFUInterface had no function after instantiation, as it passed it's state through to `TxDevice`. `TxDevice` also passes `module_invert` through to the `TxRegisters`, but because we call `enum_tx_7332_devices()` at various points, which re-instantiates the `TxRegisters`, the `TxDevice` needs to (for now) hold that state.
1 parent 7017594 commit 70b4c28

File tree

18 files changed

+1556
-872
lines changed

18 files changed

+1556
-872
lines changed

db_dvc.dvc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
outs:
2-
- md5: 107d8598bb23a151f184ead9c8593d4a.dir
3-
nfiles: 435
2+
- md5: 8c01cefb88391f95752dcc83deccc514.dir
3+
nfiles: 438
44
hash: md5
55
path: db_dvc
6-
size: 1500527483

notebooks/single_pulse.py

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
import os
5+
import sys
6+
import time
7+
from pathlib import Path
8+
9+
if os.name == 'nt':
10+
pass
11+
else:
12+
pass
13+
14+
import numpy as np
15+
16+
from openlifu.bf.pulse import Pulse
17+
from openlifu.bf.sequence import Sequence
18+
from openlifu.db import Database
19+
from openlifu.geo import Point
20+
from openlifu.io.LIFUInterface import LIFUInterface
21+
from openlifu.plan.solution import Solution
22+
23+
# set PYTHONPATH=%cd%\src;%PYTHONPATH%
24+
# python notebooks/test_watertank.py
25+
26+
"""
27+
Test script to automate:
28+
1. Connect to the device.
29+
2. Test HVController: Turn HV on/off and check voltage.
30+
3. Test Device functionality.
31+
"""
32+
33+
# Configure logging
34+
logger = logging.getLogger(__name__)
35+
logger.setLevel(logging.INFO)
36+
37+
# Prevent duplicate handlers and cluttered terminal output
38+
if not logger.hasHandlers():
39+
handler = logging.StreamHandler()
40+
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
41+
logger.addHandler(handler)
42+
logger.propagate = False
43+
44+
log_interval = 1 # seconds; you can adjust this variable as needed
45+
46+
# set focus
47+
xInput = 0
48+
yInput = 0
49+
zInput = 50
50+
51+
frequency_kHz = 400 # Frequency in kHz
52+
voltage = 20.0 # Voltage in Volts
53+
duration_msec = 5/400 # Pulse Duration in milliseconds
54+
interval_msec = 20 # Pulse Repetition Interval in milliseconds
55+
num_modules = 2 # Number of modules in the system
56+
57+
use_external_power_supply = False # Select whether to use console or power supply
58+
59+
console_shutoff_temp_C = 70.0 # Console shutoff temperature in Celsius
60+
tx_shutoff_temp_C = 70.0 # TX device shutoff temperature in Celsius
61+
ambient_shutoff_temp_C = 70.0 # Ambient shutoff temperature in Celsius
62+
63+
#TODO: script_timeout_minutes = 30 # Prevent unintentionally leaving unit on for too long
64+
#TODO: log_temp_to_csv_file = True # Log readings to only terminal or both terminal and CSV file
65+
66+
# Fail-safe parameters if the temperature jumps too fast
67+
rapid_temp_shutoff_C = 40 # Cutoff temperature in Celsius if it jumps too fast
68+
rapid_temp_shutoff_seconds = 5 # Time in seconds to reach rapid temperature shutoff
69+
rapid_temp_increase_per_second_shutoff_C = 3 # Rapid temperature climbing shutoff in Celsius
70+
71+
peak_to_peak_voltage = voltage * 2 # Peak to peak voltage for the pulse
72+
73+
here = Path(__file__).parent.resolve()
74+
db_path = here / ".." / "db_dvc"
75+
db = Database(db_path)
76+
arr = db.load_transducer(f"openlifu_{num_modules}x400_evt1_005")
77+
arr.sort_by_pin()
78+
79+
target = Point(position=(xInput,yInput,zInput), units="mm")
80+
focus = target.get_position(units="mm")
81+
distances = np.sqrt(np.sum((focus - arr.get_positions(units="mm"))**2, 1)).reshape(1,-1)
82+
tof = distances*1e-3 / 1500
83+
delays = tof.max() - tof
84+
print(f"TOF Max = {tof.max()*1e6} us")
85+
86+
apodizations = np.ones((1, arr.numelements()))
87+
#active_element = 25
88+
#active_element = np.arange(65,128)
89+
#apodizations = np.zeros((1, arr.numelements()))
90+
#apodizations[:, active_element-1] = 1
91+
92+
logger.info("Starting LIFU Test Script...")
93+
interface = LIFUInterface(ext_power_supply=use_external_power_supply)
94+
tx_connected, hv_connected = interface.is_device_connected()
95+
96+
if not use_external_power_supply and not tx_connected:
97+
logger.warning("TX device not connected. Attempting to turn on 12V...")
98+
interface.hvcontroller.turn_12v_on()
99+
100+
# Give time for the TX device to power up and enumerate over USB
101+
time.sleep(2)
102+
103+
# Cleanup and recreate interface to reinitialize USB devices
104+
interface.stop_monitoring()
105+
del interface
106+
time.sleep(1) # Short delay before recreating
107+
108+
logger.info("Reinitializing LIFU interface after powering 12V...")
109+
interface = LIFUInterface(ext_power_supply=use_external_power_supply)
110+
111+
# Re-check connection
112+
tx_connected, hv_connected = interface.is_device_connected()
113+
114+
if not use_external_power_supply:
115+
if hv_connected:
116+
logger.info(f" HV Connected: {hv_connected}")
117+
else:
118+
logger.error("❌ HV NOT fully connected.")
119+
sys.exit(1)
120+
else:
121+
logger.info(" Using external power supply")
122+
123+
if tx_connected:
124+
logger.info(f" TX Connected: {tx_connected}")
125+
logger.info("✅ LIFU Device fully connected.")
126+
else:
127+
logger.error("❌ TX NOT fully connected.")
128+
sys.exit(1)
129+
130+
stop_logging = False # flag to signal the logging thread to stop
131+
132+
# Verify communication with the devices
133+
if not interface.txdevice.ping():
134+
logger.error("Failed to ping the transmitter device.")
135+
sys.exit(1)
136+
137+
if not use_external_power_supply and not interface.hvcontroller.ping():
138+
logger.error("Failed to ping the console device.")
139+
sys.exit(1)
140+
141+
if not use_external_power_supply:
142+
try:
143+
console_firmware_version = interface.hvcontroller.get_version()
144+
logger.info(f"Console Firmware Version: {console_firmware_version}")
145+
except Exception as e:
146+
logger.error(f"Error querying console firmware version: {e}")
147+
try:
148+
tx_firmware_version = interface.txdevice.get_version()
149+
logger.info(f"TX Firmware Version: {tx_firmware_version}")
150+
except Exception as e:
151+
logger.error(f"Error querying TX firmware version: {e}")
152+
153+
logger.info("Enumerate TX7332 chips")
154+
num_tx_devices = interface.txdevice.enum_tx7332_devices()
155+
if num_tx_devices == 0:
156+
raise ValueError("No TX7332 devices found.")
157+
elif num_tx_devices == num_modules*2:
158+
logger.info(f"Number of TX7332 devices found: {num_tx_devices}")
159+
numelements = 32*num_tx_devices
160+
else:
161+
raise Exception(f"Number of TX7332 devices found: {num_tx_devices} != 2x{num_modules}")
162+
163+
logger.info(f'Apodizations: {apodizations}')
164+
logger.info(f'Delays: {delays}')
165+
166+
pulse = Pulse(frequency=frequency_kHz*1e3, duration=duration_msec*1e-3)
167+
168+
sequence = Sequence(
169+
pulse_interval=interval_msec*1e-3,
170+
pulse_count=int(60/(interval_msec*1e-3)),
171+
pulse_train_interval=0,
172+
pulse_train_count=1
173+
)
174+
175+
pin_order = np.argsort([el.pin for el in arr.elements])
176+
solution = Solution(
177+
delays = delays[:, pin_order],
178+
apodizations = apodizations[:, pin_order],
179+
transducer=arr,
180+
pulse = pulse,
181+
voltage=voltage,
182+
sequence = sequence
183+
)
184+
profile_index = 1
185+
profile_increment = True
186+
trigger_mode = "single"
187+
188+
if use_external_power_supply:
189+
logger.info(f"Using external power supply. Ensure HV is turned on and set to {voltage}V before starting the trigger.")
190+
191+
interface.set_solution(
192+
solution=solution,
193+
profile_index=profile_index,
194+
profile_increment=profile_increment,
195+
trigger_mode=trigger_mode)
196+
197+
logger.info("Get Trigger")
198+
trigger_setting = interface.txdevice.get_trigger_json()
199+
if trigger_setting:
200+
logger.info(f"Trigger Setting: {trigger_setting}")
201+
else:
202+
logger.error("Failed to get trigger setting.")
203+
sys.exit(1)
204+
205+
duty_cycle = int((duration_msec/interval_msec) * 100)
206+
if duty_cycle > 50:
207+
logger.warning("❗❗ Duty cycle is above 50% ❗❗")
208+
209+
logger.info(f"User parameters set: \n\
210+
Module Invert: {arr.module_invert}\n\
211+
Frequency: {frequency_kHz}kHz\n\
212+
Voltage Per Rail: {voltage}V\n\
213+
Voltage Peak to Peak: {peak_to_peak_voltage}V\n\
214+
Duration: {duration_msec}ms\n\
215+
Interval: {interval_msec}ms\n\
216+
Duty Cycle: {duty_cycle}%\n\
217+
Use External Power Supply: {use_external_power_supply}\n\
218+
Initial Temp Safety Shutoff: Increase to {rapid_temp_shutoff_C}°C within {rapid_temp_shutoff_seconds}s of starting.\n\
219+
General Temp Safety Shutoff: Increase of {rapid_temp_increase_per_second_shutoff_C}°C within {log_interval}s at any point.\n")
220+
221+
def turn_off_console_and_tx():
222+
if not use_external_power_supply:
223+
logger.info("Attempting to turn off High Voltage...")
224+
interface.hvcontroller.turn_hv_off()
225+
if interface.hvcontroller.get_hv_status():
226+
logger.error("High Voltage is still on.")
227+
else:
228+
logger.info("High Voltage successfully turned off.")
229+
230+
logger.info("Attempting to turn off 12V...")
231+
interface.hvcontroller.turn_12v_off()
232+
if interface.hvcontroller.get_12v_status():
233+
logger.error("12V is still on.")
234+
else:
235+
logger.info("12V successfully turned off.")
236+
237+
logger.info("turning HV ON")
238+
interface.hvcontroller.turn_hv_on()
239+
s = input("Press any key to start")
240+
logger.info("Sending Single Trigger...")
241+
interface.txdevice.start_trigger()
242+
time.sleep(0.1)
243+
interface.txdevice.stop_trigger()
244+
interface.hvcontroller.turn_hv_off()
245+
turn_off_console_and_tx()
246+
logger.info("Finished")

notebooks/test_watertank.py

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
arr = db.load_transducer(f"openlifu_{num_modules}x400_evt1")
7878
arr.sort_by_pin()
7979

80+
8081
target = Point(position=(xInput,yInput,zInput), units="mm")
8182
focus = target.get_position(units="mm")
8283
distances = np.sqrt(np.sum((focus - arr.get_positions(units="mm"))**2, 1)).reshape(1,-1)
@@ -92,7 +93,7 @@
9293

9394

9495
logger.info("Starting LIFU Test Script...")
95-
interface = LIFUInterface()
96+
interface = LIFUInterface(ext_power_supply=use_external_power_supply)
9697
tx_connected, hv_connected = interface.is_device_connected()
9798

9899
if not use_external_power_supply and not tx_connected:
@@ -108,7 +109,7 @@
108109
time.sleep(1) # Short delay before recreating
109110

110111
logger.info("Reinitializing LIFU interface after powering 12V...")
111-
interface = LIFUInterface()
112+
interface = LIFUInterface(ext_power_supply=use_external_power_supply)
112113

113114
# Re-check connection
114115
tx_connected, hv_connected = interface.is_device_connected()
@@ -291,6 +292,7 @@ def log_temperature():
291292
solution = Solution(
292293
delays = delays[:, pin_order],
293294
apodizations = apodizations[:, pin_order],
295+
transducer=arr,
294296
pulse = pulse,
295297
voltage=voltage,
296298
sequence = sequence
@@ -300,32 +302,19 @@ def log_temperature():
300302
trigger_mode = "continuous"
301303

302304
if use_external_power_supply:
303-
interface.check_solution(solution)
304-
sol_dict = solution.to_dict()
305-
interface.txdevice.set_solution(
306-
pulse = sol_dict['pulse'],
307-
delays = sol_dict['delays'],
308-
apodizations= sol_dict['apodizations'],
309-
sequence= sol_dict['sequence'],
310-
trigger_mode=trigger_mode,
311-
profile_index=profile_index,
312-
profile_increment=profile_increment
313-
)
314305
logger.info(f"Using external power supply. Ensure HV is turned on and set to {voltage}V before starting the trigger.")
315-
else:
316-
interface.set_solution(
317-
solution=solution,
318-
profile_index=profile_index,
319-
profile_increment=profile_increment,
320-
trigger_mode=trigger_mode,
321-
turn_hv_on=True)
306+
interface.set_solution(
307+
solution=solution,
308+
profile_index=profile_index,
309+
profile_increment=profile_increment,
310+
trigger_mode=trigger_mode)
322311

323312
logger.info("Get Trigger")
324313
trigger_setting = interface.txdevice.get_trigger_json()
325314
if trigger_setting:
326315
logger.info(f"Trigger Setting: {trigger_setting}")
327316
else:
328-
logger.error("Failed to sgt trigger setting.")
317+
logger.error("Failed to get trigger setting.")
329318
sys.exit(1)
330319

331320

@@ -334,6 +323,7 @@ def log_temperature():
334323
logger.warning("❗❗ Duty cycle is above 50% ❗❗")
335324

336325
logger.info(f"User parameters set: \n\
326+
Module Invert: {arr.module_invert}\n\
337327
Frequency: {frequency_kHz}kHz\n\
338328
Voltage Per Rail: {voltage}V\n\
339329
Voltage Peak to Peak: {peak_to_peak_voltage}V\n\
@@ -385,7 +375,7 @@ def input_wrapper():
385375
user_input = threading.Thread(target=input_wrapper)
386376

387377
logger.info("Starting Trigger...")
388-
if interface.start_sonication(use_external_power_supply):
378+
if interface.start_sonication():
389379
logger.info("Trigger Running...")
390380
logger.info("Press enter to STOP trigger:")
391381

@@ -408,7 +398,7 @@ def input_wrapper():
408398
user_input.join()
409399

410400
time.sleep(0.5) # Give the logging thread time to finish
411-
if interface.stop_sonication(use_external_power_supply):
401+
if interface.stop_sonication():
412402
logger.info("Trigger stopped successfully.")
413403
else:
414404
logger.error("Failed to stop trigger.")

0 commit comments

Comments
 (0)