Saturday, June 19, 2010

Non-blocking Output Socket

My MATLAB TCP/IP example has been quite popular, and I myself use it at least once a week on various applications. However, one of the main limitations when compared to our C++ style output sockets was the limitation of only 1 client per socket.

What was worse, is that I knew it COULD be done. I had just not found the time to write it! Cue a spare Saturday!


The Problem

As per my previous server.m examples, you begin by constructing a Java ServerSocket.

>> server_socket = ServerSocket(output_port); 
>> server_socket.setSoTimeout(1000);

This attempts to bind the ServerSocket to the specified port. HOWEVER, it is not listening for incoming connections! To do this, you must tell the ServerSocket to accept().

>> output_socket = server_socket.accept();

This is a BLOCKING call. That is, the code will wait for either the timeout (set above) to occur, or a client to connect.

For a lot of what I do, the output socket exists mainly as a debugging / monitoring interface. So I don't even care if noone is listening. So what we need is something that manages the connection of clients and the distribution of any written data to each of them.

Enter OutputSocket and ListeningThread.

OutputSocket

OutputSocket provides a very simple interface:

//--------------------------------------------------------------------- 
// Description : Create an OutputSocket on the specified port

//---------------------------------------------------------------------

public
OutputSocket(int port) throws IOException

//---------------------------------------------------------------------
// Description : Write the supplied byte array to all connected clients

//---------------------------------------------------------------------

public void
write(byte[] data)


//---------------------------------------------------------------------

// Description : Close the OutputSocket. This closes all client

// connections and stops listening for new connections.
//---------------------------------------------------------------------

public void
close()



ListeningThread

The OutputSocket utilises ListeningThread to manage the accepting of new connections. ListeningThread implements the Runnable interface so can be started in its own thread (vital!).

//-------------------------------------------------------------------------
// Description : Helper class that listens for new TCP/IP client
// connections on a ServerSocket
//
// Parameters : server_socket - ServerSocket to listen on
// connection_list - List of connected client
// Sockets to update
// connection_stream_list - List of connected client
// DataOutputStreams to update
//---------------------------------------------------------------------
public ListeningThread(ServerSocket server_socket,
List connection_list,
List connection_stream_list)


//---------------------------------------------------------------------
// Description : Called on Thread.start()
//---------------------------------------------------------------------
public void
run()


//---------------------------------------------------------------------
// Description : Stop the thread
//---------------------------------------------------------------------
public void
stop()



Interaction

The OutputSocket constructor creates the ServerSocket as before and stores it as a member variable. It also creates 2 empty lists, one for the Socket client connections and one for the associated DataOutputStreams used to write to them.

It supplies a reference to these to the ListeningThread on construction and then starts it running in its own Thread. This is the important part. OutputSocket and ListeningThread both share a reference to the DataOutputStream list, so as ListeningThread creates connections and output streams, these are pushed onto the client list.

When OutputSocket::write() is called, it writes the data across all output streams (be there 0 or 100!)

When you have finished with the OutputSocket, a call to close() will stop the ListeningThread from running and close all client connections.

Usage from within MATLAB

