Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
132 commits
Select commit Hold shift + click to select a range
e8cf0c2
Updating
peterhollender Jan 16, 2024
86aa838
tx7332 wip
peterhollender Mar 1, 2024
4812d52
[WIP] reorganize and rename
peterhollender Mar 28, 2024
a108a90
Update test_plan.ipynb
peterhollender Mar 28, 2024
a9e29af
rename profile to index where remaining
peterhollender Apr 4, 2024
048b943
Switch from index back to profile
peterhollender Apr 9, 2024
076ebe3
Update test_registers.ipynb
peterhollender Apr 9, 2024
68984ce
Merge branch 'main' into p/organize
peterhollender Apr 9, 2024
7fee63c
change print_dict to print_regs
peterhollender Apr 9, 2024
d8430ef
Merge branch 'p/reorganize' into p/organize
peterhollender Apr 11, 2024
0020871
Merge branch 'p/renaming' into p/organize
peterhollender Apr 13, 2024
803169c
move xdc to same folder format
peterhollender Apr 13, 2024
46a8524
Fix seg_methods loading from dictionary
peterhollender Apr 13, 2024
52c8ab7
[BUG] update Plan to Protocol
peterhollender Apr 13, 2024
c598c3b
Add c0 parameter to DirectDelays to match json
peterhollender Apr 13, 2024
45bcd6e
Update test notebooks
peterhollender Apr 13, 2024
001a242
Updates for backwards compatibility with unpatched k-wave
peterhollender Apr 15, 2024
aa1fde5
Improves import order
peterhollender Apr 18, 2024
f640f69
Adds json-serializability to transducer and elements
peterhollender Apr 18, 2024
1ec3785
Added USTX control, Added Demo Notebook and QT5 Application
georgevigelette May 17, 2024
b6d5691
added pinmap.json that is used for the transducer pin mapping by the …
georgevigelette May 17, 2024
652973b
fixed uart read
georgevigelette May 19, 2024
6a18569
added trigger frequency settings
georgevigelette May 19, 2024
89fa665
update setup tools
georgevigelette May 21, 2024
6d3ee9b
ran test on notebook
georgevigelette May 21, 2024
91d028c
updated readme with high voltge power supply pin connections
georgevigelette May 21, 2024
03bad88
note on pinmap file
georgevigelette May 21, 2024
6206f2a
fixed molex connector connections
georgevigelette May 22, 2024
64ebec0
silkscreen is wrong on the PCB + and - HV are swapped
georgevigelette May 22, 2024
67291b2
Merge branch 'main' into daxsonics
peterhollender May 29, 2024
5465975
update init
peterhollender May 29, 2024
2a3e86d
for Daxsonics only use the first AFE (64 elements)
georgevigelette May 29, 2024
04b4068
adds new json and updates indexing
peterhollender May 29, 2024
ea33e60
Merge branch 'daxsonics' of https://github.com/OpenwaterHealth/open_p…
peterhollender May 29, 2024
d6984a0
Update DemoApplication.py
peterhollender May 29, 2024
48786eb
added axis reference for focus
georgevigelette Jun 10, 2024
8b09adf
Merge branch 'daxsonics' of github.com:OpenwaterHealth/open_pyfus int…
georgevigelette Jun 10, 2024
40c9e82
Update DemoApplication.py
peterhollender Jun 10, 2024
5d5153d
update notebook
peterhollender Jun 10, 2024
b28c5ad
added apodization mapping
Jul 17, 2024
dee90f6
added flag to auto discover control board
georgevigelette Aug 1, 2024
e804e6e
Merge branch 'daxsonics' of github.com:OpenwaterHealth/open_pyfus int…
georgevigelette Aug 1, 2024
d098269
set control board to True
georgevigelette Aug 1, 2024
92e0eee
updated python notebook to async uart
georgevigelette Aug 1, 2024
ddf8a77
added async uart to Demo application and Python Notebook
georgevigelette Aug 1, 2024
eb798b9
paramaterized PORT Name for uart
georgevigelette Aug 9, 2024
b010aa2
Add HV Controller and USTX controller placeholders
peterhollender Nov 1, 2024
5f3a341
Update pulse docstrings to show that amplitude is in V, not Pa
peterhollender Nov 1, 2024
3e6baa9
Merge branch 'async' into p/sonication_control
peterhollender Nov 8, 2024
ec68e51
refactor ow_ustx into openlifu
peterhollender Nov 8, 2024
dd1feb7
starting to fix imports
peterhollender Nov 8, 2024
e17497d
refactor dict_conversion to fix import bug
peterhollender Nov 13, 2024
0fa1b84
calculate profile registers
peterhollender Nov 21, 2024
36e95b2
start integration
duvitech-llc Nov 21, 2024
6c41520
stub out some of the api
georgevigelette Nov 21, 2024
6c98762
added demo mode for testing without hardware
georgevigelette Nov 22, 2024
21c28ab
initial changes and checkin of 3.1 branch
georgevigelette Dec 4, 2024
f09667a
demo gui working
georgevigelette Dec 4, 2024
17002ac
updated notebook example with straight python (to be converted to a n…
georgevigelette Dec 4, 2024
a10752d
WIP updating tests to use TxModule
peterhollender Dec 4, 2024
34fda46
updated gui code
georgevigelette Dec 4, 2024
6204ec5
added pwr_if initial tests
georgevigelette Dec 5, 2024
e874dfe
added pwr_if all basic tests
georgevigelette Dec 5, 2024
983663c
updated dependencies
georgevigelette Dec 5, 2024
0f27e1c
update power test procedure
georgevigelette Dec 5, 2024
41c29b2
[WIP] refactor for review
peterhollender Dec 10, 2024
4844b02
Rework public interface and stress testing API
georgevigelette Jan 21, 2025
94ce5b1
transmitter demo waveform test
georgevigelette Jan 21, 2025
f61b448
removing async from demoapplication for intermediate testing
georgevigelette Jan 23, 2025
d41f406
demoapplication running with updated interface
georgevigelette Jan 23, 2025
3d5eda8
added 12v on off
georgevigelette Jan 23, 2025
d3bb5ca
added 12v enable
georgevigelette Jan 23, 2025
0c8b18f
added reset command
georgevigelette Jan 23, 2025
5291e7d
bug fixes
georgevigelette Jan 23, 2025
29479d4
set voltage
georgevigelette Jan 23, 2025
7c58b3a
added timeout
georgevigelette Jan 24, 2025
24214fd
read voltage setting
georgevigelette Jan 24, 2025
77d2ac3
adding capability to load TI config generated from TI TX7332 Demo output
georgevigelette Jan 30, 2025
4999068
updated test comms to slower freq
georgevigelette Feb 5, 2025
7cbe315
added write ti config to tx chips
georgevigelette Feb 6, 2025
96914db
added load ti config file
georgevigelette Feb 6, 2025
86074bf
finished migrating demo ui to new api interface
georgevigelette Feb 6, 2025
7b52a01
removed delay
georgevigelette Feb 6, 2025
8aa4ed7
removing unecessary old libs
georgevigelette Feb 7, 2025
421383a
new packet type for one wire
georgevigelette Feb 7, 2025
0478491
fix apodization typo in duty cycle
peterhollender Feb 11, 2025
536aa2f
remove async
georgevigelette Feb 12, 2025
9773a7a
Merge branch 'p/sonication_control_refactor' of github.com:OpenwaterH…
georgevigelette Feb 12, 2025
ad7a94a
removed i2c
georgevigelette Feb 13, 2025
7a927ad
Reorganizes ustx into LIFUTXDevice
peterhollender Feb 13, 2025
65f222a
Merge branch 'p/sonication_control_refactor' of https://github.com/Op…
peterhollender Feb 13, 2025
24ef3c9
Remove unrelated files
peterhollender Feb 13, 2025
88f9fa6
Delete pinmap.json
peterhollender Feb 13, 2025
748f120
removes ustx.py
peterhollender Feb 13, 2025
ae09fde
Merge branch 'main' into p/sonication_control_refactor
peterhollender Feb 14, 2025
d2aafe9
Fix DictMixin import
ebrahimebrahim Feb 14, 2025
71d4e2f
Fix DictMixin import
ebrahimebrahim Feb 14, 2025
67329d3
Remove instruction to mess with PYTHONPATH
ebrahimebrahim Feb 14, 2025
3d3d99d
Remove qt app dependencies
ebrahimebrahim Feb 14, 2025
f41b0b4
Run automated safe pre-commit fixes
ebrahimebrahim Feb 14, 2025
956dc7e
Run automated pre-commit UP007 fixes
ebrahimebrahim Feb 14, 2025
990dec9
Fix last remaining ruff style issues
ebrahimebrahim Feb 14, 2025
ae913d8
Auto fix indentation in LIFUHVController.py
ebrahimebrahim Feb 14, 2025
3b2c1d9
Fix remaining pylint issues
ebrahimebrahim Feb 14, 2025
28015f2
remove extraneous comments and imports
ebrahimebrahim Feb 14, 2025
7bcc507
Set specific loggers to debug rather than root logger
ebrahimebrahim Feb 14, 2025
0c941cf
Fix broken device interface reference
peterhollender Feb 14, 2025
db094cb
Remove tx_7332_if
peterhollender Feb 14, 2025
de75757
Interface cleanup
peterhollender Feb 14, 2025
50f7d70
style update
ebrahimebrahim Feb 14, 2025
2cf7a99
added dfu mode switch
georgevigelette Feb 19, 2025
0539dfc
update uart to handle serial exception
georgevigelette Feb 25, 2025
97db0dd
consolidated interface
georgevigelette Feb 25, 2025
cde79e6
reworking improving widget
georgevigelette Feb 26, 2025
1ac926a
moved to pyQT6
georgevigelette Feb 26, 2025
638b88a
added ambient temperature reading
georgevigelette Feb 26, 2025
c30dde3
Create test_solution.py
peterhollender Feb 26, 2025
7d07a6c
Merge branch 'local-dev' into p/sonication_control_refactor
georgevigelette Feb 26, 2025
ffa74a3
updates
georgevigelette Feb 26, 2025
7dceb1e
sort import
georgevigelette Feb 26, 2025
6f674e7
updated error raising
georgevigelette Feb 27, 2025
c3e6d74
fixing lint errors
georgevigelette Feb 27, 2025
bfc0098
demo mode work
georgevigelette Feb 27, 2025
7a929b1
added calls to the widget
georgevigelette Feb 27, 2025
69f3813
added get ambient temperature
georgevigelette Feb 27, 2025
b605bc0
Shift interface to Dict
peterhollender Feb 27, 2025
4994f0e
Better mocking of LIFUInterface and bugfixes
peterhollender Feb 27, 2025
8ae7b59
style fixes
ebrahimebrahim Feb 27, 2025
5150bde
Set specific loggers to debug rather than root logger
ebrahimebrahim Feb 27, 2025
04ac50c
Add LIFUInterface test mode test
ebrahimebrahim Feb 27, 2025
64fe1e0
Fix sonication control mock test
ebrahimebrahim Feb 27, 2025
ddbb57a
Merge branch 'main' into p/sonication_control_refactor
ebrahimebrahim Feb 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 247 additions & 0 deletions notebooks/LIFUTestWidget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
import asyncio
import logging
import sys

from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt6.QtGui import QBrush, QColor, QPainter
from PyQt6.QtWidgets import QApplication, QLabel, QPushButton, QVBoxLayout, QWidget
from qasync import QEventLoop

from openlifu.io.LIFUInterface import LIFUInterface
from openlifu.plan.solution import ( # Assuming Pulse is needed to create a Solution
Pulse,
Solution,
)

# Configure logging
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)


