tutorials.5 - moduleus/urx GitHub Wiki

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