Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
5 changes: 1 addition & 4 deletions examples/verification/tst01_console_selftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import sys

import base58

from openlifu_sdk.io.LIFUInterface import LIFUInterface

# set PYTHONPATH=%cd%\src;%PYTHONPATH%
Expand Down Expand Up @@ -46,8 +44,7 @@
print("Get HW ID")
hw_id = interface.hvcontroller.get_hardware_id()
print(f"HW ID: {hw_id}")
encoded_id = base58.b58encode(bytes.fromhex(hw_id)).decode()
print(f"OW-LIFU-CON-{encoded_id}")
print(f"OW-LIFU-CON-{hw_id}")

print("Get Temperature1")
temp1 = interface.hvcontroller.get_temperature1()
Expand Down
112 changes: 112 additions & 0 deletions notebooks/get_all_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
from __future__ import annotations

import logging
import os
import sys
import threading
import time
from pathlib import Path

if os.name == 'nt':
import msvcrt
else:
import select

import numpy as np

from openlifu.bf.pulse import Pulse
from openlifu.bf.sequence import Sequence
from openlifu.db import Database
from openlifu.geo import Point
from openlifu.io.LIFUInterface import LIFUInterface
from openlifu.plan.solution import Solution

# set PYTHONPATH=%cd%\src;%PYTHONPATH%
# python notebooks/test_watertank.py

"""
Test script to automate:
1. Connect to the device.
2. Test HVController: Turn HV on/off and check voltage.
3. Test Device functionality.
"""

# TO BE USED TO MONITOR TEMPERATURE CURVE TO SEE HOW LONG IT TAKES TO COOL DOWN

# Configure logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# Prevent duplicate handlers and cluttered terminal output
if not logger.hasHandlers():
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
logger.addHandler(handler)
logger.propagate = False

log_interval = 1 # seconds; you can adjust this variable as needed
num_modules = 2 # Number of modules in the system

use_external_power_supply = False # Select whether to use console or power supply

logger.info("Starting LIFU Test Script...")
interface = LIFUInterface(ext_power_supply=use_external_power_supply)
tx_connected, hv_connected = interface.is_device_connected()

if not use_external_power_supply and not tx_connected:
logger.warning("TX device not connected. Attempting to turn on 12V...")
interface.hvcontroller.turn_12v_on()

# Give time for the TX device to power up and enumerate over USB
time.sleep(2)

# Cleanup and recreate interface to reinitialize USB devices
interface.stop_monitoring()
del interface
time.sleep(1) # Short delay before recreating

logger.info("Reinitializing LIFU interface after powering 12V...")
interface = LIFUInterface(ext_power_supply=use_external_power_supply)

# Re-check connection
tx_connected, hv_connected = interface.is_device_connected()

if not use_external_power_supply:
if hv_connected:
logger.info(f" HV Connected: {hv_connected}")
else:
logger.error("❌ HV NOT fully connected.")
sys.exit(1)
else:
logger.info(" Using external power supply")

if tx_connected:
logger.info(f" TX Connected: {tx_connected}")
logger.info("✅ LIFU Device fully connected.")
else:
logger.error("❌ TX NOT fully connected.")
sys.exit(1)

# Verify communication with the devices
if not interface.txdevice.ping():
logger.error("Failed to ping the transmitter device.")
sys.exit(1)

if not use_external_power_supply and not interface.hvcontroller.ping():
logger.error("Failed to ping the console device.")
sys.exit(1)

print(f"console version: {interface.hvcontroller.get_version()}")

logger.info("Enumerate TX7332 chips")
# num_tx_devices = interface.txdevice.get_tx_module_count()
num_tx_devices = 10

for module in range(0, num_tx_devices+1):
try:
tx_firmware_version = interface.txdevice.get_version(module=module)
logger.info(f"TX Firmware Version: {tx_firmware_version}")
except Exception as e:
logger.error(f"Error querying TX firmware version: {e}")


66 changes: 32 additions & 34 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,74 +29,72 @@ classifiers = [
]
dynamic = ["version"]
dependencies = [
"xarray[io]",
"xarray[io]>=2026.2.0",
"numpy<2",
"pandas",
"scipy",
"requests",
"pandas>=3.0.2",
"scipy>=1.14.1",
"requests>=2.33.1",
]