class LIFUTestWidget(QWidget):
# Signals now accept two arguments: descriptor and port/data.
signal_connected = pyqtSignal(str, str)
signal_disconnected = pyqtSignal(str, str)
signal_data_received = pyqtSignal(str, str)

def __init__(self):
super().__init__()
self.interface = LIFUInterface(run_async=True)
# Maintain connection status for both descriptors
self.connections = {"TX": False, "HV": False}
self.treatment_running = False
self.init_ui()
self.connect_signals()

def init_ui(self):
"""Initialize the UI components."""
self.setWindowTitle("Open LIFU")
self.setGeometry(100, 100, 300, 350)

# Status label shows connection status for both devices
self.status_label = QLabel("TX: Disconnected, HV: Disconnected", self)
self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

# Existing buttons
self.send_ping_button = QPushButton("Send Ping", self)
self.send_ping_button.setEnabled(False)
self.send_ping_button.clicked.connect(self.send_ping_command)

self.treatment_button = QPushButton("Run Treatment (Off)", self)
self.treatment_button.setEnabled(False)
self.treatment_button.clicked.connect(self.toggle_treatment_run)

# New buttons to call interface methods:
self.load_solution_button = QPushButton("Load Solution", self)
self.load_solution_button.clicked.connect(self.load_solution)

