├── ConceFT_J3.m ├── ConceFT_J4.m ├── LICENSE.md ├── README.md ├── SST_J.m ├── SST_J2.m ├── STFT_J.m ├── STFT_J2.m ├── deShape_J.m ├── deShape_Jb.m ├── hermf.m └── imageSQ.m /ConceFT_J3.m: -------------------------------------------------------------------------------- 1 | function [cft, frequency] = ConceFT_J3(x, Fs, hlength, iter, hop, n, hf, lf, ths) 2 | % Computes the concentrated time-freq representation of the signal x. 3 | % Choose iter = 1 to compute the SST of the signal x. 4 | % Choose iter = 0 to compute only the STFT of the signal x. 5 | % INPUT 6 | % x : Signal (x should be a column vector). 7 | % Fs : Sampling rate of x. 8 | % hlength: Window length. 9 | % iter : Number of SST computations. 10 | % hop : Calculate the fft every hop samples, starting at 1. 11 | % n : Number of pixels in the frequency axis. 12 | % lf : Crop output to display only frequencies larger than lf. 13 | % hf : Crop output to display only frequencies less than hf. 14 | % ths : Fraction of values to reassign. 15 | % OUTPUT 16 | % cft : The concentrated time-freq representation of the signal x. 17 | % This implementation requires the function hermf.m. 18 | % Written by John Malik on 2017.4.28, john.malik@duke.edu. 19 | 20 | switch nargin 21 | case 8 22 | ths = 1; 23 | case 7 24 | ths = 1; 25 | lf = 0; 26 | case 6 27 | ths = 1; 28 | hf = inf; 29 | lf = 0; 30 | case 5 31 | ths = 1; 32 | hf = inf; 33 | lf = 0; 34 | n = pow2(nextpow2(length(x))) / 2 + 1; 35 | case 4 36 | ths = 1; 37 | hf = inf; 38 | lf = 0; 39 | hop = 1; 40 | n = pow2(nextpow2(length(x))) / 2 + 1; 41 | case 3 42 | ths = 1; 43 | hf = inf; 44 | lf = 0; 45 | iter = 1; 46 | hop = 1; 47 | n = pow2(nextpow2(length(x))) / 2 + 1; 48 | case 2 49 | error('Select a sampling rate.') 50 | case 1 51 | error('Select a window length.') 52 | case 0 53 | Fs = 200; 54 | x = 2 * mod(1e-2:1e-2:1e2, 1) - 1; 55 | x = x + random('Normal', zeros(size(x)), 0.33); 56 | hlength = 1001; 57 | iter = 10; 58 | lf = 1; 59 | hop = 40; 60 | n = 8000; 61 | hf = 12; 62 | ths = 1; 63 | disp('Testing code on a noisy 2 Hz sawtooth wave.') 64 | end 65 | 66 | % organize input 67 | x = x(:); 68 | if any(isnan(x)) 69 | x = interp1(find(~isnan(x)), x(~isnan(x)), 1:length(x), 'pchip', 'extrap')'; 70 | end 71 | 72 | % time (samples) 73 | NN = length(x); 74 | t = 1:hop:NN; 75 | [~, tcol] = size(t); 76 | 77 | % N-point fft 78 | n = n + 1 - rem(n, 2); 79 | N = 2 * (n - 1); 80 | 81 | % make sure window length is odd 82 | hlength = hlength + 1 - rem(hlength, 2); 83 | Lh = (hlength - 1) / 2; 84 | 85 | % orthogonal hermite windows 86 | bandwidth = 6; 87 | funx = 4; 88 | [H, dH] = hermf(hlength, funx, bandwidth); 89 | 90 | % non-negative frequency axis, cropped 91 | frequency = Fs / 2 * linspace(0, 1, n)'; 92 | frequency(frequency > hf) = []; 93 | neta = length(frequency); 94 | 95 | % iterate 96 | cft = zeros(neta, tcol); 97 | for ii = 1:max(1, iter) 98 | 99 | if iter == 0 100 | disp('Calculating STFT.'); 101 | else disp(['Iteration ' num2str(ii) ': Calculating SST.']); 102 | end 103 | 104 | if iter < 2 105 | h = H(:, 1); dh = dH(:, 1); 106 | else 107 | z = randn(funx, 1) + 1i * randn(funx, 1); 108 | z = z ./ norm(z); 109 | h = conj(H * z); 110 | dh = conj(dH * z); 111 | end 112 | 113 | tfr = zeros(N, tcol); 114 | tfr2 = zeros(N, tcol); 115 | for icol = 1:tcol 116 | ti = t(icol); 117 | tau = -min([n - 1, Lh, ti - 1]):min([n - 1, Lh, NN - ti]); 118 | indices = rem(N + tau, N) + 1; 119 | rSig = x(ti + tau, 1); 120 | tfr(indices, icol) = rSig .* h(Lh + 1 + tau); 121 | tfr2(indices, icol) = rSig .* dh(Lh + 1 + tau); 122 | end 123 | 124 | tfr = fft(tfr); 125 | tfr = tfr(1:neta, :); 126 | 127 | if iter == 0 128 | cft = tfr; 129 | break 130 | end 131 | 132 | tfr2 = fft(tfr2); 133 | tfr2 = tfr2(1:neta, :); 134 | 135 | % reassignment threshold 136 | threshold = quantile(abs(tfr), (1 - ths)); 137 | 138 | % omega operator 139 | neta = length(frequency); 140 | omega = -inf(neta, tcol); 141 | ups = ~bsxfun(@le, abs(tfr), threshold); 142 | omega(ups) = round(N * imag(tfr2(ups) ./ tfr(ups) / (2 * pi))); 143 | 144 | % mapped out of range 145 | index = repmat((1:neta)', [1, tcol]); 146 | omega = index - omega; 147 | id = omega < 1 | omega > neta | ~ups; 148 | omega(id) = index(id); 149 | sst = tfr; sst(id) = 0; 150 | 151 | % reassignment 152 | id = bsxfun(@plus, omega, neta * (0:tcol - 1)); 153 | cft = cft + reshape(accumarray(id(:), sst(:), [tcol * neta, 1]), [neta, tcol]); 154 | 155 | end 156 | 157 | % average 158 | cft = cft / max(1, iter); 159 | 160 | % crop output 161 | cft(frequency < lf, :) = []; 162 | frequency(frequency < lf, :) = []; 163 | 164 | end -------------------------------------------------------------------------------- /ConceFT_J4.m: -------------------------------------------------------------------------------- 1 | function [cft, frequency] = ConceFT_J4(x, Fs, hlength, iter, hop, n, hf, lf, ths) 2 | % Computes the concentrated time-freq representation of the signal x. 3 | % This serial implementation uses less memory but is slower than ConceFT_J3. 4 | % Choose iter = 1 to compute the SST of the signal x. 5 | % Choosing iter = 0 is not supported. 6 | % INPUT 7 | % x : Signal (x should be a column vector). 8 | % Fs : Sampling rate of x. 9 | % hlength: Window length. 10 | % iter : Number of SST computations. 11 | % hop : Calculate the fft every hop samples, starting at 1. 12 | % n : Number of pixels in the frequency axis. 13 | % lf : Crop output to display only frequencies larger than lf. 14 | % hf : Crop output to display only frequencies less than hf. 15 | % ths : Fraction of values to reassign. 16 | % OUTPUT 17 | % cft : The concentrated time-freq representation of the signal x. 18 | % This implementation requires the function hermf.m. 19 | % Written by John Malik on 2017.4.28, john.malik@duke.edu. 20 | 21 | switch nargin 22 | case 8 23 | ths = 1; 24 | case 7 25 | ths = 1; 26 | lf = 0; 27 | case 6 28 | ths = 1; 29 | hf = inf; 30 | lf = 0; 31 | case 5 32 | ths = 1; 33 | hf = inf; 34 | lf = 0; 35 | n = pow2(nextpow2(length(x))) / 2 + 1; 36 | case 4 37 | ths = 1; 38 | hf = inf; 39 | lf = 0; 40 | hop = 1; 41 | n = pow2(nextpow2(length(x))) / 2 + 1; 42 | case 3 43 | ths = 1; 44 | hf = inf; 45 | lf = 0; 46 | iter = 1; 47 | hop = 1; 48 | n = pow2(nextpow2(length(x))) / 2 + 1; 49 | case 2 50 | error('Select a sampling rate.') 51 | case 1 52 | error('Select a window length.') 53 | case 0 54 | Fs = 200; 55 | x = 2 * mod(1e-2:1e-2:1e2, 1) - 1; 56 | x = x + random('Normal', zeros(size(x)), 0.33); 57 | hlength = 1001; 58 | iter = 10; 59 | lf = 1; 60 | hop = 40; 61 | n = 8000; 62 | hf = 12; 63 | ths = 1; 64 | disp('Testing code on a noisy 2 Hz sawtooth wave.') 65 | end 66 | 67 | % organize input 68 | x = x(:); 69 | if any(isnan(x)) 70 | x = interp1(find(~isnan(x)), x(~isnan(x)), 1:length(x), 'pchip', 'extrap')'; 71 | end 72 | 73 | % time (samples) 74 | NN = length(x); 75 | t = 1:hop:NN; 76 | [~, tcol] = size(t); 77 | 78 | % N-point fft 79 | n = n + 1 - rem(n, 2); 80 | N = 2 * (n - 1); 81 | 82 | % make sure window length is odd 83 | hlength = hlength + 1 - rem(hlength, 2); 84 | Lh = (hlength - 1) / 2; 85 | 86 | % orthogonal hermite windows 87 | bandwidth = 6; 88 | funx = 4; 89 | [H, dH] = hermf(hlength, funx, bandwidth); 90 | 91 | % non-negative frequency axis, cropped 92 | frequency = Fs / 2 * linspace(0, 1, n)'; 93 | frequency(frequency > hf) = []; 94 | neta = length(frequency); 95 | 96 | % iterate 97 | cft = zeros(neta, tcol); 98 | for ii = 1:max(1, iter) 99 | 100 | if iter == 0 101 | error('Choose a number of iterations greater than zero.'); 102 | else disp(['Iteration ' num2str(ii) ': Calculating SST.']); 103 | end 104 | 105 | if iter < 2 106 | h = H(:, 1); dh = dH(:, 1); 107 | else 108 | z = randn(funx, 1) + 1i * randn(funx, 1); 109 | z = z ./ norm(z); 110 | h = conj(H * z); 111 | dh = conj(dH * z); 112 | end 113 | 114 | % STFT 115 | tfr = zeros(neta, tcol); 116 | tfr2 = zeros(neta, tcol); 117 | for icol = 1:tcol 118 | 119 | tmp = zeros(N, 1); 120 | tmp2 = zeros(N, 1); 121 | 122 | ti = t(icol); 123 | tau = -min([n - 1, Lh, ti - 1]):min([n - 1, Lh, NN - ti]); 124 | indices = rem(N + tau, N) + 1; 125 | rSig = x(ti + tau, 1); 126 | 127 | tmp(indices) = rSig .* h(Lh + 1 + tau); 128 | tmp = fft(tmp); 129 | tfr(:, icol) = tmp(1:neta); 130 | 131 | tmp2(indices) = rSig .* dh(Lh + 1 + tau); 132 | tmp2 = fft(tmp2); 133 | tfr2(:, icol) = tmp2(1:neta); 134 | 135 | end 136 | 137 | % reassignment threshold 138 | threshold = quantile(abs(tfr), (1 - ths)); 139 | 140 | % omega operator 141 | neta = length(frequency); 142 | omega = -inf(neta, tcol); 143 | ups = ~bsxfun(@le, abs(tfr), threshold); 144 | omega(ups) = round(N * imag(tfr2(ups) ./ tfr(ups) / (2 * pi))); 145 | 146 | % mapped out of range 147 | index = repmat((1:neta)', [1, tcol]); 148 | omega = index - omega; 149 | id = omega < 1 | omega > neta | ~ups; 150 | omega(id) = index(id); 151 | sst = tfr; sst(id) = 0; 152 | 153 | % reassignment 154 | id = bsxfun(@plus, omega, neta * (0:tcol - 1)); 155 | cft = cft + reshape(accumarray(id(:), sst(:), [tcol * neta, 1]), [neta, tcol]); 156 | 157 | end 158 | 159 | % average 160 | cft = cft / max(1, iter); 161 | 162 | % crop output 163 | cft(frequency < lf, :) = []; 164 | frequency(frequency < lf, :) = []; 165 | 166 | end -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 John Malik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # time-frequency-analysis 2 | This is a MATLAB package for the short-time Fourier transform, the synchrosqueezing transform, ConceFT, and the deshape technique. This work builds on the work of F. Auger, P. Flandrin, Li Su, and Hau-tieng Wu. 3 | -------------------------------------------------------------------------------- /SST_J.m: -------------------------------------------------------------------------------- 1 | function [sst, tfr, frequency] = SST_J(x, Fs, hlength, hop, n, hf, lf, ths) 2 | % Computes the synchrosqueezing transform of the signal x. 3 | % INPUT 4 | % x : Signal (x should be a column vector). 5 | % Fs : Sampling rate of x. 6 | % hlength: Window length (in samples). 7 | % hop : Calculate the fft every hop samples, starting at 1. 8 | % n : Number of pixels in the frequency axis. 9 | % lf : Crop output to display only frequencies larger than lf. 10 | % hf : Crop output to display only frequencies less than hf. 11 | % ths : Fraction of values to reassign. 12 | % OUTPUT 13 | % sst : The SST of the signal x. 14 | % tfr : The STFT of the signal x. 15 | % Written by John Malik on 2018.6.22, john.malik@duke.edu. 16 | 17 | switch nargin 18 | case 7 19 | ths = 1; 20 | case 6 21 | ths = 1; 22 | lf = 0; 23 | case 5 24 | ths = 1; 25 | hf = inf; 26 | lf = 0; 27 | case 4 28 | ths = 1; 29 | n = pow2(nextpow2(length(x))) / 2 + 1; 30 | hf = inf; 31 | lf = 0; 32 | case 3 33 | ths = 1; 34 | hop = 1; 35 | n = pow2(nextpow2(length(x))) / 2 + 1; 36 | hf = inf; 37 | lf = 0; 38 | case 2 39 | error('Select a window length.') 40 | case 1 41 | error('Select a sampling rate.') 42 | case 0 43 | Fs = 200; 44 | x = 2 * mod(1e-2:1e-2:1e2, 1) - 1; 45 | hlength = 1001; 46 | lf = 1; 47 | hop = 40; 48 | n = 8000; 49 | hf = 12; 50 | ths = 0.5; 51 | disp('Testing code on a 2 Hz sawtooth wave.') 52 | end 53 | 54 | % window bandwidth 55 | sigma = 0.15; 56 | 57 | % do reassignment 58 | squeeze_flag = 1; 59 | 60 | % organize input 61 | x = x(:); 62 | if any(isnan(x)) 63 | x = interp1(find(~isnan(x)), x(~isnan(x)), 1:length(x), 'pchip', 'extrap')'; 64 | end 65 | 66 | % time (samples) 67 | NN = length(x); 68 | t = 1:hop:NN; 69 | [~, tcol] = size(t); 70 | 71 | % N-point fft 72 | n = n + 1 - rem(n, 2); 73 | N = 2 * (n - 1); 74 | 75 | % make sure window length is odd 76 | hlength = hlength + 1 - rem(hlength, 2); 77 | Lh = (hlength - 1) / 2; 78 | 79 | % gaussian window and its derivative 80 | ex = linspace(-0.5, 0.5, hlength)'; 81 | h = exp(-ex.^2 / 2 / sigma^2); 82 | dh = -ex ./ sigma^2 .* h; 83 | 84 | % perform convolution 85 | tfr = zeros(N, tcol); 86 | tfr2 = zeros(N, tcol); 87 | for icol = 1:tcol 88 | ti = t(icol); 89 | tau = -min([n - 1, Lh, ti - 1]):min([n - 1, Lh, NN - ti]); 90 | indices = rem(N + tau, N) + 1; 91 | rSig = x(ti + tau, 1); 92 | tfr(indices, icol) = rSig .* h(Lh + 1 + tau); 93 | tfr2(indices, icol) = rSig .* dh(Lh + 1 + tau); 94 | end 95 | 96 | % Fourier transform 97 | tfr = fft(tfr); 98 | tfr = tfr(1:n, :); 99 | 100 | % non-negative frequency axis 101 | frequency = Fs / 2 * linspace(0, 1, n)'; 102 | 103 | if squeeze_flag 104 | tfr2 = fft(tfr2); 105 | tfr2 = tfr2(1:n, :); 106 | end 107 | 108 | % crop output 109 | u = frequency <= hf; 110 | tfr = tfr(u, :); 111 | frequency = frequency(u); 112 | 113 | if ~squeeze_flag 114 | sst = tfr; 115 | return 116 | end 117 | 118 | % crop 119 | tfr2 = tfr2(u, :); 120 | 121 | % reassignment threshold 122 | ths = quantile(abs(tfr), (1 - ths)); 123 | 124 | % omega operator 125 | neta = length(frequency); 126 | omega = -inf(neta, tcol); 127 | ups = ~bsxfun(@le, abs(tfr), ths); 128 | omega(ups) = round(N / hlength * imag(tfr2(ups) ./ tfr(ups) / (2 * pi))); 129 | 130 | % mapped out of range 131 | index = repmat((1:neta)', [1, tcol]); 132 | omega = index - omega; 133 | id = omega < 1 | omega > neta | ~ups; 134 | omega(id) = index(id); 135 | sst = tfr; sst(id) = 0; 136 | 137 | % reassignment 138 | id = bsxfun(@plus, omega, neta * (0:tcol - 1)); 139 | sst = reshape(accumarray(id(:), sst(:), [tcol * neta, 1]), [neta, tcol]); 140 | 141 | % crop 142 | tfr(frequency < lf, :) = []; 143 | sst(frequency < lf, :) = []; 144 | frequency(frequency < lf) = []; 145 | 146 | end -------------------------------------------------------------------------------- /SST_J2.m: -------------------------------------------------------------------------------- 1 | function [sst, tfr, frequency] = SST_J2(x, Fs, hlength, hop, n, hf, lf, ths) 2 | % Computes the synchrosqueezing transform of the signal x. 3 | % This serial implementation uses less memory but is slower than SST_J. 4 | % INPUT 5 | % x : Signal (x should be a column vector). 6 | % Fs : Sampling rate of x. 7 | % hlength: Window length (in samples). 8 | % hop : Calculate the fft every hop samples, starting at 1. 9 | % p : Number of pixels in the frequency axis. 10 | % lf : Crop output to display only frequencies larger than lf. 11 | % hf : Crop output to display only frequencies less than hf. 12 | % ths : Fraction of values to reassign. 13 | % OUTPUT 14 | % sst : The SST of the signal x. 15 | % tfr : The STFT of the signal x. 16 | % Written by John Malik on 2018.6.22, john.malik@duke.edu. 17 | 18 | switch nargin 19 | case 7 20 | ths = 1; 21 | case 6 22 | ths = 1; 23 | lf = 0; 24 | case 5 25 | ths = 1; 26 | hf = inf; 27 | lf = 0; 28 | case 4 29 | ths = 1; 30 | n = pow2(nextpow2(length(x))) / 2 + 1; 31 | hf = inf; 32 | lf = 0; 33 | case 3 34 | ths = 1; 35 | hop = 1; 36 | n = pow2(nextpow2(length(x))) / 2 + 1; 37 | hf = inf; 38 | lf = 0; 39 | case 2 40 | error('Select a window length.') 41 | case 1 42 | error('Select a sampling rate.') 43 | case 0 44 | Fs = 200; 45 | x = 2 * mod(1e-2:1e-2:1e2, 1) - 1; 46 | hlength = 1001; 47 | lf = 1; 48 | hop = 40; 49 | n = 8000; 50 | hf = 12; 51 | ths = 0.5; 52 | disp('Testing code on a 2 Hz sawtooth wave.') 53 | end 54 | 55 | % window bandwidth 56 | sigma = 0.15; 57 | 58 | % organize input 59 | x = x(:); 60 | if any(isnan(x)) 61 | x = interp1(find(~isnan(x)), x(~isnan(x)), 1:length(x), 'pchip', 'extrap')'; 62 | end 63 | 64 | % time (samples) 65 | NN = length(x); 66 | t = 1:hop:NN; 67 | [~, tcol] = size(t); 68 | 69 | % N-point fft 70 | n = n + 1 - rem(n, 2); 71 | N = 2 * (n - 1); 72 | 73 | % make sure window length is odd 74 | hlength = hlength + 1 - rem(hlength, 2); 75 | Lh = (hlength - 1) / 2; 76 | 77 | % gaussian window and its derivative 78 | ex = linspace(-0.5, 0.5, hlength)'; 79 | h = exp(-ex.^2 / 2 / sigma^2); 80 | dh = -ex ./ sigma^2 .* h; 81 | 82 | % non-negative frequency axis, cropped 83 | frequency = Fs / 2 * linspace(0, 1, n)'; 84 | neta = sum(frequency <= hf); 85 | frequency = frequency(frequency <= hf); 86 | 87 | % STFT 88 | tfr = zeros(neta, tcol); 89 | tfr2 = zeros(neta, tcol); 90 | for icol = 1:tcol 91 | 92 | tmp = zeros(N, 1); 93 | tmp2 = zeros(N, 1); 94 | 95 | ti = t(icol); 96 | tau = -min([n - 1, Lh, ti - 1]):min([n - 1, Lh, NN - ti]); 97 | indices = rem(N + tau, N) + 1; 98 | rSig = x(ti + tau, 1); 99 | 100 | tmp(indices) = rSig .* h(Lh + 1 + tau); 101 | tmp = fft(tmp); 102 | tfr(:, icol) = tmp(1:neta); 103 | 104 | tmp2(indices) = rSig .* dh(Lh + 1 + tau); 105 | tmp2 = fft(tmp2); 106 | tfr2(:, icol) = tmp2(1:neta); 107 | 108 | end 109 | 110 | % reassignment threshold 111 | ths = quantile(abs(tfr), (1 - ths)); 112 | 113 | % omega operator 114 | omega = -inf(neta, tcol); 115 | ups = ~bsxfun(@le, abs(tfr), ths); 116 | omega(ups) = round(N / hlength * imag(tfr2(ups) ./ tfr(ups) / (2 * pi))); 117 | 118 | % mapped out of range 119 | index = repmat((1:neta)', [1, tcol]); 120 | omega = index - omega; 121 | id = omega < 1 | omega > neta | ~ups; 122 | omega(id) = index(id); 123 | sst = tfr; sst(id) = 0; 124 | 125 | % reassignment 126 | id = bsxfun(@plus, omega, neta * (0:tcol - 1)); 127 | sst = reshape(accumarray(id(:), sst(:), [tcol * neta, 1]), [neta, tcol]); 128 | 129 | % crop output 130 | tfr(frequency < lf, :) = []; 131 | sst(frequency < lf, :) = []; 132 | frequency(frequency < lf) = []; 133 | 134 | end -------------------------------------------------------------------------------- /STFT_J.m: -------------------------------------------------------------------------------- 1 | function [tfr, frequency] = STFT_J(x, Fs, hlength, hop, n, hf, lf) 2 | % Computes the short-time Fourier transform of the signal x. 3 | % INPUT 4 | % x : Signal (x should be a column vector). 5 | % Fs : Sampling rate of x. 6 | % hlength: Window length (in samples). 7 | % hop : Calculate the fft every hop samples, starting at 1. 8 | % n : Number of pixels in the frequency axis. 9 | % lf : Crop output to display only frequencies larger than lf. 10 | % hf : Crop output to display only frequencies less than hf. 11 | % OUTPUT 12 | % tfr : The STFT of the signal x. 13 | % Written by John Malik on 2018.6.22, john.malik@duke.edu. 14 | 15 | switch nargin 16 | case 6 17 | lf = 0; 18 | case 5 19 | hf = inf; 20 | lf = 0; 21 | case 4 22 | n = pow2(nextpow2(length(x))) / 2 + 1; 23 | hf = inf; 24 | lf = 0; 25 | case 3 26 | hop = 1; 27 | n = pow2(nextpow2(length(x))) / 2 + 1; 28 | hf = inf; 29 | lf = 0; 30 | case 2 31 | error('Select a window length.') 32 | case 1 33 | error('Select a sampling rate.') 34 | case 0 35 | Fs = 200; 36 | x = 2 * mod(1e-2:1e-2:1e2, 1) - 1; 37 | hlength = 1001; 38 | lf = 1; 39 | hop = 40; 40 | n = 8000; 41 | hf = 12; 42 | disp('Testing code on a 2 Hz sawtooth wave.') 43 | end 44 | 45 | % window bandwidth 46 | sigma = 0.15; 47 | 48 | % organize input 49 | x = x(:); 50 | if any(isnan(x)) 51 | x = interp1(find(~isnan(x)), x(~isnan(x)), 1:length(x), 'pchip', 'extrap')'; 52 | end 53 | 54 | % time (samples) 55 | NN = length(x); 56 | t = 1:hop:NN; 57 | [~, tcol] = size(t); 58 | 59 | % N-point fft 60 | n = n + 1 - rem(n, 2); 61 | N = 2 * (n - 1); 62 | 63 | % make sure window length is odd 64 | hlength = hlength + 1 - rem(hlength, 2); 65 | Lh = (hlength - 1) / 2; 66 | 67 | % gaussian window 68 | ex = linspace(-0.5, 0.5, hlength)'; 69 | h = exp(-ex.^2 / 2 / sigma^2); 70 | 71 | % non-negative frequency axis, cropped 72 | frequency = Fs / 2 * linspace(0, 1, n)'; 73 | neta = sum(frequency <= hf); 74 | frequency = frequency(frequency <= hf); 75 | 76 | % perform convolution 77 | tfr = zeros(N, tcol); 78 | for icol = 1:tcol 79 | ti = t(icol); 80 | tau = -min([n - 1, Lh, ti - 1]):min([n - 1, Lh, NN - ti]); 81 | indices = rem(N + tau, N) + 1; 82 | rSig = x(ti + tau, 1); 83 | tfr(indices, icol) = rSig .* h(Lh + 1 + tau); 84 | end 85 | 86 | % Fourier transform 87 | tfr = fft(tfr); 88 | tfr = tfr(1:neta, :); 89 | 90 | % crop output 91 | tfr(frequency < lf, :) = []; 92 | frequency(frequency < lf) = []; 93 | 94 | end -------------------------------------------------------------------------------- /STFT_J2.m: -------------------------------------------------------------------------------- 1 | function [tfr, frequency] = STFT_J2(x, Fs, hlength, hop, n, hf, lf) 2 | % Computes the short-time Fourier transform of the signal x. 3 | % This serial implementation uses less memory but is slower than STFT_J. 4 | % INPUT 5 | % x : Signal (x should be a column vector). 6 | % Fs : Sampling rate of x. 7 | % hlength: Window length (in samples). 8 | % hop : Calculate the fft every hop samples, starting at 1. 9 | % p : Number of pixels in the frequency axis. 10 | % lf : Crop output to display only frequencies larger than lf. 11 | % hf : Crop output to display only frequencies less than hf. 12 | % OUTPUT 13 | % tfr : The STFT of the signal x. 14 | % Written by John Malik on 2018.6.22, john.malik@duke.edu. 15 | 16 | switch nargin 17 | case 6 18 | lf = 0; 19 | case 5 20 | hf = inf; 21 | lf = 0; 22 | case 4 23 | n = pow2(nextpow2(length(x))) / 2 + 1; 24 | hf = inf; 25 | lf = 0; 26 | case 3 27 | hop = 1; 28 | n = pow2(nextpow2(length(x))) / 2 + 1; 29 | hf = inf; 30 | lf = 0; 31 | case 2 32 | error('Select a window length.') 33 | case 1 34 | error('Select a sampling rate.') 35 | case 0 36 | Fs = 200; 37 | x = 2 * mod(1e-2:1e-2:1e2, 1) - 1; 38 | hlength = 1001; 39 | lf = 1; 40 | hop = 40; 41 | n = 8000; 42 | hf = 12; 43 | disp('Testing code on a 2 Hz sawtooth wave.') 44 | end 45 | 46 | % window bandwidth 47 | sigma = 0.15; 48 | 49 | % organize input 50 | x = x(:); 51 | if any(isnan(x)) 52 | x = interp1(find(~isnan(x)), x(~isnan(x)), 1:length(x), 'pchip', 'extrap')'; 53 | end 54 | 55 | % time (samples) 56 | NN = length(x); 57 | t = 1:hop:NN; 58 | [~, tcol] = size(t); 59 | 60 | % N-point fft 61 | n = n + 1 - rem(n, 2); 62 | N = 2 * (n - 1); 63 | 64 | % make sure window length is odd 65 | hlength = hlength + 1 - rem(hlength, 2); 66 | Lh = (hlength - 1) / 2; 67 | 68 | % gaussian window and its derivative 69 | ex = linspace(-0.5, 0.5, hlength)'; 70 | h = exp(-ex.^2 / 2 / sigma^2); 71 | 72 | % non-negative frequency axis, cropped 73 | frequency = Fs / 2 * linspace(0, 1, n)'; 74 | neta = sum(frequency <= hf); 75 | frequency = frequency(frequency <= hf); 76 | 77 | % STFT 78 | tfr = zeros(neta, tcol); 79 | for icol = 1:tcol 80 | 81 | tmp = zeros(N, 1); 82 | 83 | ti = t(icol); 84 | tau = -min([n - 1, Lh, ti - 1]):min([n - 1, Lh, NN - ti]); 85 | indices = rem(N + tau, N) + 1; 86 | rSig = x(ti + tau, 1); 87 | 88 | tmp(indices) = rSig .* h(Lh + 1 + tau); 89 | tmp = fft(tmp); 90 | tfr(:, icol) = tmp(1:neta); 91 | 92 | end 93 | 94 | % crop output 95 | tfr(frequency < lf, :) = []; 96 | frequency(frequency < lf) = []; 97 | 98 | end -------------------------------------------------------------------------------- /deShape_J.m: -------------------------------------------------------------------------------- 1 | function [deshape, ceps, mask, tfr, frequency, quefrency] = deShape_J(x, Fs, hlength, hf, gamma, hop, n, lf, ths) 2 | % Computes the de-shape synchrosqueezing transform of the signal x. 3 | % INPUT 4 | % x : Signal (x should be a column vector). 5 | % Fs : Sampling rate of x. 6 | % hlength: Window length (in samples). 7 | % hop : Calculate the fft every hop samples, starting at 1. 8 | % n : Number of pixels in the frequency axis. 9 | % lf : Crop output to display only frequencies larger than lf. 10 | % hf : Least upper bound on fundamental frequency of all components. 11 | % gamma : Cepstral power. 12 | % ths : Fraction of values to reassign. 13 | % OUTPUT 14 | % deshape: The de-shape SST of the signal x. 15 | % ceps : The short-time cepstral transform of the signal x. 16 | % mask : The inverted short-time cepstral transform of the signal x. 17 | % tfr : The STFT of the signal x. 18 | % Written by John Malik on 2018.6.22, john.malik@duke.edu. 19 | 20 | switch nargin 21 | case 8 22 | ths = 1; 23 | case 7 24 | ths = 1; 25 | lf = 0; 26 | case 6 27 | n = pow2(nextpow2(length(x))) / 2 + 1; 28 | lf = 0; 29 | ths = 1; 30 | case 5 31 | hop = 1; 32 | n = pow2(nextpow2(length(x))) / 2 + 1; 33 | lf = 0; 34 | ths = 1; 35 | case 4 36 | gamma = 0.2; 37 | hop = 1; 38 | n = pow2(nextpow2(length(x))) / 2 + 1; 39 | lf = 0; 40 | ths = 1; 41 | case 3 42 | error('Select an upper bound for the fundamental frequency of all components.') 43 | case 2 44 | error('Select a window length.') 45 | case 1 46 | error('Select a sampling rate.') 47 | case 0 48 | Fs = 200; 49 | x = 2 * mod(1e-2:1e-2:1e2, 1) - 1; 50 | hlength = 1001; 51 | lf = 1; 52 | gamma = 0.2; 53 | hop = 40; 54 | n = 8000; 55 | hf = 5; 56 | ths = 0.1; 57 | disp('Testing code on a 2 Hz sawtooth wave.') 58 | end 59 | 60 | % window bandwidth 61 | sigma = 0.15; 62 | 63 | % do reassignment 64 | squeeze_flag = 1; 65 | 66 | % organize input 67 | x = x(:); 68 | if any(isnan(x)) 69 | x = interp1(find(~isnan(x)), x(~isnan(x)), 1:length(x), 'pchip', 'extrap')'; 70 | end 71 | 72 | % time (samples) 73 | NN = length(x); 74 | t = 1:hop:NN; 75 | [~, tcol] = size(t); 76 | 77 | % N-point fft 78 | n = n + 1 - rem(n, 2); 79 | N = 2 * (n - 1); 80 | 81 | % make sure window length is odd 82 | hlength = hlength + 1 - rem(hlength, 2); 83 | Lh = (hlength - 1) / 2; 84 | 85 | % gaussian window and its derivative 86 | ex = linspace(-0.5, 0.5, hlength)'; 87 | h = exp(-ex.^2 / 2 / sigma^2); 88 | dh = -ex ./ sigma^2 .* h; 89 | 90 | % perform convolution 91 | tfr = zeros(N, tcol); 92 | tfr2 = zeros(N, tcol); 93 | for icol = 1:tcol 94 | ti = t(icol); 95 | tau = -min([n - 1, Lh, ti - 1]):min([n - 1, Lh, NN - ti]); 96 | indices = rem(N + tau, N) + 1; 97 | rSig = x(ti + tau, 1); 98 | % rSig = rSig - mean(rSig); 99 | tfr(indices, icol) = rSig .* h(Lh + 1 + tau); 100 | tfr2(indices, icol) = rSig .* dh(Lh + 1 + tau); 101 | end 102 | 103 | % Fourier transform 104 | tfr = fft(tfr); 105 | tfr = tfr(1:n, :); 106 | 107 | % non-negative frequency axis 108 | frequency = Fs / 2 * linspace(0, 1, n)'; 109 | 110 | if squeeze_flag 111 | tfr2 = fft(tfr2); 112 | tfr2 = tfr2(1:n, :); 113 | end 114 | 115 | % short-time cepstral transform and quefrency axis 116 | ceps = ifft(abs(tfr).^gamma, N, 1); 117 | ceps = real(ceps(1:n, :)); 118 | quefrency = (0:n-1)' / Fs; 119 | 120 | % remove envelope 121 | ceps(quefrency < 1 / hf, :) = 0; 122 | 123 | % inverted short-time cepstral transform 124 | mask = interp1(1 ./ quefrency(2:end), ceps(2:end, :), frequency); 125 | 126 | % handle negative entries 127 | ceps = max(ceps, 0); 128 | mask = max(mask, 0); 129 | 130 | % crop output 131 | u = frequency <= hf; 132 | tfr = tfr(u, :); 133 | mask = mask(u, :); 134 | frequency = frequency(u); 135 | 136 | % crop output (ceps) 137 | ceps = ceps(quefrency <= 2 / lf, :); 138 | quefrency = quefrency(quefrency <= 2 / lf); 139 | 140 | % deshape short-time Fourier transform 141 | deshape = tfr .* mask; 142 | 143 | if ~squeeze_flag 144 | return 145 | end 146 | 147 | % crop 148 | tfr2 = tfr2(u, :); 149 | 150 | % reassignment threshold 151 | ths = quantile(abs(tfr), (1 - ths)); 152 | 153 | % omega operator 154 | neta = length(frequency); 155 | omega = -inf(neta, tcol); 156 | ups = ~bsxfun(@le, abs(tfr), ths); 157 | omega(ups) = round(N / hlength * imag(tfr2(ups) ./ tfr(ups) / (2 * pi))); 158 | 159 | % mapped out of range 160 | index = repmat((1:neta)', [1, tcol]); 161 | omega = index - omega; 162 | id = omega < 1 | omega > neta | ~ups; 163 | omega(id) = index(id); 164 | deshape(id) = 0; 165 | 166 | % reassignment 167 | id = bsxfun(@plus, omega, neta * (0:tcol - 1)); 168 | deshape = reshape(accumarray(id(:), deshape(:), [tcol * neta, 1]), [neta, tcol]); 169 | 170 | % crop 171 | tfr(frequency < lf, :) = []; 172 | mask(frequency < lf, :) = []; 173 | deshape(frequency < lf, :) = []; 174 | frequency(frequency < lf) = []; 175 | 176 | end -------------------------------------------------------------------------------- /deShape_Jb.m: -------------------------------------------------------------------------------- 1 | function [deshape, ceps, mask, tfr, frequency, quefrency] = deShape_Jb(x, Fs, hlength, hf, gamma, hop, n, lf, ths) 2 | % Computes the de-shape synchrosqueezing transform of the signal x. 3 | % INPUT 4 | % x : Signal (x should be a column vector). 5 | % Fs : Sampling rate of x. 6 | % hlength: Window length (in samples). 7 | % hop : Calculate the fft every hop samples, starting at 1. 8 | % n : Number of pixels in the frequency axis. 9 | % lf : Crop output to display only frequencies larger than lf. 10 | % hf : Least upper bound on fundamental frequency of all components. 11 | % gamma : Cepstral power. 12 | % ths : Fraction of values to reassign. 13 | % OUTPUT 14 | % deshape: The de-shape SST of the signal x. 15 | % ceps : The short-time cepstral transform of the signal x. 16 | % mask : The inverted short-time cepstral transform of the signal x. 17 | % tfr : The STFT of the signal x. 18 | % Written by John Malik on 2018.6.22, john.malik@duke.edu. 19 | 20 | switch nargin 21 | case 8 22 | ths = 1; 23 | case 7 24 | ths = 1; 25 | lf = 0; 26 | case 6 27 | n = pow2(nextpow2(length(x))) / 2 + 1; 28 | lf = 0; 29 | ths = 1; 30 | case 5 31 | hop = 1; 32 | n = pow2(nextpow2(length(x))) / 2 + 1; 33 | lf = 0; 34 | ths = 1; 35 | case 4 36 | gamma = 0.2; 37 | hop = 1; 38 | n = pow2(nextpow2(length(x))) / 2 + 1; 39 | lf = 0; 40 | ths = 1; 41 | case 3 42 | error('Select an upper bound for the fundamental frequency of all components.') 43 | case 2 44 | error('Select a window length.') 45 | case 1 46 | error('Select a sampling rate.') 47 | case 0 48 | Fs = 200; 49 | x = 2 * mod(1e-2:1e-2:1e2, 1) - 1; 50 | hlength = 1001; 51 | lf = 1; 52 | gamma = 0.2; 53 | hop = 40; 54 | n = 8000; 55 | hf = 5; 56 | ths = 0.1; 57 | disp('Testing code on a 2 Hz sawtooth wave.') 58 | end 59 | 60 | % cepstrum resampling factor 61 | alpha = 20; 62 | 63 | % window bandwidth 64 | sigma = 0.15; 65 | 66 | % do reassignment 67 | squeeze_flag = 1; 68 | 69 | % organize input 70 | x = x(:); 71 | if any(isnan(x)) 72 | x = interp1(find(~isnan(x)), x(~isnan(x)), 1:length(x), 'pchip', 'extrap')'; 73 | end 74 | 75 | % time (samples) 76 | NN = length(x); 77 | t = 1:hop:NN; 78 | [~, tcol] = size(t); 79 | 80 | % N-point fft 81 | n = n + 1 - rem(n, 2); 82 | N = 2 * (n - 1); 83 | 84 | % make sure window length is odd 85 | hlength = hlength + 1 - rem(hlength, 2); 86 | Lh = (hlength - 1) / 2; 87 | 88 | % gaussian window and its derivative 89 | ex = linspace(-0.5, 0.5, hlength)'; 90 | h = exp(-ex.^2 / 2 / sigma^2); 91 | dh = -ex ./ sigma^2 .* h; 92 | 93 | % perform convolution 94 | tfr = zeros(N, tcol); 95 | tfr2 = zeros(N, tcol); 96 | for icol = 1:tcol 97 | ti = t(icol); 98 | tau = -min([n - 1, Lh, ti - 1]):min([n - 1, Lh, NN - ti]); 99 | indices = rem(N + tau, N) + 1; 100 | rSig = x(ti + tau, 1); 101 | rSig = rSig - mean(rSig); 102 | tfr(indices, icol) = rSig .* h(Lh + 1 + tau); 103 | tfr2(indices, icol) = rSig .* dh(Lh + 1 + tau); 104 | end 105 | 106 | % Fourier transform 107 | tfr = fft(tfr); 108 | tfr = tfr(1:n, :); 109 | 110 | % non-negative frequency axis 111 | frequency = Fs / 2 * linspace(0, 1, n)'; 112 | 113 | if squeeze_flag 114 | tfr2 = fft(tfr2); 115 | tfr2 = tfr2(1:n, :); 116 | end 117 | 118 | % short-time cepstral transform and quefrency axis 119 | ceps = ifft(abs(tfr).^gamma, N, 1); 120 | ceps = real(ceps(1:n, :)); 121 | quefrency = (0:n-1)' / Fs; 122 | 123 | % remove envelope 124 | ceps(quefrency < 1 / hf, :) = 0; 125 | 126 | % step of unknown purpose 127 | flo = tfr; 128 | flo(abs(tfr) < real(fft(ceps)).^(1 / gamma)) = 0; 129 | 130 | % upsample cepstrum by a factor of alpha 131 | uceps = interp1(1:n, ceps, 1:1/alpha:n); 132 | uquefrency = (0:1/alpha:n-1)' / Fs; 133 | 134 | % inverted short-time cepstral transform 135 | mask = zeros(size(tfr)); 136 | df = frequency(end) / (2 * n); 137 | for i = 1:length(frequency) 138 | qj = find(uquefrency > 1 / (frequency(i) + df) & ... 139 | uquefrency <= 1 / (frequency(i) - df)); 140 | if isempty(qj) 141 | continue 142 | end 143 | mask(i, :) = sum(diag(1 ./ qj) * uceps(qj, :), 1); 144 | end 145 | 146 | % force negative entries to be zero 147 | ceps = max(ceps, 0); 148 | mask = max(mask, 0); 149 | 150 | % crop output 151 | u = frequency <= hf; 152 | tfr = tfr(u, :); 153 | mask = mask(u, :); 154 | frequency = frequency(u); 155 | flo = flo(u, :); 156 | 157 | % crop output (ceps) 158 | ceps = ceps(quefrency <= 2 / lf, :); 159 | quefrency = quefrency(quefrency <= 2 / lf); 160 | 161 | % deshape short-time Fourier transform 162 | deshape = flo .* mask; 163 | 164 | if ~squeeze_flag 165 | return 166 | end 167 | 168 | % crop 169 | tfr2 = tfr2(u, :); 170 | 171 | % reassignment threshold 172 | ths = quantile(abs(tfr), (1 - ths)); 173 | 174 | % omega operator 175 | neta = length(frequency); 176 | omega = -inf(neta, tcol); 177 | ups = ~bsxfun(@le, abs(tfr), ths); 178 | omega(ups) = round(N / hlength * imag(tfr2(ups) ./ tfr(ups) / (2 * pi))); 179 | 180 | % mapped out of range 181 | index = repmat((1:neta)', [1, tcol]); 182 | omega = index - omega; 183 | id = omega < 1 | omega > neta | ~ups; 184 | omega(id) = index(id); 185 | deshape(id) = 0; 186 | 187 | % reassignment 188 | id = bsxfun(@plus, omega, neta * (0:tcol - 1)); 189 | deshape = reshape(accumarray(id(:), deshape(:), [tcol * neta, 1]), [neta, tcol]); 190 | 191 | % crop 192 | tfr(frequency < lf, :) = []; 193 | mask(frequency < lf, :) = []; 194 | deshape(frequency < lf, :) = []; 195 | frequency(frequency < lf) = []; 196 | 197 | end -------------------------------------------------------------------------------- /hermf.m: -------------------------------------------------------------------------------- 1 | function [h, Dh, tt] = hermf(N, M, tm) 2 | % P. Flandrin & J. Xiao, 2005 3 | % Computes a set of orthonormal Hermite functions 4 | % INPUT 5 | % N : Number of points (must be odd). 6 | % M : Maximum order. 7 | % tm : Half-time support (>= 6 recommended). 8 | % OUTPUT 9 | % h : Hermite functions (N x M). 10 | % Dh : Derivatives (N x M). 11 | % tt : Time vector (N x 1). 12 | 13 | dt = 2 * tm / (N - 1); 14 | tt = linspace(-tm, tm, N); 15 | g = exp(-tt.^2 / 2); 16 | 17 | P = zeros(M + 1, N); 18 | P(1, :) = ones(1, N); 19 | P(2, :) = 2 * tt; 20 | for k = 3:M + 1 21 | P(k, :) = 2 * tt .* P(k - 1, :) - 2 * (k - 2) * P(k - 2, :); 22 | end 23 | 24 | H = zeros(M + 1, N); 25 | for k = 1:M + 1 26 | H(k, :) = P(k, :) .* g / sqrt(sqrt(pi) * 2^(k - 1) * gamma(k) / dt); 27 | end 28 | h = H(1:M, :); 29 | 30 | Dh = zeros(size(h)); 31 | for k = 1:M 32 | Dh(k, :) = (tt .* H(k, :) - sqrt(2 * k) * H(k + 1, :)) * dt; 33 | end 34 | h = h'; Dh = Dh'; tt = tt'; 35 | 36 | end 37 | -------------------------------------------------------------------------------- /imageSQ.m: -------------------------------------------------------------------------------- 1 | function imageSQ(M, y, h, l) 2 | % IMAGESQ Displays the time-frequency representation M as a black and white 3 | % image. A clipping effect is used to normalize M. 4 | % INPUT 5 | % M : Time-frequency representation to be visualized. 6 | % y : Frequency axis. 7 | % h : Range above which to clip, a number in (0, 1], e.g. 0.995 8 | % l : Discard numbers below this quantile (default = 0). 9 | % Written by John Malik on 2018.6.22, john.malik@duke.edu. 10 | 11 | switch nargin 12 | case 1 13 | y = 1; 14 | h = 1; 15 | l = 0; 16 | case 2 17 | h = 1; 18 | l = 0; 19 | case 3 20 | l = 0; 21 | end 22 | 23 | q = quantile(M(:), [h, l]); 24 | M(M > q(1)) = q(1); 25 | M(M < q(2)) = 0; 26 | 27 | h = get(groot, 'defaultAxesTickLabelInterpreter'); 28 | set(groot, 'defaultAxesTickLabelInterpreter', 'latex'); 29 | 30 | imagesc(1, y, M) 31 | axis xy 32 | colormap(1 - gray) 33 | 34 | set(groot, 'defaultAxesTickLabelInterpreter', h); 35 | 36 | end 37 | --------------------------------------------------------------------------------