[project.optional-dependencies]
jupyter = [
"ipykernel",
"matplotlib",
"ipykernel>=7.2.0",
"matplotlib>=3.10.0",
]
mesh = [
"embreex; platform_machine=='x86_64' or platform_machine=='AMD64'",
"trimesh",
"scikit-image",
"vtk",
"Pillow",
"OpenEXR"
"embreex>=2.17.7.post7; platform_machine=='x86_64' or platform_machine=='AMD64'",
"trimesh>=4.11.5",
"scikit-image>=0.26.0",
"vtk>=9.6.1",
"Pillow>=12.2.0",
"OpenEXR>=3.4.9"
]
db = [
"nibabel",
"pydicom"
"nibabel>=5.4.2",
"pydicom>=3.0.2"
]
io = [
"base58",
"crc",
"crcmod",
"pyserial",
"openlifu-sdk>=2.0.12"
"crc>=7.1.0",
"crcmod>=1.7",
"openlifu-sdk>=2.0.14"
]
sim = [
"k-wave-python==0.4.0",
"nvidia-ml-py"
"nvidia-ml-py>=13.595.45"
]
cloud = [
"watchdog",
"python-socketio[client]"
"watchdog>=6.0.0",
"python-socketio[client]>=5.16.1"
]
photogrammetry = [
"embreex; platform_machine=='x86_64' or platform_machine=='AMD64'",
"opencv-contrib-python",
"embreex>=2.17.7.post7; platform_machine=='x86_64' or platform_machine=='AMD64'",
"opencv-contrib-python>=4.11.0.86",
"onnxruntime==1.18.0",
"trimesh",
"scikit-image",
"vtk",
"Pillow",
"OpenEXR"
"trimesh>=4.11.5",
"scikit-image>=0.26.0",
"vtk>=9.6.1",
"Pillow>=12.2.0",
"OpenEXR>=3.4.9"
]
dev = [
"pytest >=6",
"pytest-cov >=3",
"pytest-mock",
"dvc[gdrive]",
"pytest-mock>=3.15.1",
"dvc[gdrive]>=3.67.1",
]
docs = [
"openlifu[mesh, db, cloud]",
"sphinx>=7.0",
"myst_parser>=0.13",
"sphinx_copybutton",
"sphinx_autodoc_typehints",
"sphinx_copybutton>=0.5.2",
"sphinx_autodoc_typehints>=3.9.11",
"furo>=2023.08.17",
]
test = [
"openlifu[mesh, db, io, sim, cloud, photogrammetry]",
"pytest >=6",
"pytest-cov >=3",
"pytest-mock",
"pytest-mock>=3.15.1",
]
all = [
"openlifu[jupyter, mesh, db, io, sim, cloud, photogrammetry, test, dev, docs]",
Expand Down
17 changes: 15 additions & 2 deletions src/openlifu/bf/apod_methods/maxangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,25 @@
from openlifu.bf.apod_methods import ApodizationMethod
from openlifu.geo.point import Point
from openlifu.util.annotations import OpenLIFUFieldData
from openlifu.util.field_display import summarize_fields
from openlifu.util.units import getunittype
from openlifu.xdc import Transducer


@dataclass
class MaxAngle(ApodizationMethod):
max_angle: Annotated[float, OpenLIFUFieldData("Maximum acceptance angle", "Maximum acceptance angle for each element from the vector normal to the element surface")] = 30.0
max_angle: Annotated[float, OpenLIFUFieldData(
name="Maximum acceptance angle",
description="Maximum acceptance angle for each element from the vector normal to the element surface",
units_field="units", display_units="deg", precision=1,
)] = 30.0
"""Maximum acceptance angle for each element from the vector normal to the element surface"""

units: Annotated[str, OpenLIFUFieldData("Angle units", "Angle units")] = "deg"
units: Annotated[str, OpenLIFUFieldData(
name="Angle units",
description="Angle units",
unit_options=("deg", "rad"),
)] = "deg"
"""Angle units"""

def __post_init__(self):
Expand Down Expand Up @@ -47,3 +56,7 @@ def to_table(self) -> pd.DataFrame:
records = [{"Name": "Type", "Value": "Max Angle", "Unit": ""},
{"Name": "Max Angle", "Value": self.max_angle, "Unit": self.units}]
return pd.DataFrame.from_records(records)

def get_summary(self) -> str:
"""Return a one-liner summary of the apodization parameters."""
return summarize_fields(self, ("max_angle",))
23 changes: 20 additions & 3 deletions src/openlifu/bf/apod_methods/piecewiselinear.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,32 @@
from openlifu.bf.apod_methods import ApodizationMethod
from openlifu.geo.point import Point
from openlifu.util.annotations import OpenLIFUFieldData
from openlifu.util.field_display import summarize_fields
from openlifu.util.units import getunittype
from openlifu.xdc import Transducer


@dataclass
class PiecewiseLinear(ApodizationMethod):
zero_angle: Annotated[float, OpenLIFUFieldData("Zero Apodization Angle", "Angle at and beyond which the piecewise linear apodization is 0%")] = 90.0
zero_angle: Annotated[float, OpenLIFUFieldData(
name="Zero apodization angle",
description="Angle at and beyond which the piecewise linear apodization is 0%",
units_field="units", display_units="deg", precision=1,
)] = 90.0
"""Angle at and beyond which the piecewise linear apodization is 0%"""

rolloff_angle: Annotated[float, OpenLIFUFieldData("Rolloff start angle", "Angle below which the piecewise linear apodization is 100%")] = 45.0
rolloff_angle: Annotated[float, OpenLIFUFieldData(
name="Rolloff start angle",
description="Angle below which the piecewise linear apodization is 100%",
units_field="units", display_units="deg", precision=1,
)] = 45.0
"""Angle below which the piecewise linear apodization is 100%"""

units: Annotated[str, OpenLIFUFieldData("Angle units", "Angle units")] = "deg"
units: Annotated[str, OpenLIFUFieldData(
name="Angle units",
description="Angle units",
unit_options=("deg", "rad"),
)] = "deg"
"""Angle units"""

def __post_init__(self):
Expand Down Expand Up @@ -60,3 +73,7 @@ def to_table(self) -> pd.DataFrame:
{"Name": "Rolloff Angle", "Value": self.rolloff_angle, "Unit": self.units},
]
return pd.DataFrame.from_records(records)