self.start_sonication_button = QPushButton("Start Sonication", self)
self.start_sonication_button.clicked.connect(self.start_sonication)

self.stop_sonication_button = QPushButton("Stop Sonication", self)
self.stop_sonication_button.clicked.connect(self.stop_sonication)

self.get_status_button = QPushButton("Get Status", self)
self.get_status_button.clicked.connect(self.get_status)

# Layout
layout = QVBoxLayout()
layout.addWidget(self.status_label)
layout.addWidget(self.send_ping_button)
layout.addWidget(self.treatment_button)
layout.addWidget(self.load_solution_button)
layout.addWidget(self.start_sonication_button)
layout.addWidget(self.stop_sonication_button)
layout.addWidget(self.get_status_button)
self.setLayout(layout)

def connect_signals(self):
"""Connect the signals from the LIFU interface to the UI."""
# Connect TX signals
if hasattr(self.interface.txdevice, 'uart'):
self.interface.txdevice.uart.signal_connect.connect(self.signal_connected.emit)
self.interface.txdevice.uart.signal_disconnect.connect(self.signal_disconnected.emit)
self.interface.txdevice.uart.signal_data_received.connect(self.signal_data_received.emit)
else:
logger.warning("TX UART interface not found in LIFUInterface.")

# Connect HV signals
if hasattr(self.interface.hvcontroller, 'uart'):
self.interface.hvcontroller.uart.signal_connect.connect(self.signal_connected.emit)
self.interface.hvcontroller.uart.signal_disconnect.connect(self.signal_disconnected.emit)
self.interface.hvcontroller.uart.signal_data_received.connect(self.signal_data_received.emit)
else:
logger.warning("HV UART interface not found in LIFUInterface.")

