Skip to content

Commit 28ba613

Browse files
authored
Merge pull request #24 from KitwareMedical/add-openigtlink-apps
ENH: Adds an option to build an OpenIGTLink server for RF data
2 parents c76bef7 + e8ab82f commit 28ba613

5 files changed

Lines changed: 365 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ include( CTest )
3131

3232
# Options
3333
option( BUILD_APPLICATIONS "Build applications" OFF )
34+
option( BUILD_SERVERS "Build OpenIGTLink Servers" OFF)
3435

3536
# External dependencies
3637
find_path( IntersonArraySDK_DIR
@@ -73,6 +74,10 @@ if( BUILD_APPLICATIONS )
7374
add_subdirectory( app )
7475
endif()
7576

77+
if( BUILD_SERVERS )
78+
add_subdirectory( openigtlink )
79+
endif()
80+
7681
# Install Interson libraries
7782
install( FILES ${IntersonArraySDK_LIBRARY}
7883
DESTINATION ${INSTALL_BIN_DIR} COMPONENT Runtime

openigtlink/CMakeLists.txt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
##############################################################################
2+
#
3+
# Library: IntersonArraySDK
4+
#
5+
# Copyright Kitware Inc. 28 Corporate Drive,
6+
# Clifton Park, NY, 12065, USA.
7+
#
8+
# All rights reserved.
9+
#
10+
# Licensed under the Apache License, Version 2.0 (the "License");
11+
# you may not use this file except in compliance with the License.
12+
# You may obtain a copy of the License at
13+
#
14+
# http://www.apache.org/licenses/LICENSE-2.0
15+
#
16+
# Unless required by applicable law or agreed to in writing, software
17+
# distributed under the License is distributed on an "AS IS" BASIS,
18+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19+
# See the License for the specific language governing permissions and
20+
# limitations under the License.
21+
#
22+
##############################################################################
23+
find_package( SlicerExecutionModel REQUIRED )
24+
include( ${SlicerExecutionModel_USE_FILE} )
25+
26+
find_package(OpenIGTLink REQUIRED)
27+
include(${OpenIGTLink_USE_FILE})
28+
29+
30+
SEMMacroBuildCLI(
31+
NAME IntersonArrayServerRF
32+
TARGET_LIBRARIES ${ITK_LIBRARIES}
33+
IntersonArrayCxx
34+
OpenIGTLink
35+
INCLUDE_DIRECTORIES ${PROJECT_SOURCE_DIR}/include
36+
${PROJECT_BINARY_DIR}/include
37+
)
38+
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
/*=========================================================================
2+
3+
Library: IntersonArray
4+
5+
Copyright Kitware Inc. 28 Corporate Drive,
6+
Clifton Park, NY, 12065, USA.
7+
8+
All rights reserved.
9+
10+
Licensed under the Apache License, Version 2.0 ( the "License" );
11+
you may not use this file except in compliance with the License.
12+
You may obtain a copy of the License at
13+
14+
http://www.apache.org/licenses/LICENSE-2.0
15+
16+
Unless required by applicable law or agreed to in writing, software
17+
distributed under the License is distributed on an "AS IS" BASIS,
18+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19+
See the License for the specific language governing permissions and
20+
limitations under the License.
21+
22+
=========================================================================*/
23+
#include <Windows.h> // Sleep
24+
25+
#include "IntersonArrayCxxImagingContainer.h"
26+
#include "IntersonArrayCxxControlsHWControls.h"
27+
#include "igtlImageMessage.h"
28+
#include "igtlServerSocket.h"
29+
30+
#include "IntersonArrayServerRFCLP.h"
31+
32+
typedef IntersonArrayCxx::Imaging::Container ContainerType;
33+
typedef ContainerType::RFImagePixelType PixelType;
34+
bool running;
35+
36+
BOOL WINAPI consoleHandler(DWORD signal) {
37+
38+
if (signal == CTRL_C_EVENT)
39+
{
40+
running = false;
41+
}
42+
43+
return TRUE;
44+
}
45+
46+
struct CallbackClientData
47+
{
48+
int FrameIndex;
49+
igtl::Socket * socket;
50+
igtl::ImageMessage * imgMsg;
51+
igtl::TimeStamp * ts;
52+
};
53+
54+
void __stdcall pasteIntoImage( PixelType * buffer, void * clientData )
55+
{
56+
CallbackClientData * callbackClientData =
57+
static_cast< CallbackClientData * >( clientData );
58+
59+
igtl::ImageMessage * message = callbackClientData->imgMsg;
60+
igtl::Socket * socket = callbackClientData->socket;
61+
igtl::TimeStamp * ts = callbackClientData->ts;
62+
63+
const int framePixels = ContainerType::MAX_RFSAMPLES * ContainerType::NBOFLINES;
64+
++(callbackClientData->FrameIndex);
65+
66+
message->SetMessageID(callbackClientData->FrameIndex);
67+
ts->GetTime();
68+
igtlUint32 sec;
69+
igtlUint32 nsec;
70+
ts->GetTimeStamp(&sec, &nsec);
71+
message->SetTimeStamp(sec, nsec);
72+
73+
std::memcpy(message->GetScalarPointer(), buffer, framePixels * sizeof(PixelType));
74+
75+
if (!socket)
76+
{
77+
return;
78+
}
79+
80+
message->Pack();
81+
socket->Send(message->GetPackPointer(), message->GetPackSize());
82+
}
83+
84+
int main( int argc, char * argv[] )
85+
{
86+
PARSE_ARGS;
87+
88+
typedef IntersonArrayCxx::Controls::HWControls HWControlsType;
89+
IntersonArrayCxx::Controls::HWControls hwControls;
90+
91+
int ret = EXIT_SUCCESS;
92+
93+
int steering = 0;
94+
typedef HWControlsType::FoundProbesType FoundProbesType;
95+
FoundProbesType foundProbes;
96+
hwControls.FindAllProbes( foundProbes );
97+
if( foundProbes.empty() )
98+
{
99+
std::cerr << "Could not find the probe." << std::endl;
100+
return EXIT_FAILURE;
101+
}
102+
hwControls.FindMyProbe( 0 );
103+
const unsigned int probeId = hwControls.GetProbeID();
104+
if( probeId == 0 )
105+
{
106+
std::cerr << "Could not find the probe." << std::endl;
107+
return EXIT_FAILURE;
108+
}
109+
110+
HWControlsType::FrequenciesType frequencies;
111+
hwControls.GetFrequency( frequencies );
112+
113+
HWControlsType::FocusType focuses;
114+
hwControls.GetFocus(focuses);
115+
116+
if( !hwControls.SetFrequencyAndFocus( frequencyIndex, focusIndex,
117+
steering ) )
118+
{
119+
std::cerr << "Could not set the frequency and focus." << std::endl;
120+
return EXIT_FAILURE;
121+
}
122+
123+
if( !hwControls.SendHighVoltage( highVoltage, highVoltage ) )
124+
{
125+
std::cerr << "Could not set the high voltage." << std::endl;
126+
return EXIT_FAILURE;
127+
}
128+
if( !hwControls.EnableHighVoltage() )
129+
{
130+
std::cerr << "Could not enable high voltage." << std::endl;
131+
return EXIT_FAILURE;
132+
}
133+
134+
hwControls.DisableHardButton();
135+
ContainerType container;
136+
container.SetHWControls(&hwControls);
137+
138+
const int height_lines = hwControls.GetLinesPerArray();
139+
std::cout << "Lines per array: " << height_lines << std::endl;
140+
const int width_samples = ContainerType::MAX_RFSAMPLES;
141+
std::cout << "Max RF samples: " << width_samples << std::endl;
142+
const double ns = ContainerType::MAX_RFSAMPLES; // number of samples
143+
const double fs = 30000; // [kHz]=[samples/ms] - sampling frequency
144+
const double depth = sos * ( ns - 1 ) / ( 2 * fs );
145+
const double depthCfm = depth/2;
146+
std::cout << "Depth: " << depth << "mm" << std::endl;
147+
std::cout << std::endl;
148+
149+
container.SetRFData( true );
150+
container.IdleInitScanConverter( depth, width_samples, height_lines, probeId,
151+
steering, depthCfm, false, false, 0, false );
152+
container.HardInitScanConverter( depth, width_samples, height_lines, steering,
153+
depthCfm );
154+
155+
CallbackClientData clientData;
156+
clientData.FrameIndex = 0;
157+
158+
// size parameters
159+
const short frameRate = hwControls.GetProbeFrameRate();
160+
int size[] = { ContainerType::MAX_RFSAMPLES, ContainerType::NBOFLINES, 1 }; // image dimension
161+
float spacing[] = { sos / (2 * fs), 38.0 / (height_lines - 1), 1.0 / frameRate }; // spacing (mm/pixel)
162+
int svsize[] = { ContainerType::MAX_RFSAMPLES, ContainerType::NBOFLINES, 1 }; // sub-volume size
163+
int svoffset[] = { 0, 0, 0 }; // sub-volume offset
164+
int scalarType = igtl::ImageMessage::TYPE_INT16;// scalar type
165+
166+
//------------------------------------------------------------
167+
// Create a new IMAGE type message
168+
igtl::ImageMessage::Pointer imgMsg = igtl::ImageMessage::New();
169+
imgMsg->SetDimensions(size);
170+
imgMsg->SetSpacing(spacing);
171+
imgMsg->SetScalarType(scalarType);
172+
imgMsg->SetDeviceName("IntersonRF");
173+
imgMsg->SetSubVolume(svsize, svoffset);
174+
imgMsg->SetHeaderVersion(IGTL_HEADER_VERSION_2);
175+
IANA_ENCODING_TYPE codingScheme = IANA_TYPE_US_ASCII;
176+
177+
//Add meta data here
178+
imgMsg->SetMetaDataElement("Frequency (MHz)", codingScheme, std::to_string(frequencies[frequencyIndex]));
179+
imgMsg->SetMetaDataElement("Focus (mm)", codingScheme, std::to_string(focuses[focusIndex]));
180+
imgMsg->SetMetaDataElement("High voltage", codingScheme, std::to_string(highVoltage));
181+
imgMsg->SetMetaDataElement("SOS", codingScheme, std::to_string(sos));
182+
imgMsg->SetMetaDataElement("FPS", codingScheme, std::to_string(frameRate));
183+
imgMsg->SetMetaDataElement("Steering", codingScheme, std::to_string(steering));
184+
imgMsg->SetMetaDataElement("Image Type", codingScheme, "RF");
185+
imgMsg->SetMetaDataElement("Timestamp seconds", codingScheme, "0");
186+
imgMsg->SetMetaDataElement("Timestamp nseconds", codingScheme, "0");
187+
188+
imgMsg->AllocateScalars();
189+
imgMsg->SetEndian(igtl::ImageMessage::ENDIAN_LITTLE);
190+
igtl::Matrix4x4 matrix;
191+
matrix[0][0] = 0.0; matrix[1][0] = -1.0; matrix[2][0] = 0.0; matrix[3][0] = 0.0;
192+
matrix[0][1] = 1.0; matrix[1][1] = 0.0; matrix[2][1] = 0.0; matrix[3][1] = 0.0;
193+
matrix[0][2] = 0.0; matrix[1][2] = 0.0; matrix[2][2] = 1.0; matrix[3][2] = 0.0;
194+
matrix[0][3] = 0.0; matrix[1][3] = 0.0; matrix[2][3] = 0.0; matrix[3][3] = 1.0;
195+
imgMsg->SetMatrix(matrix);
196+
igtl::TimeStamp::Pointer ts = igtl::TimeStamp::New();
197+
clientData.ts = ts.GetPointer();
198+
clientData.imgMsg = imgMsg.GetPointer();
199+
200+
//socket setup
201+
igtl::ServerSocket::Pointer serverSocket;
202+
serverSocket = igtl::ServerSocket::New();
203+
int r = serverSocket->CreateServer(port);
204+
205+
if (r < 0)
206+
{
207+
std::cerr << "Cannot create a server socket." << std::endl;
208+
exit(0);
209+
}
210+
std::cerr << "Created Server socket." << std::endl;
211+
212+
213+
igtl::Socket::Pointer socket;
214+
clientData.socket = socket;
215+
216+
container.SetNewRFImageCallback( &pasteIntoImage, &clientData );
217+
218+
std::cout << "StartRFReadScan" << std::endl;
219+
container.StartRFReadScan();
220+
Sleep( 100 ); // "time to start"
221+
std::cout << "StartRFmode" << std::endl;
222+
if( !hwControls.StartRFmode() )
223+
{
224+
std::cerr << "Could not start RF collection." << std::endl;
225+
return EXIT_FAILURE;
226+
}
227+
228+
if (!SetConsoleCtrlHandler(consoleHandler, TRUE))
229+
{
230+
std::cout << "Could not set control handler" << std::endl;
231+
return 1;
232+
}
233+
running = true;
234+
std::cout << "Server is running, use Ctrl-C to stop" << std::endl;
235+
while( running )
236+
{
237+
//------------------------------------------------------------
238+
// Waiting for Connection
239+
socket = serverSocket->WaitForConnection(1000);
240+
clientData.socket = socket.GetPointer();
241+
if (socket.IsNotNull()) // if client connected
242+
{
243+
Sleep(10000); //chill for a bit
244+
}
245+
246+
}
247+
248+
std::cout << "Shutting down" << std::endl;
249+
hwControls.StopAcquisition();
250+
container.StopReadScan();
251+
if (socket)
252+
{
253+
socket->CloseSocket();
254+
}
255+
256+
Sleep( 100 ); // "time to stop"
257+
return ret;
258+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<executable>
3+
<category>Ultrasound</category>
4+
<title>Interson RF OpenIGTLink Server</title>
5+
<description>Braodcast RF images over OpenIGTLink.</description>
6+
<version>0.1.0</version>
7+
<license>Apache 2.0</license>
8+
<contributor>Sam Horvath (Kitware), Matt McCormick (Kitware)</contributor>
9+
<acknowledgements>This work is funded in part by a grant with InnerOptic/Kitware</acknowledgements>
10+
<parameters>
11+
<integer>
12+
<name>port</name>
13+
<label>Port Number</label>
14+
<description>Port for OpenIGTLink Server.</description>
15+
<longflag>port</longflag>
16+
<flag>p</flag>
17+
<default>18944</default>
18+
</integer>
19+
<integer>
20+
<name>frequencyIndex</name>
21+
<label>Frequency Index</label>
22+
<description>Index of the frequency to examine. See the output of PrintIntersonProbeInfo.</description>
23+
<longflag>frequencyIndex</longflag>
24+
<flag>f</flag>
25+
<default>1</default>
26+
</integer>
27+
<integer>
28+
<name>focusIndex</name>
29+
<label>Focus Index</label>
30+
<description>Index of the focus to examine. See the output of PrintIntersonProbeInfo.</description>
31+
<longflag>focusIndex</longflag>
32+
<flag>F</flag>
33+
<default>1</default>
34+
</integer>
35+
<integer>
36+
<name>highVoltage</name>
37+
<label>High Voltage</label>
38+
<description>Percentage of the high voltage for transducer excitation.</description>
39+
<longflag>highVoltage</longflag>
40+
<flag>z</flag>
41+
<default>50</default>
42+
<minimum>0</minimum>
43+
<maximum>100</maximum>
44+
</integer>
45+
<double>
46+
<name>sos</name>
47+
<label>Speed of sound</label>
48+
<description> Assumed speed of sound in the crossed medium in mm/ms.</description>
49+
<longflag>sos</longflag>
50+
<default>1540</default>
51+
</double>
52+
</parameters>
53+
</executable>

src/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,14 @@ if( BUILD_APPLICATIONS )
9797
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:IntersonArrayCxx>" "${PROJECT_BINARY_DIR}/app/bin"
9898
)
9999
endif()
100+
101+
if( BUILD_SERVERS )
102+
add_custom_command( TARGET IntersonArrayCxx
103+
POST_BUILD
104+
COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_BINARY_DIR}/openigtlink/bin/$<CONFIG>"
105+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${IntersonArraySDK_DIR}/Libraries/IntersonArray.dll" "${PROJECT_BINARY_DIR}/openigtlink/bin/$<CONFIG>"
106+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:IntersonArrayCxx>" "${PROJECT_BINARY_DIR}/openigtlink/bin/$<CONFIG>"
107+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${IntersonArraySDK_DIR}/Libraries/IntersonArray.dll" "${PROJECT_BINARY_DIR}/openigtlink/bin"
108+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:IntersonArrayCxx>" "${PROJECT_BINARY_DIR}/openigtlink/bin"
109+
)
110+
endif()

0 commit comments

Comments
 (0)