TCP communication, send matrix cut in package, and reconnect the package in the client

1 visualización (últimos 30 días)
Hi,
I create two codes that communicate together through a TCP connexion (one compute, two Matlab instances open). One is the server, the second is the client. My goal is to simulate the recording of signal from an amplifier. The server act as the amplifier, the client as my GUI that record the signal.
In the server I load a data file of signal, which contains 120 channels by XX samples. To simulate the connexion with an amplifier, I use a timer and write every 0.1 second a package of few samples equivalent to the samplifing frequency of the real amplifier (2048 Hz).
This package is a piece of signal cut from the original signal in the function of timer. This package is a matrix of 120 by XXsamples double that I reshape in a 1 by XX samples and turn it in int16 to be written by the server.
The client is running also with a timer, every 0.2 second. The client read in the TCP connexion what is available. So it gets one or more packages reshaped 1 by XX samples int16. Then, I do the opposite operation in the client, turn it back to double and reshape it in a 120 by XXsamples matrix.
My issue is the recomposed signal in the client when plotted doesn't look like the orignal one, and the reason is the concatenation make me losing the orginal package 'orgnisation', when I reshape, I think I'm not reshaping my signal in a good way. And I don't find any answer of how to do it.
Is there a way to send package of signal and to read them and reorganize them correctly ?
You can find below the two codes I did: the server first then the client.
Thank you in advance
%% VIRTUAL AMPLIFIER %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% When opened you can select between different file that will be read in %
% a loop forever, those file of EMG recorded on a participant will be %
% written in a server that the GUI can have access like it was any %
% amplifictor %
close all; clear; clc;
global parameters
signal = load('contraction30sec120channels2048Hz.mat');
fsamp = 2048;
% Timer for Amplifier
parameters.TimerVirtualAmplifier = timer();
parameters.TimerVirtualAmplifier.ExecutionMode = 'fixedRate';
parameters.TimerVirtualAmplifier.Period = 0.01;
parameters.TimerVirtualAmplifier.TimerFcn = @(src,evt) TimerVirtualAmplifierFunction(src,evt); % you can use function handle or an anonymous function.
parameters.data = signal.data;
% 1- Prepare variables for reading in an endless loop the loaded file
parameters.PreviousSize = 1;
parameters.TimeRunning = 0;
parameters.PackageSize = round(parameters.TimerVirtualAmplifier.Period*fsamp);
% 2- Prepare Server
% Find Host Name and Address
% Find the host name and address of the machine where the server is
% created. The client uses this address to connect to the server.
[~,hostname] = system('hostname');
hostname = string(strtrim(hostname));
address = resolvehost(hostname,"address");
% Create Server
% Create the tcpserver object using the address of the machine and port 5000.
% Create a callback function called connectionFcn to write data when a TCP/IP
% client connects to the server. Set the ConnectionChangedFcn property to the
% callback function connectionFcn. You can find the connectionFcn function
% at the end of this example.
try
delete(parameters.server) % In case server haven't been close correctly
catch
end
parameters.server = tcpserver(address,5000,"ConnectionChangedFcn",@connectionFcn);
parameters.server.ByteOrder = 'little-endian';
disp('Server is on')
disp('Waiting for client to connect')
% Start the timer
warning off
%% Callback Functions
% Run the timer to update the data
function TimerVirtualAmplifierFunction(~, ~)
global parameters
newpackageposition = parameters.PreviousSize+parameters.PackageSize;
if newpackageposition > size(parameters.data,1)
package = parameters.data(parameters.PreviousSize:end,:);
parameters.PreviousSize = 1;
else
package = parameters.data(parameters.PreviousSize:parameters.PreviousSize+parameters.PackageSize,:);
parameters.PreviousSize = parameters.PreviousSize+parameters.PackageSize;
end
% Write on server
DataReShapePackage = reshape((package),[],1);
% DataReShapePackage = reshape((package),1,[]);
% Write data only if a TCP Client is connected
if isempty(parameters.server.ClientPort) == 0
DataReShapePackage = int16(DataReShapePackage);
write(parameters.server,DataReShapePackage,"int16");
elseif isempty(parameters.server.ClientPort) == 1
% If the client disconnect (press stop), stop the timer
disp("Client disconnected from the server.")
disp('Waiting for client to reconnect.')
stop(parameters.TimerVirtualAmplifier);
end
end
% Connection Callback Function to Write Binary Data
% This function calls write to write data to the connected TCP/IP client.
function connectionFcn(src, ~)
global parameters
if src.Connected
% If the client is connected, start the timer
disp("Client connection accepted by server.")
start(parameters.TimerVirtualAmplifier);
else
disp("Client not connected to the server.")
end
end
%%%%%%%%%%%%%%%%%%%%% Virtual Amplifier Client %%%%%%%%%%%%%%%%%%%%
close all; clear ; clc
global parameters
%% TIMER %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
parameters.TQuattrocento = timer();
parameters.TQuattrocento.ExecutionMode = 'fixedRate';
parameters.TQuattrocento.Period = 0.2;
parameters.TQuattrocento.TimerFcn = @(src,evt) QuattroncentobackgroundRunningfunction(src,evt); % you can use function handle or an anonymous function.
%% SETTINGS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
parameters.Xlength = 40; % Length of the plot
parameters.EMGGainFactor = 5/2^16/150*1000; %Provide amplitude in mV
parameters.AuxGainFactor = 5/2^16/0.5; % Gain factor to convert Aux Channels in V
parameters.offsetQuattrocento = 50;
parameters.Fsamp = 8; % Codes to set the sampling frequency [0 8 16 24];
parameters.FsampVal = 2048; % Sampling frequency values
parameters.NumChan = 0; % Codes to set the number of channels [0 2 4 6];
parameters.NumChanVal = 72; % Channels numbers
%% TCP Connexion
% Find Host Name and Address
% Find the host name and address of the machine where the server is
% created. The client uses this address to connect to the server.
[~,hostname] = system('hostname');
hostname = string(strtrim(hostname));
parameters.TCPPort = 5000; % Number of TCP socket port
parameters.IP = resolvehost(hostname,"address"); % Virtual amplifier IP adress
parameters.tcpSocket = tcpclient(parameters.IP,parameters.TCPPort, "Timeout",10, "EnableTransferDelay",true);
parameters.tcpSocket.ByteOrder = 'little-endian'; % 'big-endian'
parameters.refreshrate = 1;
parameters.NsampVal = floor(parameters.FsampVal*parameters.TQuattrocento.Period/parameters.refreshrate);
%% Pre allocation
parameters.time = linspace(0, 1, parameters.NsampVal*parameters.Xlength); % this one will be the full time vector for the plot
parameters.signal = (repmat(1:parameters.NumChanVal,parameters.NsampVal*parameters.Xlength,1)) * parameters.offsetQuattrocento;
parameters.PlotChansignalEMG = (0:1:63)* parameters.offsetQuattrocento;
% parameters.PlotChansignalAUX = (0:1:15)* parameters.offsetQuattrocento;
parameters.PlotChansignalAUX = (0:1:1)* parameters.offsetQuattrocento;
parameters.PlotChansignalACESS = (0:1:7)* parameters.offsetQuattrocento;
parameters.temporarymatrix = zeros(round(parameters.FsampVal*parameters.TQuattrocento.Period),parameters.NumChanVal); % in this one the EMG will be stored temporarelly
%% Figure preparation
% parameters.Multiplein(1,:) = 33:96;
% parameters.AUX = 97:112;
% parameters.Accessories = 113:120;
parameters.Multiplein(1,:) = 1:64;
parameters.AUX = 65:66;
parameters.Accessories = 67:72;
parameters.figuresignalQuattrocento = figure(); parameters.figuresignalQuattrocento.Position = [10 400 1700 600];
parameters.axhIN1 = axes('Parent',parameters.figuresignalQuattrocento);parameters.axhIN1.Position = [0.1 0.50 0.8 0.45];
parameters.axhINAUX = axes('Parent',parameters.figuresignalQuattrocento); parameters.axhINAUX.Position = [0.1 0.20 0.8 0.20];
parameters.axhINAcess = axes('Parent',parameters.figuresignalQuattrocento); parameters.axhINAcess.Position = [0.1 0.03 0.8 0.10];
parameters.LineMLTIN1 = line(parameters.axhIN1,parameters.time,parameters.signal(:,parameters.Multiplein(1,:))); title(parameters.axhIN1,'Multiple in 1')
parameters.LineAUX = line(parameters.axhINAUX,parameters.time,parameters.signal(:,parameters.AUX)); title(parameters.axhINAUX,'Auxillary')
parameters.LineAcess = line(parameters.axhINAcess,parameters.time,parameters.signal(:,parameters.Accessories(4))); title(parameters.axhINAcess,'Accessories')
parameters.axhIN1.YLim = [parameters.offsetQuattrocento*-1 parameters.offsetQuattrocento*65];
parameters.axhINAUX.YLim = [parameters.offsetQuattrocento*-0.4 parameters.offsetQuattrocento*18];
parameters.axhINAcess.YLim = [0 parameters.offsetQuattrocento*4];
%% TIMER %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
parameters.i = 0;
DataQuattrocento = fread(parameters.tcpSocket)'; % empty the buffer before starting the function
clear DataQuattrocento
start(parameters.TQuattrocento);
% Wait for user to manually stop the session
uiwait(msgbox('Press OK to stop data acquisition.', 'Stop Acquisition', 'modal'));
%% Stop the DAQ and TCP
stop(parameters.TQuattrocento)
% Close the communication\
flush(parameters.tcpSocket)
delete(parameters.tcpSocket)
clear parameters.tcpSocket
%% Handle function
function QuattroncentobackgroundRunningfunction(src, ~)
tic;
global parameters
parameters.i = parameters.i + 1;
disp(['iteration: ' num2str(parameters.i)])
NumBytesAvailable = parameters.tcpSocket.NumBytesAvailable;
disp(['NumBytesAvailable: ' num2str(NumBytesAvailable)])
Remainder = rem(NumBytesAvailable, 2880);
disp(['Remainder: ' num2str(Remainder)])
NumBytesAvailable = (NumBytesAvailable-Remainder)/2;
disp(['NumBytesAvailableTotal: ' num2str(NumBytesAvailable)])
disp('------------------------------------------------------------')
if NumBytesAvailable <= 0
else
toc
tic
try
DataQuattrocento = read(parameters.tcpSocket, NumBytesAvailable, 'int16')';
% keyboard
toc
tic
DataQuattrocento = reshape((DataQuattrocento),parameters.NumChanVal,[]);
sizedata = size(DataQuattrocento,2);
parameters.NsampVal = ceil(sizedata/parameters.refreshrate);
parameters.refreshedmatrix = DataQuattrocento(:,1:parameters.refreshrate:end); % Apply downscale if necessary
parameters.signal(1+parameters.NsampVal:end,1:parameters.NumChanVal) =...
parameters.signal(1:end-parameters.NsampVal,1:parameters.NumChanVal);
parameters.signal(1:parameters.NsampVal,1:parameters.NumChanVal) = parameters.refreshedmatrix';
% Multiple in 1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Ysignal{1} = ((parameters.signal(:,parameters.Multiplein(1,:))*parameters.EMGGainFactor)+parameters.PlotChansignalEMG)';
Ysignalmultiplein1 = num2cell(Ysignal{1},2);
set(parameters.LineMLTIN1, {'YData'}, Ysignalmultiplein1)
% Aux %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Ysignal{2} = (parameters.signal(:,parameters.AUX)+parameters.PlotChansignalAUX)';
YsignalAUX = num2cell(Ysignal{2},2);
set(parameters.LineAUX, {'YData'}, YsignalAUX)
% Accessories %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Ysignal{3} = (parameters.signal(:,parameters.Accessories(4))+parameters.PlotChansignalACESS(:,4))';
YsignalAccess = num2cell(Ysignal{3},2);
set(parameters.LineAcess, {'YData'}, YsignalAccess)
clear DataQuattrocento
tcpserverfind
catch
keyboard
end
toc
end
end
  2 comentarios