# Connect our widget signals to slots
self.signal_connected.connect(self.on_connected)
self.signal_disconnected.connect(self.on_disconnected)
self.signal_data_received.connect(self.on_data_received)

async def start_monitoring(self):
"""Start monitoring for USB device connections."""
await self.interface.start_monitoring()

@pyqtSlot(str, str)
def on_connected(self, descriptor, port):
"""Handle the connected signal."""
self.connections[descriptor] = True
status_text = (
f"TX: {'Connected' if self.connections['TX'] else 'Disconnected'}, "
f"HV: {'Connected' if self.connections['HV'] else 'Disconnected'}"
)
self.status_label.setText(status_text)
# Enable buttons if TX is connected (assuming TX is needed for ping/treatment)
if self.connections["TX"]:
self.send_ping_button.setEnabled(True)
self.treatment_button.setEnabled(True)
self.update()

@pyqtSlot(str, str)
def on_disconnected(self, descriptor, port):
"""Handle the disconnected signal."""
self.connections[descriptor] = False
status_text = (
f"TX: {'Connected' if self.connections['TX'] else 'Disconnected'}, "
f"HV: {'Connected' if self.connections['HV'] else 'Disconnected'}"
)
self.status_label.setText(status_text)
# Disable TX buttons if TX is disconnected
if not self.connections["TX"]:
self.send_ping_button.setEnabled(False)
self.treatment_button.setEnabled(False)
self.update()

