This is very complicated problem about data structures, I will try and explain it as simple as possible
I have multiple signals in a array signals, every element of this is a structure with multiple segments of the signal and its attribute.
Now I have another function which filters this signal and does some calculation according to the cut off frequency.
Whenever this function is called, I want to loop through all fc, and through all signals and through all segments. But the problem is fc is calculated only in a signal, so I have something like this:
classdef datHandle < handle
properties
error_norm = {};
final_error = {};
signals = {};
end
methods
function this = addsignal(this, varargin)
%signal segmentation is done here
end
function this = addfilter(this, varargin)
for i = 1:length(this.signals)% for each signal
this.error_norm = {};
fn = 1/((mean(diff(this.signals{i}(1).time)))*2);
fc = linspace(1,fn,(fn/0.5)); %calculate fc
this.tempaddfilt(fc,i)
end
this.final_error = [this.final_error;this.error_norm];
end
function this = tempaddfilt(this,varargin)
s = [];
f = ltiFilter.PT1(); % initiate filter class
fc = varargin{1}; % take fc
i = varargin{2}; % the exact signal
for a = 1:length(fc) % fc
q = 0;
w = 0;
for k = 1:length(this.segments{i}) % segment of ith signal
f.fc = fc(a);
filt_sig = f.eval(this.segments{i}(k).signal,this.segments{i}(k).signal(1)); %signal and the initial value of the signal
filt_sig = filt_sig';
s(1,i).main(k).seg_err(a) = std((filt_sig-this.segments{i}(k).ref)); % calculate the standard diviation of the signal
q = q+s(1,i).main(k).seg_err(a);
s(1,i).main(k).fc(a) = fc(a);
end
s(1,i).main(i).sig_err(a) = q;
w = w+s(1,i).main(i).sig_err(a);
end
s(1,1).main(1).filt_err(a) = w;
this.error_norm = [this.error_norm s];
end
end
end
test script:
clear all
close all
filname = load('file');
signal1 = filname.signal; % current value
time1 = filname.time;
signal2 = filname.signal2; % current value
time2 = filname.time2;
f = ltiFilter.datHandle();
f.addsignal(signal1,time1,93);
f.addfilter()
I planned the final_norm to be something like this:
But my algorithm doesn't work, when I add 2nd signal. If anyone has better algorithm any suggestion is welcome.
I don't fully understand, what is your data structure and why each signal is divided into segments? Do you have frame-based signal processing (chunk by chunk), or there is an algorithm for signal segmentation?
I think you should make more than one class with object vectorization along signals, filters and segments.
Related
I am very new to Scilab, but so far have not been able to find an answer (either here or via google) to my question. I'm sure it's a simple solution, but I'm at a loss. I have a lot of MATLAB scripts I wrote in grad school, but now that I'm out of school, I no longer have access to MATLAB (and can't justify the cost). Scilab looked like the best open alternative. I'm trying to convert my .m files to Scilab compatible versions using mfile2sci, but when running the mfile2sci GUI, I get the error/message shown below. Attached is the original code from the M-file, in case it's relevant.
I Searched Stack Overflow and companion sites, Google, Scilab documentation.
The M-file code follows (it's a super basic MATLAB script as part of an old homework question -- I chose it as it's the shortest, most straightforward M-file I had):
Mmax = 15;
N = 20;
T = 2000;
%define upper limit for sparsity of signal
smax = 15;
mNE = zeros(smax,Mmax);
mESR= zeros(smax,Mmax);
for M = 1:Mmax
aNormErr = zeros(smax,1);
aSz = zeros(smax,1);
ESR = zeros(smax,1);
for s=1:smax % for-loop to loop script smax times
normErr = zeros(1,T);
vESR = zeros(1,T);
sz = zeros(1,T);
for t=1:T %for-loop to carry out 2000 trials per s-value
esr = 0;
A = randn(M,N); % generate random MxN matrix
[M,N] = size(A);
An = zeros(M,N); % initialize normalized matrix
for h = 1:size(A,2) % normalize columns of matrix A
V = A(:,h)/norm(A(:,h));
An(:,h) = V;
end
A = An; % replace A with its column-normalized counterpart
c = randperm(N,s); % create random support vector with s entries
x = zeros(N,1); % initialize vector x
for i = 1:size(c,2)
val = (10-1)*rand + 1;% generate interval [1,10]
neg = mod(randi(10),2); % include [-10,-1]
if neg~=0
val = -1*val;
end
x(c(i)) = val; %replace c(i)th value of x with the nonzero value
end
y = A*x; % generate measurement vector (y)
R = y;
S = []; % initialize array to store selected columns of A
indx = []; % vector to store indices of selected columns
coeff = zeros(1,s); % vector to store coefficients of approx.
stop = 10; % init. stop condition
in = 0; % index variable
esr = 0;
xhat = zeros(N,1); % intialize estimated x signal
while (stop>0.5 && size(S,2)<smax)
%MAX = abs(A(:,1)'*R);
maxV = zeros(1,N);
for i = 1:size(A,2)
maxV(i) = abs(A(:,i)'*R);
end
in = find(maxV == max(maxV));
indx = [indx in];
S = [S A(:,in)];
coeff = [coeff R'*S(:,size(S,2))]; % update coefficient vector
for w=1:size(S,2)
r = y - ((R'*S(:,w))*S(:,w)); % update residuals
if norm(r)<norm(R)
index = w;
end
R = r;
stop = norm(R); % update stop condition
end
for j=1:size(S,2) % place coefficients into xhat at correct indices
xhat(indx(j))=coeff(j);
end
nE = norm(x-xhat)/norm(x); % calculate normalized error for this estimate
%esr = 0;
indx = sort(indx);
c = sort(c);
if isequal(indx,c)
esr = esr+1;
end
end
vESR(t) = esr;
sz(t) = size(S,2);
normErr(t) = nE;
end
%avsz = sum(sz)/T;
aSz(s) = sum(sz)/T;
%aESR = sum(vESR)/T;
ESR(s) = sum(vESR)/T;
%avnormErr = sum(normErr)/T; % produce average normalized error for these run
aNormErr(s) = sum(normErr)/T; % add new avnormErr to vector of all av norm errors
end
% just put this here to view the vector
mNE(:,M) = aNormErr;
mESR(:,M) = ESR;
% had an 'end' placed here, might've been unmatched
mNE%reshape(mNE,[],Mmax)
mESR%reshape(mESR,[],Mmax)]
figure
dimx = [1 Mmax];
dimy = [1 smax];
imagesc(dimx,dimy,mESR)
colormap gray
strESR = sprintf('Average ESR, N=%d',N);
title(strESR);
xlabel('M');
ylabel('s');
strNE = sprintf('Average Normed Error, N=%d',N);
figure
imagesc(dimx,dimy,mNE)
colormap gray
title(strNE)
xlabel('M');
ylabel('s');
The command used (and results) follow:
--> mfile2sci
ans =
[]
****** Beginning of mfile2sci() session ******
File to convert: C:/Users/User/Downloads/WTF_new.m
Result file path: C:/Users/User/DOWNLO~1/
Recursive mode: OFF
Only double values used in M-file: NO
Verbose mode: 3
Generate formatted code: NO
M-file reading...
M-file reading: Done
Syntax modification...
Syntax modification: Done
File contains no instruction, no translation made...
****** End of mfile2sci() session ******
To convert the foo.m file one has to enter
mfile2sci <path>/foo.m
where stands for the path of the directoty where foo.m is. The result is written in /foo.sci
Remove the ```` at the begining of each line, the conversion will proceed normally ?. However, don't expect to obtain a working .sci file as the m2sci converter is (to me) still an experimental tool !
I programmed PID in MATLAB:
classdef PID < handle
properties
Kp = 0
Ki = 0
Kd = 0
SetPoint = 1
Dt = 0.01
end
properties (Access = private)
IState = 0
PreErr = 0
end
methods
function obj = PID(Kp, Ki, Kd, SetPoint, Dt)
if nargin == 0
return;
end
obj.Kp = Kp;
obj.Ki = Ki;
obj.Kd = Kd;
obj.SetPoint = SetPoint;
obj.Dt = Dt;
end
function output = update(obj, measuredValue, t)
err = obj.SetPoint - measuredValue;
P = obj.getP(err);
I = obj.getI(err);
val = lowPass(obj,t);
D = obj.getD(err*val);
output = P + I + D;
end
function val = getP(obj, err)
val = obj.Kp*err;
end
function val = getI(obj, err)
obj.IState = obj.IState + err * obj.Dt;
val = obj.Ki * obj.IState;
end
function val = getD(obj, err)
val = obj.Kd * (err - obj.PreErr) / obj.Dt;
obj.PreErr = err;
end
function val = lowPass(obj,t)
N = 10;
val = 1-exp(-N*t);
end
end
end
And tested it using a random low pass filter as the plant:
function r = getResponse(t)
r = 1 - exp(-5*t);
end
The test code:
sr = 1e2; % sampling rate 100Hz
st = 10; % sampling time 10s
ss = st*sr+1; % sample size
t = 0:1/sr:st; % time
input = ones(1,ss)*100;
output = zeros(1,ss);
measured = 0;
pid = PID(0,1,1,input(1),t(2)-t(1));
for i = 2:ss
rPID(i) = pid.update(measured, t(i));
output(i) = rPID(i)*getResponse(t(i));
measured = output(i);
end
figure
plot(t,output)
hold on;
plot(t,input)
plot(t,rPID)
legend('Output','Input','PID')
Note that the parameters are set to kp=0;ki=1;kd=1;. I'm only testing the differential part here. The result is very wrong:
Notice the Y-axis is scaled by 10^307. It gets too big that after ~1.6s the PID value exceeds the range of double precision and therefore, the curve stops.
I have ensured that both P and I parts work well enough (see this question I asked a while ago).
From the curve for the D component (see figure below), one can clearly see that it starts to oscillate heavy from the very beginning; its value reaches >50k after the 5th timestamp at 0.04s:
I'm almost certain I must have made a mistake in implementing the low pass filter, but I also noticed that even with the low pass filter removed, the differential values still behave similarly.
To have some sort of reference and comparison, I also made a Simulink simulation of the same system, using the exact same PID gains (i.e. kp=0;ki=1;kd=1;). Below is the block diagram (left), figure for input and output (top right figure) and figure for PID values (bottom right)
Note that there is no top/lower limit in the gain blocks and the initial inputs/outputs are set to zeros.
These PID gains are nowhere near optimised but they give completely different results in the simulation and coded PID.
Therefore the big question is am I doing something wrong here? Why is there a difference between the two results?
The implementation of the low pass filter is not correct. The difference equation of a low pass filter is as shown:
The call of the getResponse function could look like this:
pid = PID(0,1,1,input(1),t(2)-t(1));
for i = 2:ss
rPID(i) = pid.update(measured, t(i));
alpha = getResponse(0.25,0.01);
output(i) = rPID(i)*alpha+(1-alpha)*output(i-1);
measured = output(i);
end
Thus getResponse is equivalent to alpha
function r = getResponse(wc,Ts)
r = 1 - exp(-wc*Ts);
end
Further you have to modify the lowPass function in the PID class.
function output = update(obj, measuredValue)
err = obj.SetPoint - measuredValue;
P = obj.getP(err);
I = obj.getI(err);
val = lowPass(obj,err,0.1,0.01);
D = obj.getD(val);
output = P + I + D;
end
% ...
function val = lowPass(obj,err,wc,Ts)
alpha = getResponse(wc,Ts);
output = err*alpha+(1-alpha)*obj.output_1;
obj.output_1 = output;
val = output;
end
I'm using ode15s to simulate/solve a set of ODEs. I would like to implement a feature, where upon reaching a given condition during the simulation, a number in the model changes programatically (e.g., an indicator constant) for a fixed amount of time, and then reverts back.
This could be, for example using Lotka-Volterra equations:
dx/dt = alphax - betax*y
dy/dt = (delta+indicator)xy - gammay + epsilonindicator
indicator starts as 0. Let's say that when x reaches 10, I'd like to switch indicator to 1 for 10 time units, and then flip it back to 0.
This can be done in a dirty way by using global variables, however, this is something I'd like to avoid (impossible to parallelize + general avoidance of global variables). Is there a neat alternative way when using ode15s (i.e., I don't know the time step)?
Many thanks for any suggestions!
Edit: As noted correctly by LutzL, wrapping an ODE with non-smooth state without handling events may lead to inaccurate results
as you can not predict at what time points in what order the ODE
function is evaluated. LutzL
So the accurate solution is to deal with ODE events. An example for the modified Lotka-Volterra equations is given below, where the event fires, if x gets >10 and the indicator will be turned on for 10 seconds:
% parameters and times:
params = ones(5,1); % [alpha, ..., epsilon]
z_start = [2, 1];
t_start = 0;
t_end = 30;
options = odeset('Events',#LotkaVolterraModEvents); % set further options here, too.
% wrap ODE function with indicator on and off
LVModODE_indicatorOn = #(t,z)LotkaVolterraModODE(t,z,1, params);
LVModODE_indicatorOff = #(t,z)LotkaVolterraModODE(t,z,0, params);
% storage for simulation values:
data.t = t_start;
data.z = z_start;
data.teout = [];
data.zeout = zeros(0,2);
data.ieout = [];
% temporary loop variables:
z_0 = z_start;
t_0 = t_start;
isIndicatorActive = false;
while data.t(end) < t_end % until the end time is reached
if isIndicatorActive
% integrate for 10 seconds, if indicator is active
active_seconds = 10;
[t, z, te,ze,ie] = ode15s(LVModODE_indicatorOn, [t_0 t_0+active_seconds], z_0, options);
else
% integrate until end or event, if indicator is not active.
[t, z, te,ze,ie] = ode15s(LVModODE_indicatorOff, [t_0 t_end], z_0, options);
isIndicatorActive = true;
end
%append data to storage
t_len = length(t);
data.t = [data.t; t(2:end)];
data.z = [data.z; z(2:end,:)];
data.teout = [data.teout; te];
data.zeout = [data.zeout; ze];
data.ieout = [data.ieout; ie];
% reinitialize start values for next iteration of loop
t_0 = t(end);
z_0 = z(end, :);
% set the length of the last instegration
options = odeset(options,'InitialStep',t(end) - t(end-1));
end
%% plot your results:
figure;
plot(data.t, data.z(:,1), data.t, data.z(:,2));
hold all
plot(data.teout, data.zeout(:,1), 'ok');
legend('x','y', 'Events in x')
%% Function definitions for integration and events:
function z_dot = LotkaVolterraModODE(t, z, indicator, params)
x = z(1); y= z(2);
% state equations: modified Lotka-Volterra system
z_dot = [params(1)*x - params(2)*y;
(params(4) + indicator)*x*y - params(3)*y + params(5)*indicator];
end
function [value, isTerminal, direction] = LotkaVolterraModEvents(t,z)
x = z(1);
value = x-10; % event on rising edge when x passes 10
isTerminal = 1; %stop integration -> has to be reinitialized from outer logic
direction = 1; % only event on rising edge (i.e. x(t_e-)<10 and x(t_e+)>10)
end
The main work is done in the while loop, where the integration takes place.
(Old post) The following solution may lead to inaccurate results, handling events, as explained in the first part should be preferred.
You could wrap your problem in a class, which is able to hold a state (i.e. its properties). The class should have a method, which is used as odefun for the variable-step integrator. See also here on how to write classes in MATLAB.
The example below demonstrates, how it could be achieved for the example you provided:
% file: MyLotkaVolterra.m
classdef MyLotkaVolterra < handle
properties(SetAccess=private)
%define, if the modified equation is active
indicator;
% define the start time, where the condition turned active.
tStart;
% ode parameters [alpha, ..., epsilon]
params;
end
methods
function self = MyLotkaVolterra(alpha, beta, gamma, delta, epsilon)
self.indicator = 0;
self.tStart = 0;
self.params = [alpha, beta, gamma, delta, epsilon];
end
% ODE funciton for the state z = [x;y] and time t
function z_dot = odefun(self, t, z)
x = z(1); y= z(2);
if (x>=10 && ~self.indicator)
self.indicator = 1;
self.tStart = t;
end
%condition to turn indicator off:
if (self.indicator && t - self.tStart >= 10)
self.indicator = false;
end
% state equations: modified Lotka-Volterra system
z_dot = [self.params(1)*x - self.params(2)*y;
(self.params(4) + self.indicator)*x*y - ...
self.params(3)*y + self.params(5)*self.indicator];
end
end
end
This class could be used as follows:
% your ode using code:
% 1. create an object (`lvObj`) from the class with parameters alpha = ... = epsilon = 1
lvObj = MyLotkaVolterra(1, 1, 1, 1, 1);
% 2. pass its `odefun`method to the integrator (exaple call with ode15s)
[t,y] = ode15s(#lvObj.odefun, [0,5], [9;1]); % 5 seconds
I have originally written the following Matlab code to find intersection between a set of Axes Aligned Bounding Boxes (AABB) and space partitions (here 8 partitions). I believe it is readable by itself, moreover, I have added some comments for even more clarity.
function [A,B] = AABBPart(bbx,it) % bbx: aabb, it: iteration
global F
IT = it+1;
n = size(bbx,1);
F = cell(n,it);
A = Part([min(bbx(:,1:3)),max(bbx(:,4:6))],it,0); % recursive partitioning
B = F; % matlab does not allow
function s = Part(bx,it,J) % output to be global
s = {};
if it < 1; return; end
s = cell(8,1);
p = bx(1:3);
q = bx(4:6);
h = 0.5*(p+q);
prt = [p,h;... % 8 sub-parts (octa)
h(1),p(2:3),q(1),h(2:3);...
p(1),h(2),p(3),h(1),q(2),h(3);...
h(1:2),p(3),q(1:2),h(3);...
p(1:2),h(1),h(1:2),q(3);...
h(1),p(2),h(3),q(1),h(2),q(3);...
p(1),h(2:3),h(1),q(2:3);...
h,q];
for j=1:8 % check for each sub-part
k = 0;
t = zeros(0,1);
for i=1:n
if all(bbx(i,1:3) <= prt(j,4:6)) && ... % interscetion test for
all(prt(j,1:3) <= bbx(i,4:6)) % every aabb and sub-parts
k = k+1;
t(k) = i;
end
end
if ~isempty(t)
s{j,1} = [t; Part(prt(j,:),it-1,j)]; % recursive call
for i=1:numel(t) % collecting the results
if isempty(F{t(i),IT-it})
F{t(i),IT-it} = [-J,j];
else
F{t(i),IT-it} = [F{t(i),IT-it}; [-J,j]];
end
end
end
end
end
end
Concerns:
In my tests, it seems that probably few intersections are missing, say, 10 or so for 1000 or more setup. So I would be glad if you could help to find out any problematic parts in the code.
I am also concerned about using global F. I prefer to get rid of it.
Any other better solution in terms of speed, will be loved.
Note that the code is complete. And you can easily try it by some following setup.
n = 10000; % in the original application, n would be millions
bbx = rand(n,6);
it = 3;
[A,B] = AABBPart(bbx,it);
`sol = pdepe(m,#ParticleDiffusionpde,#ParticleDiffusionic,#ParticleDiffusionbc,x,t);
% Extract the first solution component as u.
u = sol(:,:,:);
function [c,f,s] = ParticleDiffusionpde(x,t,u,DuDx)
global Ds
c = 1/Ds;
f = DuDx;
s = 0;
function u0 = ParticleDiffusionic(x)
global qo
u0 = qo;
function [pl,ql,pr,qr] = ParticleDiffusionbc(xl,ul,xr,ur,t,x)
global Ds K n
global Amo Gc kf rhop
global uavg
global dr R nr
sum = 0;
for i = 1:1:nr-1
r1 = (i-1)*dr; % radius at i
r2 = i * dr; % radius at i+1
r1 = double(r1); % convert to double precision
r2 = double(r2);
sum = sum + (dr / 2 * (r1*ul+ r2*ur));
end;
uavg = 3/R^3 * sum;
ql = 1;
pl = 0;
qr = 1;
pr = -((kf/(Ds.*rhop)).*(Amo - Gc.*uavg - ((double(ur/K)).^2).^(n/2) ));`
dq(r,t)/dt = Ds( d2q(r,t)/dr2 + (2/r)*dq(r,t)/dr )
q(r, t=0) = 0
dq(r=0, t)/dr = 0
dq(r=dp/2, t)/dr = (kf/Ds*rhop) [C(t) - Cp(at r = dp/2)]
q = solid phase concentration of trace compound in a particle with radius dp/2
C = bulk liquid concentration of trace compound
Cp = trace compound concentration at particle surface
I want to solve the above pde with initial and boundary conditions given. Tried Matlab's pdepe, but does not work satisfactorily. Maybe the boundary conditions is creating problem for me. I also used this isotherm equation for equilibrium: q = K*Cp^(1/n). This is convection-diffusion equation but i could not find any write ups that addresses solving this type of equation properly.
There are two problems with the current implementation.
Incorrect Source Term
The PDE you are attempting to solve has the form
which has the equivalent form
where the last term arises due to the factor of 2 in the original PDE.
The last term needs to be incorporated into pdepe via a source term.
Calculation of q average
The current implementation attempts to calculate the average value of q using the left and right values of q passed to the boundary condition function.
This is incorrect.
The average value of q needs to be calculated from a vector of up-to-date values of the quantity.
However, we have the complication that the only function to receive all mesh values is ParticleDiffusionpde; however, the mesh values passed to that function are not guaranteed to be from the mesh we provided.
Solution: use events (as described in the pdepe documentation).
This is a hack since the event function is meant to detect zero-crossings, but it has the advantage that the function is given all values of q on the mesh we provide.
So, the working example below (you'll notice I set all of the parameters to 1 since I didn't know better) uses the events function to update a variable qStore that can be accessed by the boundary condition function (see here for an explanation), and the boundary condition function performs a vectorized trapezoidal integration for the average calculation.
Working Example
function [] = ParticleDiffusion()
% Parameters
Ds = 1;
q0 = 0;
K = 1;
n = 1;
Amo = 1;
Gc = 1;
kf = 1;
rhop = 1;
% Space
rMesh = linspace(0,1,10);
rMesh = rMesh(:) ;
dr = rMesh(2) - rMesh(1) ;
% Time
tSpan = linspace(0,1,10);
% Vector to store current u-value
qStore = zeros(size(rMesh));
options.Events = #(m,t,x,y) events(m,t,x,y);
% Solve
[sol,~,~,~,~] = pdepe(1,#ParticleDiffusionpde,#ParticleDiffusionic,#ParticleDiffusionbc,rMesh,tSpan,options);
% Use the events function to update qStore
function [value,isterminal,direction] = events(m,~,~,y)
qStore = y; % Value of q on rMesh
value = m; % Since m is constant, it will never be zero (no event detection)
isterminal = 0; % Continue integration
direction = 0; % Detect all zero crossings (not important)
end
function [c,f,s] = ParticleDiffusionpde(r,~,~,DqDr)
% Define the capacity, flux, and source
c = 1/Ds;
f = DqDr;
s = DqDr./r;
end
function u0 = ParticleDiffusionic(~)
u0 = q0;
end
function [pl,ql,pr,qr] = ParticleDiffusionbc(~,~,R,ur,~)
% Calculate average value of current solution
qL = qStore(1:end-1);
qR = qStore(2: end );
total = sum((qL.*rMesh(1:end-1) + qR.*rMesh(2:end))) * dr/2;
qavg = 3/R^3 * total;
% Left boundary
pl = 0;
ql = 1;
% Right boundary
qr = 1;
pr = -(kf/(Ds.*rhop)).*(Amo - Gc.*qavg - (ur/K).^n);
end
end