adjusting swept signal equation - matlab

When I do a spectrogram in matlab / octave I can create a swept signal that looks like the RED plot line below. But how can I create a swept signal like the BLUE line in the 1st plot using the equation below.
thanks to Daniel and David for getting me this far with the code is below
startfreq=200;
fs=44100;
endfreq=20;
dursec= 10;%duration of signal in seconds
t=(0:dursec*fs)/fs; %Time vector
alpha=log(startfreq/endfreq)/dursec;
sig = exp(-j*2*pi*startfreq/alpha*exp(-alpha*t));
sig=(sig/max(abs(sig))*.8); %normalize signal
wavwrite([sig'] ,fs,32,strcat('/tmp/del.wav')); %export file
specgram(sig,150,400);
1st plot
2nd plot
How can I fix the the equation in the variable sig to get it to look like the BLUE line in the 1st plot?
3rd plot

This question is almost an month old, so you might have figured this out by now. Here's an answer in case you are still interested.
It appears that your current model for the frequency is
freq(t) = b*exp(-alpha*t)
with
freq(0) = b = startfreq
freq(dursec) = b*exp(-alpha*dursec) = endfreq
There are two free parameters (b and alpha), and two equations. The first equation, b = startfreq, gives us b (trivially).
Solving the last equation for alpha gives
alpha = -log(endfreq/startfreq)/dursec
= log(startfreq/endfreq)/dursec
So
freq(t) = startfreq * exp(-alpha*t)
To use this as the instantaneous frequency of a frequency-swept signal,
we need the integral, which I'll call phase(t):
phase(t) = -(startfreq/alpha) * exp(-alpha*t)
The (complex) frequency-swept signal is then
sig(t) = exp(2*pi*j * phase(t))
The real part of this signal is
sig(t) = cos(2*pi*phase(t))
That explains your current code. To generate a chirp whose frequency varies like the blue curve, you need a different model for the frequency. A more general model than the one used above is
freq(t) = a + b*exp(-alpha*t)
The requirements at t=0 and t=dursec are
freq(0) = a + b = startfreq
freq(dursec) = a + b*exp(-alpha*dursec) = endfreq
That's two equation, but we now have three parameters: a, b, and alpha. I'll use the two equations to determine a and b, and leave alpha as a free parameter. Solving gives
b = (startfreq - endfreq)/(1 - exp(-alpha*dursec))
a = startfreq - b
Integrating the model gives
phase(t) = a*t - (b/alpha)*exp(-alpha*t)
alpha is an arbitrary parameter. Following the formula from the first model, I'll use:
alpha = abs(log(startfreq/endfreq))/dursec
The following is a complete script. Note that I also changed the use of exp(-j*2*pi*...) to cos(2*pi*...). The factor 0.8 is there to match your code.
startfreq = 20;
endfreq = 200;
fs = 44100;
dursec = 10; % duration of signal in seconds
t = (0:dursec*fs)/fs; % Time vector
if (startfreq == endfreq)
phase = startfreq * t;
else
alpha = abs(log(endfreq/startfreq))/dursec;
b = (startfreq - endfreq)/(1 - exp(-alpha*dursec));
a = startfreq - b;
phase = a*t - (b/alpha)*exp(-alpha*t);
endif
sig = 0.8 * cos(2*pi*phase);
wavwrite([sig'] ,fs,32,strcat('del.wav')); % export file
specgram(sig,150,400);

Related

Input & Coefficients from time domain to frequency, add them together and back to time domain

I am currently studying computer science and i have a task to solve for my lab project. I have to transfer input's signal & coefficients' from time domain to frequency domain, add them together and transfer back to time domain. My results have to match filter function output. However i cannot seem to find what am doing wrong here. I think its something wrong when i add two frequency via conj function. Unfortunately neither my teacher nor my lab supervisor are interested in actually teaching anything so i have to find answers on my own. Hope you guys can help.
clc
clear
B = [0.2];
A = [1,-0.5];
xt = ones(1,20);
xt = padarray(xt,[0,100])
A1 = 1;
A2 = 1;
f1 = 1;
f2 = 25;
fs = 1000;
xd = fft(xt);
wd = freqz(B,A,length(xt));
y = filter(B,A,xt);
yd = conj((wd)').*xd;
yt = real(ifft(yd));
subplot(4,2,1);
plot(xt)
title('Input signal')
subplot(4,2,2);
plot(abs(xd))
title('Input in frequency domain')
subplot(4,2,4);
plot(abs(wd))
title('Coefficients in frequency domain')
subplot(4,2,7);
plot(y)
title('Output using FILTER function')
subplot(4,2,6);
plot(yd)
title('Adding input with coefficients in frequency domain')
subplot(4,2,8);
plot(yt)
title('Back to time domain using IFFT')
The matlab function freqz() can be a little misleading. The "FFT" domain of your coefficients needs to be generated differently. Replace your stuff with the following code, and it should give you what you want:
xt = xt.';
xd = fft(xt);
wd = freqz(B,A,length(xt),'whole');
y = filter(B,A,xt);
yd = wd.*xd;
yt = ifft(yd);
figure
plot(abs(xd))
hold on
plot(abs(wd))
figure
plot(y,'.k','markersize',20)
hold on
plot(yt,'k')
hold off
Also, a note on the ' operator with complex vectors: unless you use the .' operator (e.g., x = x.'), it will transpose the vector while taking the complex conjugate, i.e., (1+1i).' = (1+1i) while (1+1i)' = (1-1i)

saving output of for loop when dimensions of output vary as a function of iterator in matlab

I am attempting to fit some UV/Vis absorbance spectra to published reference standards. In general, the absorbance one obtains from the spectrometer is equal to a linear combination of the concentration of each absorber multiplied by the cross-section of absorption for each molecule at each wavelength (and multiplied by the pathlength of the spectrometer).
That said, not all spectrometers are precise in the x axis (wavelength), so some adjustment may be necessary to fit one's experimental data to the reference standards.
In this script, I am adjusting the index of my wavelength and spectral intensity to see if integer steps of my spectra result in a better fit to the reference standards (each step is 0.08 nm). Of course, I need to save the output of the fit parameters; however, since each fit has a different set of dimensions, I'm having difficulty just throwing them into a structure(k) (commented out in the following code snippet).
If anyone has a tip or hint, I'd be very appreciative. The relevant portion of my sample code follows:
for i = -15:15
lengthy = length(wavelengthy)
if i >= 0
xvalue = (1:lengthy - abs(i))
yvalue = (1+abs(i):lengthy)
else
xvalue = (1+abs(i):lengthy)
yvalue = (1: lengthy - abs(i))
end
Phi = #(k,wavelengthy) ( O3standard(yvalue) .* k(1) + Cl2standard(yvalue) .* k(2) + ClOstandard(yvalue) .* k(3) + OClOstandard(yvalue) .* k(4));
[khat, resnorm, residual, exitflag, output, lambda, jacobian] = lsqcurvefit(Phi,k0,wavelengthy(xvalue),workingspectra(yvalue), lowerbound, upperbound, options);
%parameters.khat(k,:) = khat;
%parameters.jacobian(k,:) = jacobian;
%parameters.exitflag(k,:) = exitflag;
%parameters.output(k,:) = output;
%parameters.residuals(k,:) = residual
%concentrations(:,k) = khat./pathlength
k=k+1
end

MATLAB code for Harmonic Product Spectrum

Can someone tell me how I can implement Harmonic Product Spectrum using MATLAB to find the fundamental frequency of a note in the presence of harmonics?? I know I'm supposed to downsample my signal a number of times (after performing fft of course) and then multiply them with the original signal.
Say my fft signal is "FFT1"
then the code would roughly be like
hps1 = downsample(FFT1,2);
hps2 = downsample(FFT1,3);
hps = FFT1.*hps1.*hps2;
Is this code correct??? I want to know if I've downsampled properly and since each variable has a different length multiplying them results in matrix dimension error.. I really need some real quick help as its for a project work... Really desperate....
Thanx in advance....
OK you can't do "hps = FFT1.*hps1.*hps2;" for each downsampled data, do you have different sizes ...
I did a example for you how make a very simple Harmonic Product Spectrum (HPS) using 5 harmonics decimation (downsample), I just test in sinusoidal signals, I get very near fundamental frequency in my tests.
This code only shows how to compute the main steps of the algorithm, is very likely that you will need improve it !
Source:
%[x,fs] = wavread('ederwander_IN_250Hz.wav');
CorrectFactor = 0.986;
threshold = 0.2;
%F0 start test
f = 250;
fs = 44100;
signal= 0.9*sin(2*pi*f/fs*(0:9999));
x=signal';
framed = x(1:4096);
windowed = framed .* hann(length(framed));
FFT = fft(windowed, 4096);
FFT = FFT(1 : size(FFT,1) / 2);
FFT = abs(FFT);
hps1 = downsample(FFT,1);
hps2 = downsample(FFT,2);
hps3 = downsample(FFT,3);
hps4 = downsample(FFT,4);
hps5 = downsample(FFT,5);
y = [];
for i=1:length(hps5)
Product = hps1(i) * hps2(i) * hps3(i) * hps4(i) * hps5(i);
y(i) = [Product];
end
[m,n]=findpeaks(y, 'SORTSTR', 'descend');
Maximum = n(1);
%try fix octave error
if (y(n(1)) * 0.5) > (y(n(2))) %& ( ( m(2) / m(1) ) > threshold )
Maximum = n(length(n));
end
F0 = ( (Maximum / 4096) * fs ) * CorrectFactor
plot(y)
HPS usually generates an error showing the pitch one octave up, I change a bit a code, see above :-)

Issues in fitting data to linear model

Assuming a noiseless AR(1) process y(t)= a*y(t-1) . I have following conceptual questions and shall be glad for the clarification.
Q1 - Discrepancy between mathematical formulation and implementation - The mathematical formulation of AR model is in the form of y(t) = - summmation over i=1 to p[a*y(t-p)] + eta(t) where p=model order and eta(t) is a white gaussian noise. But when estimating coefficients using any method like arburg() or the least square, we simply call that function. I do not know if a white gaussian noise is implicitly added. Then, when we resolve the AR equation with the estimated coefficients, I have seen that the negative sign is not considered nor the noise term added.
What is the correct representation of AR model and how do I find the average coefficients over k number of trials when I have only a single sample of 1000 data points?
Q2 - Coding problem in How to simulate fitted_data for k number of trials and then find the residuals - I fitted a data "data" generated from unknown system and obtained the coefficient by
load('data.txt');
for trials = 1:10
model = ar(data,1,'ls');
original_data=data;
fitted_data(i)=coeff1*data(i-1); % **OR**
data(i)=coeff1*data(i-1);
fitted_data=data;
residual= original_data - fitted_data;
plot(original_data,'r'); hold on; plot(fitted_data);
end
When calculating residual is the fitted_data obtained as above by resolving the AR equation with the obtained coefficients? Matlab has a function for doing this but I wanted to make my own. So, after finding coefficients from the original data how do I resolve ? The coding above is incorrect. Attached is the plot of original data and the fitted_data.
If you model is simply y(n)= a*y(n-1) with scalar a, then here is the solution.
y = randn(10, 1);
a = y(1 : end - 1) \ y(2 : end);
y_estim = y * a;
residual = y - y_estim;
Of course, you should separate the data into train-test, and apply a on the test data. You can generalize this approach to y(n)= a*y(n-1) + b*y(n-2), etc.
Note that \ represents mldivide() function: mldivide
Edit:
% model: y[n] = c + a*y(n-1) + b*y(n-2) +...+z*y(n-n_order)
n_order = 3;
allow_offset = true; % alows c in the model
% train
y_train = randn(20,1); % from your data
[y_in, y_out] = shifted_input(y_train, n_order, allow_offset);
a = y_in \ y_out;
% now test
y_test = randn(20,1); % from your data
[y_in, y_out] = shifted_input(y_test, n_order, allow_offset);
y_estim = y_in * a; % same a
residual = y_out - y_estim;
here is shifted_input():
function [y_in, y_out] = shifted_input(y, n_order, allow_offset)
y_out = y(n_order + 1 : end);
n_rows = size(y, 1) - n_order;
y_in = nan(n_rows, n_order);
for k = 1 : n_order
y_in(:, k) = y(1 : n_rows);
y = circshift(y, -1);
end
if allow_offset
y_in = [y_in, ones(n_rows, 1)];
end
return
AR-type models can serve a number of purposes, including linear prediction, linear predictive coding, filtering noise. The eta(t) are not something we are interested in retaining, rather part of the point of the algorithms is to remove their influence to any extent possible by looking for persistent patterns in the data.
I have textbooks that, in the context of linear prediction, do not include the negative sign included in your expression prior to the sum. On the other hand Matlab's function lpcdoes:
Xp(n) = -A(2)*X(n-1) - A(3)*X(n-2) - ... - A(N+1)*X(n-N)
I recommend you look at function lpc if you haven't already, and at the examples from the documentation such as the following:
randn('state',0);
noise = randn(50000,1); % Normalized white Gaussian noise
x = filter(1,[1 1/2 1/3 1/4],noise);
x = x(45904:50000);
% Compute the predictor coefficients, estimated signal, prediction error, and autocorrelation sequence of the prediction error:
p = lpc(x,3);
est_x = filter([0 -p(2:end)],1,x); % Estimated signal
e = x - est_x; % Prediction error
[acs,lags] = xcorr(e,'coeff'); % ACS of prediction error
The estimated x is computed as est_x. Note how the example uses filter. Quoting the matlab doc again, filter(b,a,x) "is a "Direct Form II Transposed" implementation of the standard difference equation:
a(1)*y(n) = b(1)*x(n) + b(2)*x(n-1) + ... + b(nb+1)*x(n-nb)
- a(2)*y(n-1) - ... - a(na+1)*y(n-na)
which means that in the prior example est_x(n) is computed as
est_x(n) = -p(2)*x(n-1) -p(3)*x(n-2) -p(4)*x(n-3)
which is what you expect!
Edit:
As regards the function ar, the matlab documentation explains that the output coefficients have the same meaning as in the lp scenario discussed above.
The right way to evaluate the output of the AR model is to compute
data_armod(i)= -coeff(2)*data(i-1) -coeff(3)*data(i-2) -coeff(4)*data(i-3)
where coeff is the coefficient matrix returned with
model = ar(data,3,'ls');
coeff = model.a;

Matlab fourier descriptors what's wrong?

I am using Gonzalez frdescp function to get Fourier descriptors of a boundary. I use this code, and I get two totally different sets of numbers describing two identical but different in scale shapes.
So what is wrong?
im = imread('c:\classes\a1.png');
im = im2bw(im);
b = bwboundaries(im);
f = frdescp(b{1}); // fourier descriptors for the boundary of the first object ( my pic only contains one object anyway )
// Normalization
f = f(2:20); // getting the first 20 & deleting the dc component
f = abs(f) ;
f = f/f(1);
Why do I get different descriptors for identical - but different in scale - two circles?
The problem is that the frdescp code (I used this code, that should be the same as referred by you) is written also in order to center the Fourier descriptors.
If you want to describe your shape in a correct way, it is mandatory to mantain some descriptors that are symmetric with respect to the one representing the DC component.
The following image summarize the concept:
In order to solve your problem (and others like yours), I wrote the following two functions:
function descriptors = fourierdescriptor( boundary )
%I assume that the boundary is a N x 2 matrix
%Also, N must be an even number
np = size(boundary, 1);
s = boundary(:, 1) + i*boundary(:, 2);
descriptors = fft(s);
descriptors = [descriptors((1+(np/2)):end); descriptors(1:np/2)];
end
function significativedescriptors = getsignificativedescriptors( alldescriptors, num )
%num is the number of significative descriptors (in your example, is was 20)
%In the following, I assume that num and size(alldescriptors,1) are even numbers
dim = size(alldescriptors, 1);
if num >= dim
significativedescriptors = alldescriptors;
else
a = (dim/2 - num/2) + 1;
b = dim/2 + num/2;
significativedescriptors = alldescriptors(a : b);
end
end
Know, you can use the above functions as follows:
im = imread('test.jpg');
im = im2bw(im);
b = bwboundaries(im);
b = b{1};
%force the number of boundary points to be even
if mod(size(b,1), 2) ~= 0
b = [b; b(end, :)];
end
%define the number of significative descriptors I want to extract (it must be even)
numdescr = 20;
%Now, you can extract all fourier descriptors...
f = fourierdescriptor(b);
%...and get only the most significative:
f_sign = getsignificativedescriptors(f, numdescr);
I just went through the same problem with you.
According to this link, if you want invariant to scaling, make the comparison ratio-like, for example by dividing every Fourier coefficient by the DC-coefficient. f*1 = f1/f[0], f*[2]/f[0], and so on. Thus, you need to use the DC-coefficient where the f(1) in your code is not the actual DC-coefficient after your step "f = f(2:20); % getting the first 20 & deleting the dc component". I think the problem can be solved by keeping the value of the DC-coefficient first, the code after adjusted should be like follows:
% Normalization
DC = f(1);
f = f(2:20); % getting the first 20 & deleting the dc component
f = abs(f) ; % use magnitudes to be invariant to translation & rotation
f = f/DC; % divide the Fourier coefficients by the DC-coefficient to be invariant to scale