Place the .class files in a suitable location (I've stuck them in my current working directory).
>> % Add the directory containing the .class files to your Java path
>> javaaddpath(pwd)
>> % Create the OutputSocket
>> output_socket = OutputSocket(1234)

output_socket =

OutputSocket@1c68b20

>> % Write some data to the output socket - text will do for now
>> for i = 1:100
output_socket.write(int8(sprintf('This is line %03d\r\n', i)));
pause(1);
end
>> % cleanup the socket - close connections
>> output_socket.close()
>> clear output_socket % no longer needed / useful


As you can see, multiple connections are supported. They can connect/disconnect at any time. Even if there are no connections, a call to OutputSocket::write() will be successful.


**Update**

I've included in the revised submission to MATLABCentral a wrapper MATLAB class that helps manage the lifetime of the Java object.

This is VERY useful because if you happen to clear the Java object (ie clear output_socket) before calling .close() the Thread and ServerSocket etc remain in memory (and stay connected!) until MATLAB is closed!

Suggested usage of MatlabOutputSocket below.
>> javaaddpath(pwd); % get the Java class
>> output_socket = MatlabOutputSocket(1234);
for i = 1:100
output_socket.write(int8(sprintf('Line %03d\r\n', i)));
pause(1);
end
>> output_socket.close()
>> clear output_socket

I've included the Java source file in the MATLABCentral submission, so feel free to have a look at the source code, have a fiddle, recompile.

To do this you will need to install the Java Development Kit (JDK).

To compile the classes, navigate to the directory containing the .java file and type:
>> javac OutputSocket.java
NOTE: You will have to repeat the call to javaaddpath() for the new files to be seen by MATLAB.

Monday, September 7, 2009

TCP/IP Socket Communications in MATLAB - Part 2

So my little blog post about TCP/IP Socket Communications in MATLAB was rather popular despite being a very simple example.

One of the main limitations that people found when trying to utilise the server/client scripts for their own applications, was that it was incredibly inefficient at shifting large volumes of data around. This is thanks to the following lines:
message = zeros(1, bytes_available, 'uint8');
for i = 1:bytes_available
message(i) = d_input_stream.
readByte;
end
For each individual byte within the message, there was the overhead of a function call.

I mentioned in comments on MATLAB Central that I made a Java class that bypassed this overhead and allowed for more efficient transfer. Being in a caring sharing mood (and getting sick of emailing it to people all the time!) I thought I would upload it also.

The is available on the Mathworks File Exchange: TCP/IP Socket Comms Example using Java Class

Compiled Java?

The client/server scripts are essentially identical to their previous iterations.

The server still uses a ServerSocket and the resulting DataOutputStream to which we write data.

The client side uses a Socket to connect to the specified host and port which provides us an InputStream which we wrap in a DataInputStream to read data from. However, instead of using the interface of DataInputStream directly, we hand off to another function (the new DataReader class) to perform the read.

The code for the example client is outlined below. I have bolded the changes. (The server is unchanged).

client.m
% CLIENT connect to a server and read a message
%
% Usage - message = client(host, port, number_of_retries)
function message = client(host, port, number_of_retries)

import java.net.Socket
import java.io.*

if (
nargin < number_of_retries =" 20;" style="color: rgb(0, 153, 0);">% set to -1 for infinite
end

retry = 0;
input_socket = [];
message = [];

while true

retry = retry + 1;
if ((number_of_retries > 0) && (retry > number_of_retries))
fprintf(1, 'Too many retries\n');
break;
end

try
fprintf(1, 'Retry %d connecting to %s:%d\n', ...
retry, host, port);

% throws if unable to connect
input_socket = Socket(host, port);

% get a buffered data input stream from the socket
input_stream = input_socket.
getInputStream;
d_input_stream =
DataInputStream(input_stream);

fprintf(1, 'Connected to server\n');

% read data from the socket - wait a short time first
pause(0.5);
bytes_available = input_stream.available;
fprintf(1, 'Reading %d bytes\n', bytes_available);

data_reader = DataReader(d_input_stream);
message = data_reader.readBuffer(bytes_available);


message = char(message'); % Data comes out as a column vector

% cleanup
input_socket.close;
break;

catch
if ~
isempty(input_socket)
input_socket.close;
end

% pause before retrying
pause(1);
end
end
end

Instead of looping for each byte, we now ask the DataReader object to read the specified number of bytes and return them to us. So a single function call per read. This could be used to read buffers of expected data sizes.

DataReader

Below is the Java source for the DataReader class (available in the MATLAB File Exchange also).

import java.io.*;

class DataReader
{
public DataReader(DataInput data_input)
{
m_data_input = data_input;
}

public byte[] readBuffer(int length)
{
byte[] buffer = new byte[length];

try
{
m_data_input.readFully(buffer, 0, length);
}

catch (StreamCorruptedException e)
{
System.out.println("Stream Corrupted Exception Occured");
buffer = new byte[0];
}
catch (EOFException e)
{
System.out.println("EOF Reached");
buffer = new byte[0];
}
catch (IOException e)
{
System.out.println("IO Exception Occured");
buffer = new byte[0];
}

return buffer;
}

private DataInput m_data_input;
}
It is very simple class that is given a DataInput to read from, and has a single public method readBuffer(int length) that returns a byte array.

If you have the Java Software Development Kit installed, you can compile the class with:
C:\matlab\matlab_socket> javac data_reader.java

Running the Client/Server example with DataReader

This is the same as the previous example, with one exception. You must tell MATLAB where to find the compiled DataReader class (In my case it is located in the C:\matlab\matlab_socket directory):
>> javaaddpath('C:\matlab\matlab_socket');
Opening up two instances of Matlab:
% Instance 1
>> message = char(mod(1:1000, 255)+1);
>> server(message, 3000, 10)
Try 1 waiting for client to connect to this host on port : 3000
Try 2 waiting for client to connect to this host on port : 3000
Try 3 waiting for client to connect to this host on port : 3000
Try 4 waiting for client to connect to this host on port : 3000
Client connected
Writing 1000 bytes

% Instance 2 (simultaneously)
% NOTE: If the 'server' was runnning on a non local machine, substitute its IP address
% or host name here:
% data = client('10.61.1.200', 2666); % To connect to server at IP 10.61.1.200:2666
>> javaaddpath('C:\matlab\matlab_socket');
>> data = client('
localhost', 3000)
Retry 1 connecting to
localhost:3000
Connected to server
Reading 1000 bytes

data =



 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~    

Why not call readFully() from within client.m?

The more astute readers may be asking the above question. Why not do something like this in client.m:
message = zeros(1, bytes_available, 'uint8');
d_input_stream.
readFully(message, 0 bytes_available);
At first glance that seems great. It runs without error, but hey, you don't seem to be getting any output?¿?

The problem is that DataInputStream.readFully() takes a reference to a byte array to populate.

When you pass things around in MATLAB that are to be modified, MATLAB supplies a copy. Thus the Java most likely sees a byte array reference, and populates it, but it is only the copy that is populated, not the original message array.

Until there exists some way in MATLAB to pass references to Java methods, I am stuck using helper Java classes (If anyone else has ideas on how this could be done better, feel free to comment!).

Thursday, September 18, 2008

Mathworks File Exchange Profile Information - Part 2

Ok, A quick little update here before I head off on holidays!

Continuing on from my previous post I realised that I wanted to watch how my submissions were performing over time (my memory is not quite what it used to be).

I had a crazy idea, could I create a Timer that would scrape my user statistics from the Mathworks File Exchange site and save them in a format such that I could plot them easily???

Introducing Monitor File Exchange Statistics.

Plotting my stats for the past week:
I'm not a big fan of the look of the submitted files plot as it can get a bit busy. I'm envisaging a lightweight GUI with a drop-down selector for each submitted file might be nicer. Particularly for those people with a large number of submitted files. I'll update the submission after I get back to do something like this.

To set the automatic scraping of your File Exchange statistics you need to do the following:
  1. Download the required MATLAB files from here. Need Monitor File Exchange Stats and Retrieve File Exchange Profile Information.
  2. Place the files somewhere in your MATLAB path. Note, if you already have a startup.m, just add the InitialiseFileExchangeInfoTimer and UpdateFileExchangeInfoTimerFcn to your existing startup.m and call InitialiseFileExchangeInfoTimer from within.
  3. Modify FEX_AUTHOR_ID and UPDATE_PERIOD with your File Exchange Author ID and your desired scrape frequency (seconds). The cache file defaults to being stored in your MATLAB root directory, modify FEX_SAVE_FILE if you wish to customise this location.
  4. Save any changes and restart MATLAB.
  5. Each time you restart Matlab you will be presented with a plot of your current and historical stats (for as long as you have been scraping).
  6. To manually plot your current stats, call plotLocalFileExchangeInfo(cache_file)
In terms of file size, I've had the script running every 10 minutes for nearly a week and my cache file is only 45 kB.

Thursday, September 4, 2008

Mathworks File Exchange Profile Information - Part 1

I have been uploading files to the Mathworks File Exchange and have found that I get a sick pleasure looking at my profile page each morning and seeing what people are downloading.

So I thought I would write some MATLAB that would streamline my morning operation as well as gather some additional stats such as downloads/rank over time.

Part 1 - Retrieving File Exchange Profile Information

I've developed a simple function that will, given an Author ID download the Author's profile page from the Mathworks File Exchange and parse it for the Name, Total Downloads, Rank and the list of submitted files.

The function is available on the Mathworks File Exchange: Retrieve File Exchange Profile Information
function file_exchange_info = getFileExchangeProfileInfo(author_id)
%GETFILEEXCHANGEPROFILEINFO Retrieve File Exchange information
% file_exchange_info = GETFILEEXCHANGEPROFILEINFO(author_id) retrieves an
% Author's information from the Mathworks File Exchange
% (http://www.mathworks.com/matlabcentral/fileexchange/)
%
% file_exchange_info is a structure with the following fields:
%
% .author_id - As supplied to the function
% .author_name - Author name
% .rank - Author rank
% .num_downloads - Total number of downloads
% .submitted_files - An array of structures with the following fields:
% .id - File Exchange submission ID
% .num_downloads - Number of downloads for this submission
% .name - Number of reviews for this file
%
% If the File Exchange page could not be loaded, file_exchange_info will be
% returned empty.
%
% Examples:
% file_exchange_info = getFileExchangeProfileInfo(1109866) % My profile
%
% See also urlread


To use the script simply call it supplying the author if interest's Author ID. For example lets have a look at Stuart McGarrity's profile page with Author ID 126174 (currently the number 1 ranked author):
>> info = getFileExchangeProfileInfo(126174)
info =
author_id: 126174
author_name: 'Stuart McGarrity'
rank: 1
num_downloads: 163181
submitted_files: [1x23 struct]
Looking at each submitted file in particular:
>> for i = 1:length(info.submitted_files)
info.submitted_files(i)
end


ans =
id: 2262
name: '802.11b PHY Simulink Model'
num_downloads: 21054
ans =
id: 722
name: 'Bluetooth modulation and frequency hopping'
num_downloads: 19999
ans =
id: 724
name: 'DTMF generator and receiver'
num_downloads: 15117
ans =
id: 907
name: 'Bluetooth voice transmission'
num_downloads: 12225
ans =
id: 3213
name: '802.11b PHY MATLAB Code'
num_downloads: 11803
ans =
id: 2283
name: 'Bluetooth Full Duplex Voice and Data Transmission'
num_downloads: 10830
ans =
id: 746
name: 'IS-95A CDMA Power Control'
num_downloads: 10406
ans =
id: 787
name: 'IS-95A Mobile Phone Call Processing'
num_downloads: 9214
ans =
id: 4380
name: 'MATLAB for C/C++ Programmers'
num_downloads: 9174
ans =
id: 9060
name: 'Handling Large Data Sets Efficiently in MATLAB'
num_downloads: 8329
ans =
id: 7595
name: [1x59 char]
num_downloads: 8165
ans =
id: 2596
name: '10Base-T Ethernet'
num_downloads: 5366
ans =
id: 1550
name: 'Packet Switch'
num_downloads: 4564
ans =
id: 9622
name: 'MDF Import Tool and Function'
num_downloads: 3491
ans =
id: 3939
name: 'Import Fig File to Axes'
num_downloads: 2386
ans =
id: 6528
name: 'Introduction to MATLAB 7 Webinar Demonstrations'
num_downloads: 2168
ans =
id: 13548
name: 'chkmem'
num_downloads: 1941
ans =
id: 18972
name: [1x78 char]
num_downloads: 1941
ans =
id: 16075
name: 'Textscantool'
num_downloads: 1511
ans =
id: 18971
name: [1x70 char]
num_downloads: 1410
ans =
id: 14438
name: [1x69 char]
num_downloads: 924
ans =
id: 9298
name: 'Data and M-Files for Demonstrations on MATLAB Demo Page'
num_downloads: 663
ans =
id: 19540
name: [1x77 char]
num_downloads: 614

Part 2 of this post will detail how I will be using the information returned by this function over time.

Now all I need to do is write it!

Tuesday, August 19, 2008

TCP/IP Socket Communications in MATLAB

I often see people asking about network communications on the MATLAB Newsgroup. Often this is for the communication between instances of MATLAB.

Using the ability to call Java directly from within MATLAB, I'm going to provide a short example of a client/server written solely in MATLAB and usable from Release 14 onwards (possibly even earlier).

The example is available on the Mathworks File Exchange: Simple TCP/IP Socket Comms Example

I'm working on a little TCP/IP comms library at the moment using these techniques. It will provide a nice layer of abstraction and allow you to use Sockets as you would in other programming languages (as well as one can in a single thread). Keep an eye out for it on the File Exchange.

Interpreted Java?

Amazingly we can execute Java code, even from within the Command Window without the need to compile. For example, the traditional example:
>> import java.lang.*
>> System.out.println('Hello World')
Hello World
To perform socket communications, we utilise the Java Socket and Input/OutputStream classes to pass data around via TCP/IP sockets.

On the server side we use (unsurprisingly) a ServerSocket, which once a client has been accepted, provides a Socket around which we wrap a DataOutputStream to which we can write data.

On the client side we use a Socket to connect to the specified host and port which provides us an InputStream which we wrap in a DataInputStream to read data from.

The code for the example server and client is outlined below.

client.m
% CLIENT connect to a server and read a message
%
% Usage - message = client(host, port, number_of_retries)
function message = client(host, port, number_of_retries)

import java.net.Socket
import java.io.*

if (nargin <>
number_of_retries = 20; % set to -1 for infinite
end

retry = 0;
input_socket = [];
message = [];

while true

retry = retry + 1;
if ((number_of_retries > 0) && (retry > number_of_retries))
fprintf(1, 'Too many retries\n');
break;
end

try
fprintf(1, 'Retry %d connecting to %s:%d\n', ...
retry, host, port);

% throws if unable to connect
input_socket = Socket(host, port);

% get a buffered data input stream from the socket
input_stream = input_socket.getInputStream;
d_input_stream = DataInputStream(input_stream);

fprintf(1, 'Connected to server\n');

% read data from the socket - wait a short time first
pause(0.5);
bytes_available = input_stream.available;
fprintf(1, 'Reading %d bytes\n', bytes_available);

message = zeros(1, bytes_available, 'uint8');
for i = 1:bytes_available
message(i) = d_input_stream.readByte;
end

message = char(message);

% cleanup
input_socket.close;
break;

catch
if ~isempty(input_socket)
input_socket.close;
end

% pause before retrying
pause(1);
end
end
end

server.m
% SERVER Write a message over the specified port
%
% Usage - server(message, output_port, number_of_retries)
function server(message, output_port, number_of_retries)

import java.net.ServerSocket
import java.io.*

if (nargin <>
number_of_retries = 20; % set to -1 for infinite
end
retry = 0;

server_socket = [];
output_socket = [];

while true

retry = retry + 1;

try
if ((number_of_retries > 0) && (retry > number_of_retries))
fprintf(1, 'Too many retries\n');
break;
end

fprintf(1, ['Try %d waiting for client to connect to this ' ...
'host on port : %d\n'], retry, output_port);

% wait for 1 second for client to connect server socket
server_socket = ServerSocket(output_port);
server_socket.setSoTimeout(1000);

output_socket = server_socket.accept;

fprintf(1, 'Client connected\n');

output_stream = output_socket.getOutputStream;
d_output_stream = DataOutputStream(output_stream);

% output the data over the DataOutputStream
% Convert to stream of bytes
fprintf(1, 'Writing %d bytes\n', length(message))
d_output_stream.writeBytes(char(message));
d_output_stream.flush;

% clean up
server_socket.close;
output_socket.close;
break;

catch
if ~isempty(server_socket)
server_socket.close
end

if ~isempty(output_socket)
output_socket.close
end

% pause before retrying
pause(1);
end
end
end
Opening up two instances of Matlab:
% Instance 1
>> message = char(mod(1:1000, 255)+1);
>> server(message, 3000, 10)
Try 1 waiting for client to connect to this host on port : 3000
Try 2 waiting for client to connect to this host on port : 3000
Try 3 waiting for client to connect to this host on port : 3000
Try 4 waiting for client to connect to this host on port : 3000
Client connected
Writing 1000 bytes

% Instance 2 (simultaneously)
% NOTE: If the 'server' was runnning on a non local machine, substitute its IP address
% or host name here:
% data = client('10.61.1.200', 2666); % To connect to server at IP 10.61.1.200:2666
>> data = client('localhost', 3000)
Retry 1 connecting to localhost:3000
Retry 2 connecting to localhost:3000
Connected to server
Reading 1000 bytes

data =



 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~    
This code can be expanded to read/write arbitrary data types, and SHOULD be expanded to properly deal with errors (ie not getting all of the buffer on receive end), but it serves as a simple example of how to get communication between MATLAB and other applications / instances of MATLAB.

Tuesday, August 12, 2008

Optimisation through MEX files

Whilst MATLAB is an excellent expressive tool, it can occasionally run a little bit slow for our liking. However, the folks at Mathworks have provided an interface that can be used to speed up code execution in particular circumstances.

MEX Files

MATLAB allows for compilation of C or Fortran sub-routines into a DLL (or equivalent) such that it can be called from within MATLAB as per any other function.

I'll be using a simple example I came across a while ago when attempting to read in large GPS logs containing on the order of a million GPS position records. As expected, the process of parsing these files took some time. What was unexpected was where the code was using up CPU time.

A quick run of the MATLAB Profiler revealed that approximately 50% of my processing time was spent in the calculation of the NMEA checksum (defined here). The MATLAB calculateChecksum function used is outlined below.
%===============================================================================
% Description : Calculate the NMEA Checksum for the supplied string. Calculated
% as the successive bitwise exclusive OR of all characters
%===============================================================================
function checksum = calculateChecksum(sentence)

% Initialise checksum

checksum = uint8(0);


for i_char = 1:length(sentence)

checksum = bitxor(checksum, uint8(sentence(i_char)));

end


checksum = dec2hex(checksum, 2);


end

To demonstrate the CPU usage of the above code snippet, a short test function was created:
function test_Checksum

nmea_sentence = 'GPGGA,195237,4308.639,S,07744.402,E,1,03,3.2,365.3,M,-34.5,M,1001,';
cs = '';

tic
for i = 1:50000
cs = calculateChecksum(nmea_sentence);
end
toc

% Verify Checksum
if (~strcmp(cs, '7F'))
error('Incorrect Checksum calculated');
end
end
The result of this function when executed several times:
Elapsed time is 4.990806 seconds.
Elapsed time is 4.978824 seconds.
Elapsed time is 5.029520 seconds.
Looking at the profiler output (run independently):

The majority of the time is spent performing the iterative XOR and the conversion from decimal to hexadecimal.

Using this example from Mathworks as a guide I created a simple MEX compatible C function that would calculate the 2 character hexadecimal checksum from a supplied string.
#include "mex.h"
#include <stdio.h>


void calculateChecksumFunction(const char* in_string, char *out_string)
{
int checksum_as_int = 0;
int i, str_length = strlen(in_string);

for (i = 0; i < style="color: rgb(51, 102, 255);">int
)*(in_string++);
}

checksum_as_int &= 0xFF;
sprintf(out_string, "%02X", checksum_as_int);
}

//****************************************************************
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
{
char *input_buf, *output_buf;
int buflen, status;

/* Check for proper number of arguments. */
if (nrhs != 1)
mexErrMsgTxt("One input required.");
else if (nlhs > 1)
mexErrMsgTxt("Too many output arguments.");

/* Input must be a string. */
if (mxIsChar(prhs[0]) != 1)
mexErrMsgTxt("Input must be a string.");

/* Input must be a row vector. */
if (mxGetM(prhs[0]) != 1)
mexErrMsgTxt("Input must be a row vector.");

/* Get the length of the input string. */
buflen = (mxGetM(prhs[0]) * mxGetN(prhs[0])) + 1;

/* Allocate memory for input and output strings.
* output string should be 2 ASCII characters (plus terminator) */
input_buf = mxCalloc(buflen, sizeof(char));
output_buf = mxCalloc(3, sizeof(char));

/* Copy the string data from prhs[0] into a C string
* input_buf. */
status = mxGetString(prhs[0], input_buf, buflen);
if (status != 0)
mexWarnMsgTxt("Not enough space. String is truncated.");

/* Calculate checksum and store result in output_buf */
calculateChecksumFunction(input_buf, output_buf);

/* Format return as a mex-string */
plhs[0] = mxCreateString(output_buf);

return;
}

This MEX compatible C file was then compiled using the 'mex' command from the MATLAB command window:
mex calculateChecksumMEX.c
This created a DLL in the same directory named calculateChecksumMEX.dll.

Substituting a call to calculateChecksumMEX in the test function redirects the processing to the created DLL.

The speed improvement is immediately noticeable:
Elapsed time is 0.423503 seconds.
Elapsed time is 0.425224 seconds.
Elapsed time is 0.430266 seconds.
An order of magnitude speed improvement was gained through the simple technique of identifying and isolating portions of code which were using the most CPU time and performing these operations in an an efficient C sub-routine.

Now MEX is not the silver bullet for every slow performing MATLAB function, but can prove to be useful. I would always recommend running the MATLAB Profiler over your code at least once to identify regions of poor performance. Poorly written MATLAB can run orders of magnitude slower than well written MATLAB.

Tuesday, July 29, 2008

Display Cursor Coordinates

A simple callback function that prints the current cursor location in plot coordinates into the plot window in a user specified location/format/colour.

DisplayCursorLocation

% cursorLocation - WindowButtonMotionFcn displaying cursor location in plot
%===============================================================================
% Description : Display the current cursor location within the bounds of a
% figure window. Assigned as a WindowButtonMotionFcn callback
% function. Only updates when mouse is moved over plot contents.
%
% Parameters : obj - Figure originating the callback
% event - not used (but required)
% location - Location within plot window for text. Can be
% 'BottomLeft', 'BottomRight', 'TopRight', 'TopLeft'
% or a [1x2] array of XY location
% format_str - A sprintf format string that will accept 2 float
% parameters. ie 'X: %.3f, Y: %.3f'
% text_color - either a color character (ie 'r') or a RGB
% triplet (ie [1.0 1.0 0.5])
%
% Return : None
%
% Usage : Assign to a Figure WindowButtonMotionFcn callback:
% set(fig_handle, 'WindowButtonMotionFcn',
% @(obj, event)cursorLocation(obj, event, 'BottomLeft',
% 'X: %.3f, Y: %.3f', 'r')
%
% Author : Rodney Thomson
% http://iheartmatlab.blogspot.com
%===============================================================================
function cursorLocation(obj, event, location, format_str, text_color)

The cursorLocation function is assigned as the WindowButtonMotionFcn for a figure. Any time the mouse is moved over the specified figure, the callback function will be executed.

The callback function retrieves the cursor location in plot axes coordinates and uses the supplied sprintf format to produce a text label which is printed in a specific location in the plot. This location can be a preset value or an arbitrary [X,Y] coordinate.

Example usage:
t = linspace(-5,5);
y = sinc(t); f = figure; plot(t, y, 'r');
set(f, 'WindowButtonMotionFcn', ...
@(obj, event)cursorLocation(obj, event, 'BottomLeft', ...
' X: %.3f\n Y: %.3f', 'r')

If you wanted to avoid setting the WindowButtonMotionFcn callback yourself, you could use the following wrapper function:
function displayCursorLocation(figure_handle, location, format_string, text_color)

set(figure_handle, 'WindowButtonMotionFcn', ...
@(obj, event)cursorLocation(obj, event, location, format_string, text_color));
end