@pyqtSlot(str, str)
def on_data_received(self, descriptor, data):
"""Handle the data received signal."""
self.status_label.setText(f"{descriptor} Received: {data}")
self.update()

def send_ping_command(self):
"""Send a ping command on the TX device."""
if hasattr(self.interface.txdevice, 'ping'):
self.interface.txdevice.ping()
else:
logger.warning("TX device does not support ping.")

def toggle_treatment_run(self):
"""Toggle the treatment run state."""
self.interface.toggle_treatment_run(self.treatment_running)
self.treatment_running = not self.treatment_running
self.treatment_button.setText(
"Run Treatment (On)" if self.treatment_running else "Stop Treatment (Off)"
)

def load_solution(self):
"""Call the interface's set_solution method using a dummy solution."""
try:
# Create a fake solution for testing
fake_solution = Solution(name="Test Solution", pulse=Pulse(amplitude=5))
result = self.interface.set_solution(fake_solution)
if result:
self.status_label.setText("Solution loaded successfully.")
else:
self.status_label.setText("Failed to load solution.")
except Exception as e:
self.status_label.setText(f"Error loading solution: {e}")
logger.error("Error loading solution: %s", e)

def start_sonication(self):
"""Call the interface's start_sonication method."""
try:
result = self.interface.start_sonication()
if result:
self.status_label.setText("Sonication started.")
else:
self.status_label.setText("Failed to start sonication.")
except Exception as e:
self.status_label.setText(f"Error starting sonication: {e}")
logger.error("Error starting sonication: %s", e)

def stop_sonication(self):
"""Call the interface's stop_sonication method."""
try:
result = self.interface.stop_sonication()
if result:
self.status_label.setText("Sonication stopped.")
else:
self.status_label.setText("Failed to stop sonication.")
except Exception as e:
self.status_label.setText(f"Error stopping sonication: {e}")
logger.error("Error stopping sonication: %s", e)

def get_status(self):
"""Call the interface's get_status method and display the status."""
try:
status = self.interface.get_status()
self.status_label.setText(f"Status: {status}")
except Exception as e:
self.status_label.setText(f"Error getting status: {e}")
logger.error("Error getting status: %s", e)

def paintEvent(self, event):
"""Draw the connection status indicator."""
painter = QPainter(self)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
dot_radius = 20
dot_color = QColor("green") if any(self.connections.values()) else QColor("red")
brush = QBrush(dot_color)
painter.setBrush(brush)
rect = self.rect()
painter.drawEllipse(
rect.center().x() - dot_radius // 2,
rect.top() + 20,
dot_radius,
dot_radius
)

def closeEvent(self, event):
"""Handle application closure."""
self.cleanup_task = asyncio.create_task(self.cleanup_tasks())
super().closeEvent(event)

async def cleanup_tasks(self):
"""Stop monitoring and cancel running tasks."""
self.interface.stop_monitoring()
loop = asyncio.get_running_loop()
tasks = [t for t in asyncio.all_tasks(loop) if t is not asyncio.current_task()]
for task in tasks:
task.cancel()
await asyncio.gather(*tasks, return_exceptions=True)


if __name__ == "__main__":
app = QApplication(sys.argv)
loop = QEventLoop(app)
asyncio.set_event_loop(loop)

widget = LIFUTestWidget()
widget.show()

async def main():
await widget.start_monitoring()