def get_summary(self) -> str:
"""Return a one-liner summary of the apodization parameters."""
return summarize_fields(self, ("zero_angle", "rolloff_angle"))
11 changes: 10 additions & 1 deletion src/openlifu/bf/apod_methods/uniform.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@
from openlifu.bf.apod_methods import ApodizationMethod
from openlifu.geo.point import Point
from openlifu.util.annotations import OpenLIFUFieldData
from openlifu.util.field_display import summarize_fields
from openlifu.xdc import Transducer


@dataclass
class Uniform(ApodizationMethod):
value: Annotated[float, OpenLIFUFieldData("Value", "Uniform apodization value between 0 and 1.")] = 1.0
value: Annotated[float, OpenLIFUFieldData(
name="Value",
description="Uniform apodization value between 0 and 1.",
precision=2,
)] = 1.0
"""Uniform apodization value between 0 and 1."""

def calc_apodization(self, arr: Transducer, target: Point, params: xa.Dataset, transform:np.ndarray | None=None):
Expand All @@ -30,3 +35,7 @@ def to_table(self):
records = [{"Name": "Type", "Value": "Uniform", "Unit": ""},
{"Name": "Value", "Value": self.value, "Unit": ""}]
return pd.DataFrame.from_records(records)

def get_summary(self) -> str:
"""Return a one-liner summary of the apodization parameters."""
return summarize_fields(self, ("value",))
11 changes: 10 additions & 1 deletion src/openlifu/bf/delay_methods/direct.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@
from openlifu.bf.delay_methods import DelayMethod
from openlifu.geo.point import Point
from openlifu.util.annotations import OpenLIFUFieldData
from openlifu.util.field_display import summarize_fields
from openlifu.xdc import Transducer


@dataclass
class Direct(DelayMethod):
c0: Annotated[float, OpenLIFUFieldData("Speed of Sound (m/s)", "Speed of sound in the medium (m/s)")] = 1480.0
c0: Annotated[float, OpenLIFUFieldData(
name="Speed of sound",
description="Speed of sound in the medium",
units="m/s", precision=0,
)] = 1480.0
"""Speed of sound in the medium (m/s)"""

def __post_init__(self):
Expand Down Expand Up @@ -46,3 +51,7 @@ def to_table(self) -> pd.DataFrame:
records = [{"Name": "Type", "Value": "Direct", "Unit": ""},
{"Name": "Default Sound Speed", "Value": self.c0, "Unit": "m/s"}]
return pd.DataFrame.from_records(records)

def get_summary(self) -> str:
"""Return a one-liner summary of the delay method."""
return summarize_fields(self, ("c0",))
Loading
Loading