function [phased,c]=gabphasederivreal(type,method,varargin)
%GABPHASEDERIVREAL DGT phase derivatives
% Usage: [phased,c] = gabphasederivreal(dflag,'dgt',f,g,a,M);
% phased = gabphasederivreal(dflag,'cross',f,g,a,M)
% phased = gabphasederivreal(dflag,'phase',cphase,a,M);
% phased = gabphasederivreal(dflag,'phase',cphase,a,M,difforder);
% phased = gabphasederivreal(dflag,'abs',s,g,a,M);
% phased = gabphasederivreal(dflag,'abs',s,a,M,difforder);
% [{phased1,phased2,...}] = gabphasederivreal({dflag1,dflag2,...},...);
% [{phased1,phased2,...},c] = gabphasederivreal({dflag1,dflag2,...},'dgt',...);
%
% phased=GABPHASEDERIVREAL(dflag,method,...) computes the time-frequency
% derivative dflag of the phase of the DGTREAL of a signal using algorithm
% method.
%
% The following strings can be used in place of dflag:
%
% 't' First phase derivative in time.
%
% 'f' First phase derivative in frequency.
%
% 'tt' Second phase derivative in time.
%
% 'ff' Second phase derivative in frequency.
%
% 'tf' or 'ft' Second order mixed phase derivative.
%
% phased is scaled such that (possibly non-integer) distances are measured
% in samples. Similarly, the frequencies are scaled such that the Nyquist
% frequency (the highest possible frequency) corresponds to a value of L/2.
%
% The computation of phased is inaccurate when the absolute
% value of the Gabor coefficients is low. This is due to the fact the the
% phase of complex numbers close to the machine precision is almost
% random. Therefore, phased attain very large random values when abs(c)
% is close to zero.
%
% The phase derivative computation can be done using four different methods
% depending on the string method:
%
% 'dgt' Directly from the signal using algorithm by Auger and
% Flandrin.
%
% 'cross' Directly from the signal using algorithm by Nelson.
%
% 'phase' From the unwrapped phase of a DGT of the signal using a
% finite differences scheme. This is the classic method used
% in the phase vocoder.
%
% 'abs' From the absolute value of the DGT exploiting explicit
% dependency between partial derivatives of log-magnitudes and
% phase.
% Currently this method works only for Gaussian windows.
%
% phased=GABPHASEDERIVREAL(dflag,'dgt',f,g,a,M) computes the time-frequency
% derivative using a DGT of the signal f. The DGT is computed using the
% window g on the lattice specified by the time shift a and the number
% of channels M. The algorithm used to perform this calculation computes
% several DGTs, and therefore this routine takes the exact same input
% parameters as DGTREAL.
%
% [phased,c]=GABPHASEDERIVREAL(dflag,'dgt',f,g,a,M) additionally returns
% the Gabor coefficients c, as they are always computed as a byproduct
% of the algorithm.
%
% phased=GABPHASEDERIVREAL(dflag,'cross',f,g,a,M) does the same as above
% but this time using algorithm by Nelson which is based on computing
% several DGTs.
%
% phased=GABPHASEDERIVREAL(dflag,'phase',cphase,a,M) computes the phase
% derivative from the phase cphase of a DGT of the signal. The number of
% channels M is a required input to disambiguate the existence of a
% Nyquist channel, which is only present if M is even. The original DGT
% from which the phase is obtained must have been computed using a
% time-shift of a using the default phase convention ('freqinv') e.g.:
%
% phased=gabphasederivreal(dflag,'phase',angle(dgt(f,g,a,M)),a,M)
%
% phased=GABPHASEDERIVREAL(dflag,'abs',s,g,a) computes the phase
% derivative from the absolute values of DGT coefficients s. The number
% of channels M is a required input to disambiguate the existence of a
% Nyquist channel, which is only present if M is even. The spectrogram
% must have been computed using the window g and time-shift a e.g.:
%
% phased=gabphasederivreal(dflag,'abs',abs(dgt(f,g,a,M)),g,a,M)
%
% Currently the 'abs' method only works if the window g is a Gaussian
% window specified as a string or a cell array.
%
% phased=GABPHASEDERIVREAL(dflag,'abs',s,g,a,M,difforder) and
% phased=GABPHASEDERIVREAL(dflag,'pgase',cphase,a,M,difforder) use a
% centered finite diffence scheme of order difforder to perform the
% needed numerical differentiation. Default is to use a 4th order scheme.
%
% Phase conventions
% -----------------
%
% First derivatives in either direction are subject to phase convention.
% The following additional flags define the phase convention the original
% phase would have had:
%
% 'freqinv' Derivatives reflect the frequency-invariant phase of dgt.
% This is the default.
%
% 'timeinv' Derivatives reflect the time-invariant phase of dgt.
%
% 'symphase' Derivatives reflect the symmetric phase of dgt.
%
% 'relative' This is a combination of 'freqinv' and 'timeinv'.
% It uses 'timeinv' for derivatives along frequency and
% and 'freqinv' for derivatives along time and for the
% mixed derivative.
% This is usefull for the reassignment functions.
%
% Please see ltfatnote042 for the description of relations between the
% phase derivatives with different phase conventions. Note that for the
% 'relative' convention, the following holds:
%
% gabphasederiv('t',...,'relative') == gabphasederiv('t',...,'freqinv')
% gabphasederiv('f',...,'relative') == -gabphasederiv('f',...,'timeinv')
% gabphasederiv('tt',...,'relative') == gabphasederiv('tt',...)
% gabphasederiv('ff',...,'relative') == -gabphasederiv('ff',...)
% gabphasederiv('tf',...,'relative') == gabphasederiv('tf',...,'freqinv')
%
% Several derivatives at once
% ---------------------------
%
% phasedcell=gabphasederiv({dflag1,dflag2,...},...) computes several
% phase derivatives at once while reusing some temporary computations thus
% saving computation time.
% {dflag1,dflag2,...} is a cell array of the derivative flags and
% cell elements of the returned phasedcell contain the corresponding
% derivatives i.e.:
%
% [pderiv1,pderiv2,...] = deal(phasedcell{:});
%
% [phasedcell,c]=gabphasederiv({dflag1,dflag2,...},'dgt',...) works the
% same as above but in addition returns coefficients c which are the
% byproduct of the 'dgt' method.
%
% Other flags and parameters work as before.
%
% See also: resgram, gabreassign, dgt, pderiv, gabphasegrad
%
% References:
% F. Auger and P. Flandrin. Improving the readability of time-frequency
% and time-scale representations by the reassignment method. IEEE Trans.
% Signal Process., 43(5):1068--1089, 1995.
%
% E. Chassande-Mottin, I. Daubechies, F. Auger, and P. Flandrin.
% Differential reassignment. Signal Processing Letters, IEEE,
% 4(10):293--294, 1997.
%
% J. Flanagan, D. Meinhart, R. Golden, and M. Sondhi. Phase Vocoder. The
% Journal of the Acoustical Society of America, 38:939, 1965.
%
% K. R. Fitz and S. A. Fulop. A unified theory of time-frequency
% reassignment. CoRR, abs/0903.3080, 2009.
%
% D. J. Nelson. Instantaneous higher order phase derivatives. Digital
% Signal Processing, 12(2-3):416--428, 2002. [1]http ]
%
% F. Auger, E. Chassande-Mottin, and P. Flandrin. On phase-magnitude
% relationships in the short-time fourier transform. Signal Processing
% Letters, IEEE, 19(5):267--270, May 2012.
%
% Z. Průša. STFT and DGT phase conventions and phase derivatives
% interpretation. Technical report, Acoustics Research Institute,
% Austrian Academy of Sciences, 2015.
%
% References
%
% 1. http://dx.doi.org/10.1006/dspr.2002.0456
%
%
% Url: http://ltfat.github.io/doc/gabor/gabphasederivreal.html
% Copyright (C) 2005-2023 Peter L. Soendergaard <peter@sonderport.dk> and others.
% This file is part of LTFAT version 2.6.0
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/>.
% AUTHOR: Peter L. Søndergaard, 2008; Zdenek Průša, 2015
% REMARK: There is no problem with phase conventions with the second
% derivatives.
complainif_notenoughargs(nargin,2,upper(mfilename));
definput.flags.type = {'t','f','tt','ff','tf','ft'};
definput.flags.method = {'dgt','phase','abs','cross'};
definput.import = {'gabphasederivconv'};
typewascell = 0;
if ischar(type)
types = {type};
elseif iscell(type)
%error('Multiple derivatives at once were not implemented yet.');
types = type;
typewascell = 1;
else
error('%s: First argument must be either char or a cell array.',...
upper(mfilename));
end
types = lower(types);
for ii=1:numel(types)
type = types{ii};
if ~ischar(type) || ~any(strcmpi(type, definput.flags.type))
error(['%s: First argument must contain the type of the derivative: %s'],...
upper(mfilename),...
strjoin(cellfun(@(el) sprintf('"%s"',el),...
definput.flags.type,'UniformOutput',0),', '));
end
end
if ~ischar(method) || ~any(strcmpi(method, definput.flags.method))
error(['%s: Second argument must be the method name: %s '], ...
upper(mfilename),...
strjoin(cellfun(@(el) sprintf('"%s"',el),...
definput.flags.method,'UniformOutput',0),', '));
end;
diphaseconv = arg_gabphasederivconv;
foundFlags = cellfun(@(el)ischar(el) && any(strcmpi(el,diphaseconv.flags.phaseconv)),...
varargin);
flags1 = ltfatarghelper({},definput,{types{1},method,varargin{foundFlags}});
definput = []; % Definput is used again later
varargin(foundFlags) = []; % Remove the phaseconv flags
switch flags1.method
case {'phase','abs'}
if nargout>1
error('%s: Too many output arguments. ', upper(mfilename));
end
end
% Change ft to tf as they are equal
if any(strcmpi('ft',types))
types(strcmpi('ft',types)) = {'tf'};
end
% Get only unique flags
[~,tmpidx] = unique(types,'first');
dflagsUnique = types(sort(tmpidx));
% Find duplicates
dflagsDupl = cellfun(@(tEl) strcmpi(tEl,types),dflagsUnique,'UniformOutput',0);
% Sort the unique flags according to character count
[~,dflagsOrder]=sort(cellfun(@length,dflagsUnique));
% Sort
dflagsUnique = dflagsUnique(dflagsOrder);
% Allocate the output cell array
phased2 = cell(1,numel(dflagsUnique));
switch flags1.method
case {'dgt','cross'}
complainif_notenoughargs(numel(varargin),4,mfilename);
[f,gg,a,M]=deal(varargin{1:4});
definput.keyvals.L=[];
definput.keyvals.minlvl=eps;
definput.keyvals.lt=[0 1];
[~,kv,L,minlvl]=ltfatarghelper({'L','minlvl'},definput,varargin(5:end));
% Change f to correct shape.
[f,Ls]=comp_sigreshape_pre(f,upper(mfilename),0);
% Even though this check is also in dgt, we must do it here too
if isempty(L)
L = dgtlength(Ls,a,M,kv.lt);
else
Luser = dgtlength(L,a,M,kv.lt);
if Luser~=L
error(['%s: Incorrect transform length L=%i specified. Next valid length ' ...
'is L=%i. See the help of DGTLENGTH for the requirements.'],...
upper(mfilename),L,Luser);
end
end
% Extend or crop f to correct length
f=postpad(f,L);
N = L/a;
b = L/M;
end
switch flags1.method
case 'dgt'
% --------------------------- DGT method ------------------------
%
% Naming conventions used here:
% c - dgt coefficients using window g
% c_s - second power of c with small values set to minlvl*max(c_s)
% c_h - dgt coefficietns computed using time-weighted window hg
% c_d - dgt coefficients computed using time-derived window dg
% c_h2 - dgt coefficients computed using twice time-weighted window hg2
% c_d2 - dgt coefficients computed using second derivative of window g: hg2
% c_hd - dgt coefficients computed using time-weighted derivative of window g: hdg
% c_dh - dgt coefficients computed using derivative of time-weighted window g: dhg
%
% Call dgt once to check all the parameters
% This computes frequency invariant phase.
[c,~,g] = dgtreal(f,gg,a,M,L,'lt',kv.lt);
% Compute spectrogram and remove small values because we need to
% divide by c_s.
% This will also set the derivative to zeros in the regions
% containing only small coefficients.
c_s = abs(c).^2;
c_s = max(c_s,minlvl*max(c_s(:)));
% We also need info for info.gauss
[~,info]=gabwin(gg,a,M,L,kv.lt,'callfun',upper(mfilename));
% These could be shared
dg = []; c_d = []; hg = []; c_h = [];
for typedId=1:numel(dflagsUnique)
typed = dflagsUnique{typedId};
if info.gauss
% TODO: Save computations for 2nd derivatives
end
switch typed
case 't'
if info.gauss && ~isempty(c_h)
phased = imag(c_h.*conj(c)./c_s)/info.tfr;
else
% fir2long is here to avoid possible boundary effects
% as the time support of the Inf derivative is not FIR
dg = pderiv(fir2long(g,L),[],Inf)/(2*pi);
c_d = comp_dgtreal(f,dg,a,M,kv.lt,0);
phased = -imag(c_d.*conj(c)./c_s);
end
switch flags1.phaseconv
case {'freqinv','relative'}
% Do nothing
case 'timeinv'
M2 = floor(M/2)+1;
phased = bsxfun(@plus,phased,(0:M2-1).'*b);
case 'symphase'
M2 = floor(M/2)+1;
phased = bsxfun(@plus,phased,(0:M2-1).'*b/2);
end
case 'f'
if info.gauss && ~isempty(c_d)
phased = real(c_d.*conj(c)./c_s)*info.tfr;
else
% Compute the time weighted version of the window.
% g is already a column vector
hg = fftindex(size(g,1)).*g;
c_h = comp_dgtreal(f,hg,a,M,kv.lt,0);
phased = -real(c_h.*conj(c)./c_s);
end
switch flags1.phaseconv
case 'timeinv'
% Do nothing
case 'relative'
phased = -phased;
case 'freqinv'
phased = bsxfun(@plus,phased,-fftindex(N).'*a);
case 'symphase'
phased = bsxfun(@plus,phased,-fftindex(N).'*a/2);
end
case 'tt'
if isempty(dg)
dg = pderiv(fir2long(g,L),[],Inf)/(2*pi);
c_d = comp_dgtreal(f,dg,a,M,kv.lt,0);
end
dg2 = pderiv(dg,[],Inf);
c_d2 = comp_dgtreal(f,dg2,a,M,kv.lt,0);
phased = imag(c_d2.*conj(c)./c_s - 2*pi*(c_d.*conj(c)./c_s).^2)/L;
% Phase convention does not have any effect
case 'ff'
if isempty(hg)
% Time weighted window
hg = fftindex(size(g,1)).*g;
c_h = comp_dgtreal(f,hg,a,M,kv.lt,0);
end
hg2 = fftindex(size(g,1)).^2.*g;
c_h2 = comp_dgtreal(f,hg2,a,M,kv.lt,0);
phased = imag(-c_h2.*conj(c)./c_s + (c_h.*conj(c)./c_s).^2)*2*pi/L;
switch flags1.phaseconv
case 'relative'
phased = -phased;
end
case {'ft','tf'}
if isempty(hg)
hg = fftindex(size(g,1)).*g;
c_h = comp_dgtreal(f,hg,a,M,kv.lt,0);
end
if isempty(dg)
dg = pderiv(fir2long(g,L),[],Inf)/(2*pi);
c_d = comp_dgtreal(f,dg,a,M,kv.lt,0);
end
hdg = (fftindex(size(dg,1))/L).*dg;
c_hd = comp_dgtreal(f,hdg,a,M,kv.lt,0);
% This is mixed derivative tf for freq. invariant
phased = real(c_hd.*conj(c)./c_s - (1/L)*c_h.*c_d.*(conj(c)./c_s).^2)*2*pi;
switch flags1.phaseconv
case {'freqinv','relative'}
% Do nothing
case 'timeinv'
phased = phased + 1;
case 'symphase'
phased = phased + 1/2;
end
otherwise
error('%s: This should never happen.',upper(mfilename));
end
phased2{typedId} = phased;
end
case 'phase'
% ---------- Direct numerical derivative of the phase ----------------
complainif_notenoughargs(numel(varargin),3,mfilename);
[cphase,a,M]=deal(varargin{1:3});
complainif_notposint(a,'a',mfilename)
complainif_notposint(M,'M',mfilename)
if ~isreal(cphase)
error(['%s: Input phase must be real valued. Use the "angle" ' ...
'function to compute the argument of complex numbers.'],...
upper(mfilename));
end
% --- linear method ---
[M2,N,W]=size(cphase);
L=N*a;
b=L/M;
% Extend cphase to account for periodic frequency derivative
difforder = 2; % Currently, pderivunwrap only supports second order
% centered differences.
if mod(M,2) == 0
if ~(difforder == Inf)
addIdx = [M2-(1:difforder),1+(difforder:-1:1)];
else
addIdx = M2-(1:M2-2);
end
else
if ~(difforder == Inf)
addIdx = [M2-(0:difforder-1),1+(difforder:-1:1)];
else
addIdx = M2-(0:M2-1);
end
end
cphase = [cphase;-cphase(addIdx,:,:)];
tgrad = []; fgrad = [];
for typedId=1:numel(dflagsUnique)
typed = dflagsUnique{typedId};
% REMARK: Second derivative in one direction does not need phase
% unwrapping
if isempty(tgrad) && any(strcmpi(typed,{'t','tt','ft','tf'}))
% This is the classic phase vocoder algorithm by Flanagan modified to
% yield a second order centered difference approximation.
% Perform derivative along rows while unwrapping the phase by 2*pi
tgrad = pderivunwrap(cphase,2,2*pi);
% Normalize
%tgrad = tgrad/(2*pi);
% Normalize again using time step
tgrad = tgrad/(a);
end
if isempty(fgrad) && any(strcmpi(typed,{'f','ff'}))
% Phase-lock the angles.
% We have the frequency invariant phase ...
TimeInd = (0:(N-1))*a;
if difforder == Inf
FreqInd = (0:M-1)/M;
else
FreqInd = [(0:M2-1+difforder),M-(difforder:-1:1)]/M;
end
phl = FreqInd'*TimeInd;
cphaseLock = cphase+2*pi.*phl;
% ... and now the time-invariant phase.
% Perform derivative along cols while unwrapping the phase by 2*pi
fgrad = pderivunwrap(cphaseLock,1,2*pi);
% Convert from radians to relative frequencies
%fgrad = fgrad/(2*pi);
% Normalize again using frequency step
fgrad = fgrad/(b);
end
% tgrad fgrad here are relative quantites
switch typed
case 't'
phased = tgrad*L/(2*pi);
switch flags1.phaseconv
case {'freqinv','relative'}
% Do nothing
case 'timeinv'
if difforder == Inf
fftIdxs = fftindex(M);
elseif mod(M,2) == 0
fftIdxs = [(0:M2-1),-M2+(1:difforder)+1,-(difforder:-1:1)].';
else
fftIdxs = [(0:M2-1),-M2+(1:difforder),-(difforder:-1:1)].';
end
phased = bsxfun(@plus,phased,fftIdxs*b);
case 'symphase'
if difforder == Inf
fftIdxs = fftindex(M);
elseif mod(M,2) == 0
fftIdxs = [(0:M2-1),-M2+(1:difforder)+1,-(difforder:-1:1)].';
else
fftIdxs = [(0:M2-1),-M2+(1:difforder),-(difforder:-1:1)].';
end
phased = bsxfun(@plus,phased,fftIdxs*b/2);
end
case 'f'
phased = fgrad*L/(2*pi);
switch flags1.phaseconv
case 'timeinv'
% Do nothing
case 'relative'
phased = -phased;
case 'freqinv'
phased = bsxfun(@plus,phased,-fftindex(N).'*a);
case 'symphase'
phased = bsxfun(@plus,phased,-fftindex(N).'*a/2);
end
case 'tt'
% Second derivatives should be independent of the phase
% convention.
% tgrad is already unwrapped along time, we can call pderiv
% directly.
phased = pderiv(tgrad,2,2)/(2*pi);
% Phase convention does not have any effect.
case 'ff'
% fgrad is already unwrapped along frequency
phased = M*pderiv(fgrad,1,2)/(2*pi)/(M2+2*difforder);
switch flags1.phaseconv
case 'relative'
phased = -phased;
end
case {'tf','ft'}
% Phase has not yet been unwrapped along frequency
phased = pderivunwrap(tgrad,1,2*pi)*M/(2*pi);
switch flags1.phaseconv
case 'timeinv'
phased = phased +1;
case {'freqinv','relative'}
% Do nothing
case 'symphase'
phased = phased +1/2;
end
% Phase has not yet been unwrapped along time
% phased = pderivunwrap(fgrad,2,2*pi)*N/(2*pi);
otherwise
error('%s: This should never happen.',upper(mfilename));
end
phased2{typedId} = phased(1:M2,:,:);
end
case 'abs'
% --------------------------- abs method ------------------------
complainif_notenoughargs(numel(varargin),4,mfilename);
[s,g,a,M]=deal(varargin{1:4});
complainif_notposint(a,'a',mfilename)
complainif_notposint(M,'M',mfilename)
if numel(varargin)>4
difforder=varargin{5};
else
difforder=4;
end;
if ~(all(s(:)>=0))
error('%s: s must be positive or zero.',mfilename);
end;
[M2,N,W]=size(s);
L=N*a;
[~,info]=gabwin(g,a,M,L,'callfun',upper(mfilename));
if ~info.gauss
error(['%s: The window must be a Gaussian window (specified as a string or ' ...
'as a cell array).'],upper(mfilename));
end;
L=N*a;
b=L/M;
% We must avoid taking the log of zero.
% Therefore we add the smallest possible
% number
logs=log(s+realmin);
% XXX REMOVE Add a small constant to limit the dynamic range. This should
% lessen the problem of errors in the differentiation for points close to
% (but not exactly) zeros points.
maxmax=max(logs(:));
tt=-11; % This is equal to about 95dB of 'normal' dynamic range
logs(logs<maxmax+tt)=tt;
% Extend logs to account for periodic frequency derivative
if mod(M,2) == 0
if ~(difforder == Inf)
addIdx = [M2-(1:difforder),1+(difforder:-1:1)];
else
addIdx = M2-(1:M2-2);
end
else
if ~(difforder == Inf)
addIdx = [M2-(0:difforder-1),1+(difforder:-1:1)];
else
addIdx = M2-(0:M2-1);
end
end
logs = [logs;logs(addIdx,:,:)];
for typedId=1:numel(dflagsUnique)
typed = dflagsUnique{typedId};
tgrad = []; fgrad = [];
if isempty(tgrad) && any(strcmpi(typed,{'t','tt','ft','tf'}))
tgrad = M*pderiv(logs,1,difforder)/(2*pi)/(M2+2*difforder);
end
if isempty(fgrad) && any(strcmpi(typed,{'f','ff'}))
fgrad = -pderiv(logs,2,difforder)/(2*pi);
end
switch typed
case 't'
% Derivative of log-magnitude along frequency gives phase
% time derivative
phased=tgrad/info.tfr;
switch flags1.phaseconv
case {'freqinv','relative'}
% Do nothing
case 'timeinv'
if difforder == Inf
fftIdxs = fftindex(M);
elseif mod(M,2) == 0
fftIdxs = [(0:M2-1),-M2+(1:difforder)+1,-(difforder:-1:1)].';
else
fftIdxs = [(0:M2-1),-M2+(1:difforder),-(difforder:-1:1)].';
end
phased = bsxfun(@plus,phased,fftIdxs*b);
case 'symphase'
if difforder == Inf
fftIdxs = fftindex(M);
elseif mod(M,2) == 0
fftIdxs = [(0:M2-1),-M2+(1:difforder)+1,-(difforder:-1:1)].';
else
fftIdxs = [(0:M2-1),-M2+(1:difforder),-(difforder:-1:1)].';
end
phased = bsxfun(@plus,phased,fftIdxs*b/2);
end
case 'f'
% Derivative of log-magnitude along time gives phase frequency
% derivative
phased= fgrad*info.tfr;
switch flags1.phaseconv
case 'timeinv'
% Do nothing
case 'relative'
phased = - phased;
case 'freqinv'
phased = bsxfun(@plus,phased,-fftindex(N).'*a);
case 'symphase'
phased = bsxfun(@plus,phased,-fftindex(N).'*a/2);
end
case 'ff'
% Mixed derivatives of log-magnitude give second derivative
% of phase
phased= M*info.tfr*pderiv(fgrad,1,difforder)/L/(M2+2*difforder);;
switch flags1.phaseconv
case 'relative'
phased = -phased;
end
case 'tt'
% Second phase derivatives are equal up to factor
% -info.tfr^2
phased=pderiv(tgrad,2,difforder)/(L*info.tfr);
% Phase convention does not have any effect
case {'ft','tf'}
% (Both) second log-magnitude derivatives give mixed phase
% derivatives.
phased = M*pderiv(tgrad,1,difforder)/(info.tfr*L)/(M2+2*difforder);
% This is equal
% phased = pderiv(logs,2,difforder)/(2*pi);
% phased = -info.tfr*pderiv(phased,2,difforder)/L - 1;
switch flags1.phaseconv
case {'freqinv','relative'}
% Do nothing
case 'timeinv'
phased = phased + 1;
case 'symphase'
phased = phased + 1/2;
end
otherwise
error('%s: This should never happen.',upper(mfilename));
end
phased2{typedId} = phased(1:M2,:,:);
end
case 'cross'
% This is the Nelson's cross-spectral matrix algorithm modified to
% do centered finite difference approximations of the derivatives
%
% NOTE: This method computes dgts shifted by one in time and/or
% frequency. Frequency-shifted dgts do not benefit from using
% dgtreal/fftreal, since the frequency shifted window/input signal
% will not be real.
[g,info] = gabwin(gg,a,M,L,kv.lt,'callfun',upper(mfilename));
M2 = floor(M/2)+1;
if info.gl<L-2
% FIR case
gtmp = fir2long(g,numel(g)+2);
else
% fir2long is here to cover gl == L-2 and L-1
gtmp = fir2long(g,L);
end
cright = []; cleft = []; cabove = []; cbelow = []; ccenterfi = [];
ccenterti = [];
crightabove = []; crightbelow = []; cleftabove = []; cleftbelow = [];
for typedId=1:numel(dflagsUnique)
typed = dflagsUnique{typedId};
if (isempty(cright) || isempty(cleft)) && any(strcmpi(typed,{'t','tt'}))
cright = dgtreal(f,timefreqshift(gtmp,L,1,0),a,M,L,'lt',kv.lt);
cleft = dgtreal(f,timefreqshift(gtmp,L,-1,0),a,M,L,'lt',kv.lt);
end
if (isempty(cabove) || isempty(cbelow)) && any(strcmpi(typed,{'f','ff'}))
cabove = dgt(f,timefreqshift(gtmp,L,0,1),a,M,L,'lt',kv.lt,'timeinv');
cabove = cabove(1:M2,:,:);
cbelow = dgt(f,timefreqshift(gtmp,L,0,-1),a,M,L,'lt',kv.lt,'timeinv');
cbelow = cbelow(1:M2,:,:);
end
if isempty(ccenterfi) && any(strcmpi(typed,{'tt','t'}))
ccenterfi = dgtreal(f,g,a,M,L,'lt',kv.lt);
end
if isempty(ccenterti) && any(strcmpi(typed,{'ff','f'}))
ccenterti = dgtreal(f,g,a,M,L,'lt',kv.lt,'timeinv');
end
if (isempty(crightabove) || isempty(crightbelow) || ...
isempty(cleftabove) || isempty(cleftbelow)) ...
&& any(strcmpi(typed,{'tf','ft'}))
% Get four dgts shifted by one in time and in frequency
crightabove = dgt(timefreqshift(f,L,-1,-1),g,a,M,L,'lt',kv.lt);
crightabove = crightabove(1:M2,:,:);
crightbelow = dgt(timefreqshift(f,L,-1,1),g,a,M,L,'lt',kv.lt);
crightbelow = crightbelow(1:M2,:,:);
cleftabove = dgt(timefreqshift(f,L,1,-1),g,a,M,L,'lt',kv.lt);
cleftabove = cleftabove(1:M2,:,:);
cleftbelow = dgt(timefreqshift(f,L,1,1),g,a,M,L,'lt',kv.lt);
cleftbelow = cleftbelow(1:M2,:,:);
end
switch typed
case 't'
% This way, the implicit phase unwrapping is done in
% both differences.
ccross1 = cright.*conj(ccenterfi);
ccross2 = ccenterfi.*conj(cleft);
phased = (angle(ccross1)+angle(ccross2))/(2*2*pi)*L;
switch flags1.phaseconv
case {'freqinv','relative'}
% Do nothing
case 'timeinv'
phased = bsxfun(@plus,phased,(0:M2-1).'*b);
case 'symphase'
phased = bsxfun(@plus,phased,(0:M2-1).'*b/2);
end
case 'f'
ccross1 = cabove.*conj(ccenterti);
ccross2 = ccenterti.*conj(cbelow);
phased = (angle(ccross1)+angle(ccross2))/(2*2*pi)*L;
switch flags1.phaseconv
case 'timeinv'
% Do nothing
case 'relative'
phased = -phased;
case 'freqinv'
phased = bsxfun(@plus,phased,-fftindex(N).'*a);
case 'symphase'
phased = bsxfun(@plus,phased,-fftindex(N).'*a/2);
end
case 'tt'
ccross = cright.*conj(ccenterfi).^2.*cleft;
phased = angle(ccross)/(2*pi)*L;
case 'ff'
ccross = cabove.*conj(ccenterti).^2.*cbelow;
phased = angle(ccross)/(2*pi)*L;
switch flags1.phaseconv
case 'relative'
phased = -phased;
end
case {'ft','tf'}
ccross = crightabove.*conj(crightbelow).*conj(cleftabove).*cleftbelow;
phased = angle(ccross)/(4*2*pi)*L;
switch flags1.phaseconv
case 'timeinv'
% Do nothing
case {'freqinv','relative'}
phased = phased - 1;
case 'symphase'
phased = phased - 1/2;
end
otherwise
error('%s: This should never happen.',upper(mfilename));
end
phased2{typedId} = phased;
end
end;
% Undo flag sort
phased2(dflagsOrder) = phased2;
% Distribute duplicate flags
phased = cell(1,numel(dflagsDupl{1}));
for ii=1:numel(phased2)
phased(dflagsDupl{ii}) = phased2(ii);
end
if ~typewascell
assert(numel(phased)==1,'phased should contain only 1 element');
phased = phased{1};
end
function fd=pderivunwrap(f,dim,unwrapconst)
%PDERIVUNWRAP Periodic derivative with unwrapping
%
% `pderivunwrap(f,dim,unwrapconst)` performs derivative of *f* along
% dimmension *dim* using second order centered difference approximation
% including unwrapping by *unwrapconst*
%
% This effectivelly is just
% (circshift(f,-1)-circshift(f,1))/2
if nargin<2 || isempty(dim)
dim = 1;
end
if nargin<3 || isempty(unwrapconst)
unwrapconst = 2*pi;
end
shiftParam = {[1,0],[-1,0]};
if dim == 2
shiftParam = {[0,1],[0,-1]};
end
% Forward approximation
fd_1 = f-circshift(f,shiftParam{1});
fd_1 = fd_1 - unwrapconst*round(fd_1/(unwrapconst));
% Backward approximation
fd_2 = circshift(f,shiftParam{2})-f;
fd_2 = fd_2 - unwrapconst*round(fd_2/(unwrapconst));
% Average
fd = (fd_1+fd_2)/2;
function g = timefreqshift(g,L,tshift,fshift,timeshiftfirst)
% This function circularly shifts g by tshift in time and
% by fshift in frequency.
if nargin<3
error('Not enough args');
elseif nargin<4
fshift = 0;
timeshiftfirst = 1;
elseif nargin<5
timeshiftfirst = 1;
end
gl = size(g,1);
tshiftop = @(g) g;
fshiftop = @(g) g;
if abs(fshift)>0
l = fftindex(gl)/L;
fshiftop = @(g) g.*exp(1i*2*pi*fshift*l);
end
if abs(tshift) >0
tshiftop = @(g) circshift(g,tshift);
end
if timeshiftfirst
g = fshiftop(tshiftop(g));
else
g = tshiftop(fshiftop(g));
end