Tutorial - Transmit Setup
Python
import numpy as np
import ultrasound_acquisition_configuration as uac
#### FUNCTIONS #####
def makePlaneWaveTransmitSetupUac(theta, voltage, nPeriods, transmitFrequency, excitationSamplingFrequency, maxDelay, soundSpeed, Probe, Excitation=None):
"""
makePlaneWaveTransmitSetupUac creates a plane waves transmit setup with a linear array in uac format
Inputs:
theta : plane wave angle of the event [°]
voltage : voltage of the excitation [Vpeak]
nPeriods : number of periods of transmitted signal
transmitFrequency : central frequency of the transmitted signal [Hz]
excitationSamplingFrequency : sampling frequency of the excitation [Hz]
soundSpeed : celerity of the sound in the medium [m/s]
Probe (uac.Probe) : ultrasound probe used for the transmit event
Excitation (uac.Excitation) : (optional) excitation reused from another transmit event
Outputs:
TransmitSetup (uac.TransmitSetup) : built for plane wave transmit event with a linear array probe
Excitation (uac.Excitation) : excitation to be used in another transmit event
Requirements:
urx 1.1.0 toolbox
uac 1.1.0 toolbox
More information:
see : https://github.com/moduleus/urx/wiki
"""
## Delay
nElements = len(Probe.elements) # number of probe's elements
pitch = Probe.elements[1].transform.translation.x - Probe.elements[0].transform.translation.x # pitch of the probe
delays = np.sin(np.deg2rad(theta)) * (np.arange(-(nElements-1)/2, (nElements-1)/2 + 1)) * pitch / soundSpeed # linear delay along the element to make the plane wave
## Wave
Wave = uac.Wave()
Wave.type = uac.WaveType.PLANE_WAVE # 0: CONVERGING_WAVE, 1: DIVERGING_WAVE, 2:PLANE_WAVE, 3:CYLINDRICAL_WAVE, -1: UNDEFINED
Wave.parameters = [np.sin(np.deg2rad(theta)), 0, np.cos(np.deg2rad(theta))] # defines the plane wave with the normal vector
Wave.time_zero = maxDelay + nPeriods / transmitFrequency / 2 # default value, reference point for the time_zero
## Excitation
if Excitation is None: # if no Excitation input to be reused, definition of the Excitation
Excitation = uac.Excitation()
Excitation.pulse_shape = "square wave" # description of the pulse shape
Excitation.transmit_frequency = transmitFrequency
Excitation.sampling_frequency = excitationSamplingFrequency
nSamplesExcitation = round(excitationSamplingFrequency / transmitFrequency / 2) # number of samples for a half period of excitation
if nSamplesExcitation != int(nSamplesExcitation): # excitationSamplingFrequency may not allow transmitFrequency so it is corrected
print(f'(Warning) Real transmit frequency: {excitationSamplingFrequency / nSamplesExcitation / 2} Hz') # warn if frequency has been changed
waveform = voltage * np.tile(np.concatenate((np.ones(nSamplesExcitation),-np.ones(nSamplesExcitation))), (1,nPeriods))
Excitation.waveform = uac.VecFloat64(waveform.ravel()) # square excitation waveform
## TransmitSetup
TransmitSetup = uac.TransmitSetup()
TransmitSetup.wave = Wave
TransmitSetup.active_elements = [[i] for i in range(0, nElements)] # active elements (arrays dimension) per excitations (cells dimension)
TransmitSetup.excitations = [Excitation] * nElements # shared excitation for all the elements
TransmitSetup.delays = uac.VecFloat64(delays.ravel())
TransmitSetup.probe = Probe
return TransmitSetup, Excitation
def makeLinearArrayUac(nElements, pitch, width, height, description):
"""
makeLinearArrayUac.py creates a linear array probe in uac format
Inputs:
nElements : number of elements (transducers) in the probe
pitch : distance between the center of adjacent elements [m]
width : size of the element along the x-axis [m]
height : size of the element along the y-axis [m]
description : name or description of the probe
Outputs:
Probe (uac.Probe) : built as a linear array along the x-axis
Drawing:
Linear array (nElements = 12)
___ pitch __ width
o--->x __ __ __ __ __ __ __ __ __ __ __ __
/| / // // // //+//+// // // // // // / /
/ | /_//_//_//_//_//_//_//_//_//_//_//_/ / height
y z
+--+ perimeter of and element with + as elements' edges
/ / defined either clockwise or counterclockwise
+--+
Requirements:
urx 1.1.0 toolbox
uac 1.1.0 toolbox
More information:
see : https://github.com/moduleus/urx/wiki
"""
## Element's geometry : all the elements share the same impulse response
edges = np.array([ # x, y z coordinates (dimension 1) of the 4 edges (dimensions 2)
[-width / 2, -width / 2, width / 2, width / 2],
[-height / 2, height / 2, height / 2, -height / 2],
[0, 0, 0, 0]
])
ElementGeometry = uac.ElementGeometry()
ElementGeometry.perimeter = [ # perimeter of one element
uac.Vector3D(edges[0, 0], edges[1, 0], edges[2, 0]),
uac.Vector3D(edges[0, 1], edges[1, 1], edges[2, 1]),
uac.Vector3D(edges[0, 2], edges[1, 2], edges[2, 2]),
uac.Vector3D(edges[0, 3], edges[1, 3], edges[2, 3])
]
## (Optional) Impulse response : all the elements share the same impulse response
ImpulseResponse = uac.ImpulseResponse()
ImpulseResponse.sampling_frequency = 50e6 # sampling frequency of the impulse response data [Hz]
ImpulseResponse.time_offset = 0 # delay before impulse reponse starts [s]
ImpulseResponse.units = "N/A" # unit of the impulse response
ImpulseResponse.data = [ # impulse response depending on time
0.0000, 0.0467, 0.1641, 0.2780, 0.2521, 0.0000,
-0.4160, -0.7869, -0.8756, -0.5759, -0.0000, 0.5759,
0.8756, 0.7869, 0.4160, 0.0000, -0.2521, -0.2780
]
## Build elements' array
Elements = uac.VecElement()
xmin = -pitch * (nElements - 1) / 2 # first element center position
for i in range(0, nElements): # loop on the elements
x = xmin + i * pitch # computes all the position element
Element = uac.Element()
Element.transform.translation = uac.Vector3D(x, 0, 0) # elements are only along x-axis
Element.element_geometry = ElementGeometry # shared geometry between elements
Element.impulse_response = ImpulseResponse # shared impulse response between elements
Elements.append(Element) # adds elements to the array
## Build the probe
Probe = uac.Probe()
Probe.description = description
Probe.type = uac.ProbeType.LINEAR # 0: LINEAR, 1: CURVILINEAR, 2: MATRIX, 3: RCA, 4: SPARSE, -1: UNDEFINED
Probe.impulse_responses = [ImpulseResponse] # could store different impulse responses
Probe.element_geometries = [ElementGeometry] # could store different element's geometry
Probe.elements = Elements # set all the elements once to avoid repetitive call to Probe
return Probe
#### MAIN #####
## Transmit parameters
Theta = 3 # angle of the plane wave [°]
ThetaMax = 5 # maximum plane wave angle of the sequence [°]
TransmitFrequency = 10e6 # transmit frequency of the pulse [Hz]
ExcitationSamplingFrequency = 200e6 # excitation definition sampling frequency [Hz]
Voltage = 25 # voltage of the excitation Vpeak [V]
NPeriods = 2 # number of transmitted periods
SoundSpeed = 1540 # celerity of the sound in the medium [m/s]
## Probe parameters
ProbeDescription = 'SL10-2 192 elements' # description or name of the probe
NElements = 192 # number of transducers in the ultrasound probe
Pitch = 300e-6 # pitch (spatial step) of the array [m]
Width = 280e-6 # width of a single transducer [m]
Height = 6e-3 # height of a single transducer [m]
Kerf = Pitch - Width # space between adjacent transducer elements [m]
## Probe
Probe = makeLinearArrayUac(NElements, Pitch, Width, Height, ProbeDescription) # build the probe
## Transmit
maxDelays = -min(np.sin(np.deg2rad(ThetaMax)) * (np.arange(-(NElements-1)/2, (NElements-1)/2 + 1)) * Pitch / SoundSpeed)
[transmitSetup, excitation] = makePlaneWaveTransmitSetupUac(Theta, Voltage, NPeriods, TransmitFrequency, ExcitationSamplingFrequency, maxDelays, SoundSpeed, Probe)
MATLAB
clear all
%% Transmit parameters
Theta = 3; % angle of the plane wave [°]
ThetaMax = 5; % maximum plane wave angle of the sequence [°]
TransmitFrequency = 10e6; % transmit frequency of the pulse [Hz]
ExcitationSamplingFrequency = 200e6; % excitation definition sampling frequency [Hz]
Voltage = 25; % voltage of the excitation Vpeak [V]
NPeriods = 2; % number of transmitted periods
SoundSpeed = 1540; % celerity of the sound in the medium [m/s]
%% Probe parameters
ProbeDescription = 'SL10-2 192 elements'; % description or name of the probe
NElements = 192; % number of transducers in the ultrasound probe
Pitch = 300e-6; % pitch (spatial step) of the array [m]
Width = 280e-6; % width of a single transducer [m]
Height = 6e-3; % height of a single transducer [m]
Kerf = Pitch - Width; % space between adjacent transducer elements [m]
%% Probe
Probe = makeLinearArrayUac(NElements, Pitch, Width, Height, ProbeDescription); % build the probe
%% Transmit
maxDelays = -min(sind(ThetaMax) * (-(NElements-1)/2:(NElements-1)/2) * Pitch / SoundSpeed);
[transmitSetup, excitation] = makePlaneWaveTransmitSetupUac(Theta, Voltage, NPeriods, TransmitFrequency, ExcitationSamplingFrequency, maxDelays, SoundSpeed, Probe);
%% Helpers
function [TransmitSetup, Excitation] = makePlaneWaveTransmitSetupUac(theta, voltage, nPeriods, transmitFrequency, excitationSamplingFrequency, maxDelay, soundSpeed, Probe, Excitation)
%makePlaneWaveTransmitSetupUac.m Creates a plane waves transmit setup with a linear array in uac format
% Intputs:
% theta : plane wave angle of the event [°]
% voltage : voltage of the excitation [Vpeak]
% nPeriods : number of periods of transmitted signal
% transmitFrequency : central frequency of the transmitted signal [Hz]
% excitationSamplingFrequency : sampling frequency of the excitation [Hz]
% soundSpeed : celerity of the sound in the medium [m/s]
% Probe (uac.Probe) : ultrasound probe used for the transmit event
% Excitation (uac.Excitation) : (optional) excitation reused from another transmit event
%
% Outputs:
% TransmitSetup (uac.TransmitSetup) : built for plane wave transmit event with a linear array probe
% Excitation (uac.Excitation) : excitation to be used in another transmit event
%
% For more information see : https://github.com/moduleus/urx/wiki
%
% Requirements: uac 1.1.0 toolbox
%% Plane wave delay
nElements = length(Probe.elements); % number of probe's elements
pitch = Probe.elements(2).transform.translation.x-Probe.elements(1).transform.translation.x; % pitch of the probe
delays = sind(theta) * (-(nElements-1)/2:(nElements-1)/2) * pitch / soundSpeed; % linear delay along the element to make the plane wave
%% Wave
Wave = uac.Wave();
Wave.type = uac.Wave.WaveType.PLANE_WAVE; % 0: CONVERGING_WAVE, 1: DIVERGING_WAVE, 2:PLANE_WAVE, 3:CYLINDRICAL_WAVE, -1: UNDEFINED
Wave.parameters = [sind(theta), 0, cosd(theta)]; % defines the plane wave with the normal vector
% Wave.timeZeroReferencePoint = uac.Vector3D(); % default value, reference point for the timeZero
Wave.timeZero = maxDelay + nPeriods/transmitFrequency/2; % delay to avoid negative time in the plane waves' delays
%% Excitation
if ~exist('Excitation','var') % if no Excitation input to be reused, definition of the Excitation
Excitation = uac.Excitation();
Excitation.pulseShape = "square wave"; % description of the pulse shape
Excitation.transmitFrequency = transmitFrequency;
Excitation.samplingFrequency = excitationSamplingFrequency;
nSamplesExcitation = round(excitationSamplingFrequency/transmitFrequency/2); % number of samples for a half period of excitation
if nSamplesExcitation ~= floor(nSamplesExcitation) % excitationSamplingFrequency may not allow transmitFrequency so it is corrected
warning(['Real transmit frequency:', num2str(excitationSamplingFrequency/nSamplesExcitation/2)]); % warn if frequency has been changed
end
Excitation.waveform = voltage * repmat([ones(1,nSamplesExcitation), - ones(1,nSamplesExcitation)], [1,nPeriods]); % square excitation waveform
end
%% TransmitSetup
TransmitSetup = uac.TransmitSetup();
TransmitSetup.wave = Wave;
TransmitSetup.activeElements = num2cell(0:nElements-1); % active elements (arrays dimension) per excitations (cells dimension), starting at 0
TransmitSetup.excitations = repmat(Excitation, [1, nElements]); % shared excitation for all the elements
TransmitSetup.delays = delays;
TransmitSetup.probe = Probe;
% TransmitSetup.probeTransform.translation = uac.Vector3D(); % default value, position of the probe for this transmit event
% TransmitSetup.probeTransform.rotation = uac.Vector3D(); % default value, rotation of the probe for this transmit event
% TransmitSetup.timeOffset = 0; % default value, delay before this transmit event
% TransmitSetup.hwConfig; % not used here, store hardware configuration
end
%
function Probe = makeLinearArrayUac(nElements, pitch, width, height, description)
%makeLinearArrayUac.m Creates a linear array probe in uac format
% Inputs:
% nElements : number of elements (transducers) in the probe
% pitch : distance between the center of adjacent elements [m]
% width : size of the element along the x-axis [m]
% height : size of the element along the y-axis [m]
% description : name or description of the probe
%
% Outputs:
% Probe (uac.Probe) : built as a linear array along the x-axis
%
% Drawing: Linear array (nElements = 12)
% __ width
% ____ x __ __ __ __ __ __ __ __ __ __ __ __
% /| / // // // // // // // // // // // / /
% / | /_//_//_//_//_//_//_//_//_//_//_//_/ / height
% y z ___ pitch
%
% +--+ perimeter of and element with + as elements edges
% / / defined either clockwise or counterclockwise
% +--+
%
% For more information see : https://github.com/moduleus/urx/wiki
%
% Requirements: uac 1.1.0 toolbox
%% Element's geometry : all the elements share the same impulse response
edges = [ -width/2, -width/2, width/2, width/2; ... % x, y z coordinates (dimension 1) of the 4 edges (dimensions 2)
-height/2, height/2, height/2, -height/2; ...
0, 0, 0, 0];
ElementGeometry = uac.ElementGeometry();
ElementGeometry.perimeter = [ uac.Vector3D(edges(1,1), edges(2,1), edges(3,1)),... % perimeter of one element
uac.Vector3D(edges(1,2), edges(2,2), edges(3,2)),...
uac.Vector3D(edges(1,3), edges(2,3), edges(3,3)),...
uac.Vector3D(edges(1,4), edges(2,4), edges(3,4))];
%% (Optional) Impulse response : all the elements share the same impulse response
ImpulseResponse = uac.ImpulseResponse();
ImpulseResponse.samplingFrequency = 50e6; % sampling frequency of the impulse response data [Hz]
ImpulseResponse.timeOffset = 0; % delay before impulse reponse starts [s]
ImpulseResponse.units = "N/A"; % unit of the impulse response
ImpulseResponse.data = [ 0.0000, 0.0467, 0.1641, 0.2780, 0.2521, 0.0000,... % impulse response depending on time
-0.4160, -0.7869, -0.8756, -0.5759, -0.0000, 0.5759,...
0.8756, 0.7869, 0.4160, 0.0000, -0.2521, -0.2780];
%% Build the elements' array
Elements = uac.Element(); % defines the first element ...
xmin = -pitch * (nElements - 1) / 2; % first element center position
Elements.transform.translation = uac.Vector3D(xmin, 0, 0); % set in the transform translation
% Elements.transform.rotation = uac.Vector3D(); % default value
Elements.elementGeometry = ElementGeometry; % set element's Geometry
Elements.impulseResponse = ImpulseResponse; % set element's impulse response (optional)
% Loop on the other elements % ... then increments this element with a loop
for i = 2:nElements
x = xmin + (i - 1) * pitch; % computes all the position element
Element = uac.Element();
Element.transform.translation = uac.Vector3D(x, 0, 0); % elements are only along x-axis
% Element.transform.rotation = uac.Vector3D(); % default value, rotation of the element
Element.elementGeometry = ElementGeometry; % shared geometry between elements
Element.impulseResponse = ImpulseResponse; % shared impulse response between elements
Elements = [Elements, Element]; % appends elements to the array
end
%% Build the probe
Probe = uac.Probe();
Probe.description = description;
Probe.type = uac.Probe.ProbeType.LINEAR; % 0: LINEAR, 1: CURVILINEAR, 2: MATRIX, 3: RCA, 4: SPARSE, -1: UNDEFINED
% Probe.transform.rotation = uac.Vector3D(); % default value,
% Probe.transform.translation = uac.Vector3D(); % default value
Probe.impulseResponses = ImpulseResponse; % could be an array in case of different impulse responses
Probe.elementGeometries = ElementGeometry; % could be an array in case of different elements geometries
Probe.elements = Elements; % set all the elements once to avoid repetitive call to Probe
end
C++
// TODO