Animesh
Animesh el 27 de Ag. de 2024
Hey @Mikael Jonathan, can you attach the "contraction30sec120channels2048Hz.mat" file?
Mikael Jonathan
Mikael Jonathan el 27 de Ag. de 2024
Hi @Animesh, thank you for your interest in my issue.
Here is a like to the file:
Or I linked a smaller version were I reduced the number of samples (to be below the limit of the 5mb)

Iniciar sesión para comentar.

Respuesta aceptada

Sivsankar
Sivsankar el 10 de Sept. de 2024
Hi Mikael,
You have mentioned that the package signal is a matrix of 120 by XX samples. But the provided mat file is of the format XX by 120. So, I believe that the discrepancy that you are facing is because of the package you are sending.
In the server code make the following change:
DataReShapePackage = reshape(package', [], 1); % Transpose to get [120, XX] and then reshape
Transposing the package matrix would make the signal a 120 by XX matrix. This is the output that I’ve obtained by making the following change:
I believe this is the desired output. Hope this helps!
  1 comentario
Mikael Jonathan
Mikael Jonathan el 10 de Sept. de 2024
Hi @Sivsankar, you indeed found the solution, I just did several trials, it was this transposition that I missed. Thank you very much !

Iniciar sesión para comentar.

Más respuestas (0)

Productos


Versión

R2024a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by