with loop:
widget._monitor_task = asyncio.ensure_future(main())
loop.run_forever()
Binary file removed notebooks/foo.nii.gz
Binary file not shown.
Binary file removed notebooks/intensity.nii.gz
Binary file not shown.
25 changes: 25 additions & 0 deletions notebooks/run_self_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from openlifu.io.LIFUInterface import LIFUInterface

# set PYTHONPATH=%cd%\src;%PYTHONPATH%
# python notebooks/run_self_test.py
"""
Test script to automate:
1. Connect to the device.
2. Test HVController: Turn HV on/off and check voltage.
3. Test Device functionality.
"""
print("Starting LIFU Test Script...")
interface = LIFUInterface(test_mode=False)
tx_connected, hv_connected = interface.is_device_connected()
if tx_connected and hv_connected:
print("LIFU Device Fully connected.")
else:
print(f'LIFU Device NOT Fully Connected. TX: {tx_connected}, HV: {hv_connected}')

print("Ping the device")
interface.txdevice.ping()

print("Run Self OneWire Test")
interface.txdevice.run_test()

print("Tests Finished")
86 changes: 86 additions & 0 deletions notebooks/stress_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import random

from openlifu.io.LIFUInterface import LIFUInterface


def run_test(interface, iterations):
"""
Run the LIFU test loop with random trigger settings.

Args:
interface (LIFUInterface): The LIFUInterface instance.
iterations (int): Number of iterations to run.
"""
for i in range(iterations):
print(f"Starting Test Iteration {i + 1}/{iterations}...")

try:
tx_connected, hv_connected = interface.is_device_connected()
if not tx_connected: # or not hv_connected:
raise ConnectionError(f"LIFU Device NOT Fully Connected. TX: {tx_connected}, HV: {hv_connected}")

print("Ping the device")
interface.txdevice.ping()

print("Toggle LED")
interface.txdevice.toggle_led()

print("Get Version")
version = interface.txdevice.get_version()
print(f"Version: {version}")

print("Echo Data")
echo, length = interface.txdevice.echo(echo_data=b'Hello LIFU!')
if length > 0:
print(f"Echo: {echo.decode('utf-8')}")
else:
raise ValueError("Echo failed.")

print("Get HW ID")
hw_id = interface.txdevice.get_hardware_id()
print(f"HWID: {hw_id}")

print("Get Temperature")
temperature = interface.txdevice.get_temperature()
print(f"Temperature: {temperature} °C")

print("Get Trigger")
trigger_setting = interface.txdevice.get_trigger()
if trigger_setting:
print(f"Trigger Setting: {trigger_setting}")
else:
raise ValueError("Failed to get trigger setting.")

print("Set Trigger with Random Parameters")
# Generate random trigger frequency and pulse width
trigger_frequency = random.randint(5, 25) # Random frequency between 5 and 25 Hz
trigger_pulse_width = random.randint(10, 30) * 1000 # Random pulse width between 10 and 30 ms (convert to µs)

json_trigger_data = {
"TriggerFrequencyHz": trigger_frequency,
"TriggerMode": 1,
"TriggerPulseCount": 0,
"TriggerPulseWidthUsec": trigger_pulse_width
}
trigger_setting = interface.txdevice.set_trigger(data=json_trigger_data)
if trigger_setting:
print(f"Trigger Setting Applied: Frequency = {trigger_frequency} Hz, Pulse Width = {trigger_pulse_width // 1000} ms")
if trigger_setting["TriggerFrequencyHz"] != trigger_frequency or trigger_setting["TriggerPulseWidthUsec"] != trigger_pulse_width:
raise ValueError("Failed to set trigger setting.")
else:
raise ValueError("Failed to set trigger setting.")

print(f"Iteration {i + 1} passed.\n")

except Exception as e:
print(f"Test failed on iteration {i + 1}: {e}")
break

if __name__ == "__main__":
print("Starting LIFU Test Script...")
interface = LIFUInterface(test_mode=False)

# Number of iterations to run
test_iterations = 1000 # Change this to the desired number of iterations

run_test(interface, test_iterations)
Loading
Loading