Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e2e4f2f
Load transducer arrays from connected module configurations (#440)
peterhollender May 26, 2026
c464746
Add rich representations for transducer models (#481)
peterhollender May 26, 2026
06bac4d
Normalize transducer standoff transforms (#440)
peterhollender May 26, 2026
6c2f85e
Honor device-stored transducer array configurations (#440)
peterhollender May 27, 2026
3dff914
Add connected-module version inspection script (#74)
peterhollender May 27, 2026
9535956
Warn when connected arrays differ from database definitions (#440)
peterhollender May 27, 2026
c530c10
Select cloud endpoints by environment (#482)
posnova Mar 6, 2026
d51294f
Stop synchronizing obsolete Systems resources (#425)
peterhollender Jun 1, 2026
4106b0d
Add display metadata and model summaries (#481)
peterhollender Jun 5, 2026
9611d43
Sanitize stale photoscan references when loading sessions (#467)
peterhollender Jun 10, 2026
de4b680
Derive solution analysis regions from beam geometry (#468)
peterhollender Jun 10, 2026
0e10d82
Report thermal model validity at debug level (#422)
peterhollender Jun 10, 2026
ab78a92
Persist solution analyses and session solution references (#484)
peterhollender Jun 18, 2026
e80b046
Separate photoscan registrations from tracking results (#483)
peterhollender Jun 26, 2026
de765ee
Declare minimum dependency versions (#337)
peterhollender Jul 14, 2026
baadec4
Allow NumPy 2 and newer ONNX Runtime releases (#438)
peterhollender Jul 14, 2026
a0f9636
Fix style for linter (#337)
ebrahimebrahim Jul 17, 2026
35d0fe5
update ISPTA for multiple foci
peterhollender Jul 23, 2026
d8b285e
Add Session.solutions list of SolutionInfo provenance records (https:…
peterhollender Jul 23, 2026
02f7785
Further update ISPTA for rastering
peterhollender Jul 23, 2026
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
102 changes: 102 additions & 0 deletions notebooks/get_all_versions.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this file meant to be included in this PR? If so maybe it belongs elsewhere like examples/tools

Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from __future__ import annotations

import logging
import os
import sys
import time

if os.name == 'nt':
pass
else:
pass


from openlifu.io.LIFUInterface import LIFUInterface

# 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(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}")
70 changes: 34 additions & 36 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]",
"numpy<2",
"pandas",
"scipy",
"requests",
"xarray[io]>=2026.2.0",
"numpy>=2.0.0",
"pandas>=3.0.2",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peterhollender quick question as I am starting to look over this: do you need all these version pins? do you need this new of a pandas? this would force us to drop Python 3.10

"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",
"onnxruntime==1.18.0",
"trimesh",
"scikit-image",
"vtk",
"Pillow",
"OpenEXR"
"embreex>=2.17.7.post7; platform_machine=='x86_64' or platform_machine=='AMD64'",
"opencv-contrib-python>=4.11.0.86",
"onnxruntime>=1.20.0",
"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