runLTspice.m

October 29, 2014 6 comments
% runLTspice is a function which can be used to read/obtain the voltage and/or
% current waveforms produced when an LTspice schematic (.asc file) is
% run/simulated.
%
% The function also allows you to replace the voltage and current sources
% which are used in the LTspice schematic (.asc file) with signals created in matlab.
%
% obtaining data usage:
%   volt_curr_list =  runLTspice('filename.asc'); %returns a list of all voltages and currents in the schematic.
%   results = runLTspice('filename.asc', volt_curr_list); %returns the data of the voltages and currents specified in parameter 2 (parameter 2 is a comma separated list of voltages and currents that the user wants returned)
%   plot(results.time_vec, results.data{1}); % plot the first set of data received
%   xlabel('Time (seconds)')
%   title(results.data_name{1})
%
% alter source data usage:
%  [volt_curr_list sources_list] = runLTspice('filename.asc');
%  fs = 100; %100 Hz sampling rate
%  step_input = [zeros(1, fs*1) ones(1, fs*10)]; % unit step input of duration 11 seconds 
%  first_source = sources_list(1: findstr(',',sources_list)-1);
%  if(~length(first_source))
%      first_source = sources_list;
%  end
%  results = runLTspice('filename.asc', volt_curr_list, first_source, step_input, fs);
%
% Written by: David Dorran (david.dorran@dit.ie)
%
% Acknowledgements:
% This function uses the LTspice2Matlab function written by Paul Wagner to capture data from a raw LTspice file.
% see - http://www.mathworks.com/matlabcentral/fileexchange/23394-fast-import-of-compressed-binary-raw-files-created-with-ltspice-circuit-simulator

function [varargout] = runLTspice(asc_file, varargin)
if(~exist('LTspice2Matlab'))
    error(sprintf('This function requires the function LTspice2Matlab written by Paul Wagner \n- Download from: http://www.mathworks.com/matlabcentral/fileexchange/23394-fast-import-of-compressed-binary-raw-files-created-with-ltspice-circuit-simulator'))
end
read_data = 0;
display_LTspice_variables = 0;
incorrect_output_parameters = 0;
incorrect_input_parameters = 0;
num_sources = 0;
if(nargin == 1)
    % just need to let the user know what voltages/currents can specified
    % in parameter 2 (parameter 2  is a comma separated list of
    % voltage/currents the the user requires) and what voltage current
    % sources can be modified with user specified data.
    if(nargout == 0)
        display_LTspice_variables = 1;
    elseif( nargout > 2)
        error(sprintf('You must specify either none, 1 or 2 return parameters when obtaining the voltage/current wavforms and sources from an LTspice asc file.\n\nE.g.\n\trunLTspice(''filename.asc'');\nor\n\tvolt_curr_list = runLTspice(''filename.asc'');\nor\n\t[volt_curr_list sources_list] = runLTspice(''filename.asc'');'))
    end
elseif(nargin == 2)
    % No need to modify the asc file with voltage/current pwl sources as
    % the user just wants to be able to capture data from the asc file
    read_data = 1;

    if(~isstr(varargin{1}))
        error(sprintf('The second parameter must be string containing a comma separated list of LTspice voltage/currents you would like returned. \n\nType ''runLTspice(''%s'')'' to get a list of valid values. \n\nType ''help runLTspice'' for usage examples. ', asc_file));
    end
    if(nargout  > 1)
        incorrect_output_parameters = 1;
    end
elseif(nargin > 2)
    read_data = 1;
    if(nargout  > 1)
        incorrect_output_parameters = 1;
    end
    mynargin = nargin - 2;
    if(mod(mynargin, 3))
        incorrect_input_parameters = 1;
    else
        num_sources = mynargin/3;
        for k = 1 : num_sources;
            if ~isstr(varargin{1+(k-1)*3+1}) || isstr(varargin{1+(k-1)*3+2}) || isstr(varargin{1+(k-1)*3+3}) || length(varargin{1+(k-1)*3+3}) > 1
                incorrect_input_parameters = 1;
            end
            sources2update{k} = varargin{1+(k-1)*3+1};
        end
    end
end

if(incorrect_output_parameters)
    error(sprintf('You can only specify one output parameter when retrieving data\n\nType ''help runLTspice'' for example usage.'))
end

if(incorrect_input_parameters)
    error(sprintf('When specifying data for either a supply voltage or current source three parameters are required for each source \ni.e. the source name (a string), the data (a vector/array of numbers), the sampling rate (a number).\n\ne.g. \n\tresults = runLTspice(''%s'', ''V(n002), V(n005) '', ''V1'', [0:0.1:10], 2, ''V2'', ones(1, 20), 4);\n\nNOTE: The values voltages V(n002), V(n005) and sources V1 and V2 must exist in ''%s''.\n\nTo determine the valid voltage/currents and sources in your LTspice asc file run the following command:\n\t runLTspice(''%s'')', asc_file,asc_file, asc_file))
end

if(~exist(asc_file))
    error(['Could not find the LTspice file ' asc_file ])
end

asc_path = fileparts(asc_file);
if(~length(asc_path))
    asc_path = pwd;
end
asc_path(end+1) = '\';

raw_file = regexprep(asc_file, '.asc$', '.raw');

this_file_path = which('runLTspice');
this_dir_path = this_file_path(1:end-length('runLTspice.m'));

config_file = [this_dir_path 'runLTspice_config.txt'];
if(exist(config_file))
    ltspice_path = fileread(config_file);
else
    ltspice_path = 'C:\Program Files\LTC\LTspiceXVII\XVIIx64.exe';
end

% See if the LTspice exe can be found in the path ltspice_path. If not get
% the user to enter the path. Once a valid path is entered then store it
% in the config file
exe_file = [ltspice_path ];
path_needs_updating = 0;
while ~exist(exe_file)
    disp(['The LTspice execuatble file could not be found. ']);
    if(~path_needs_updating)
        disp('Once the correct location is entered it will be recorded for future use of this function.')
        disp('     Example location 1: C:\Program Files\LTC\LTspiceIV\scad3.exe')
        disp('     Example location 2: C:\Program Files\LTC\LTspiceXVII\XVIIx64.exe')
    end
    disp(' ')
    ltspice_path = input('Please enter the location where the executable exists => ','s');
    ltspice_path= strtrim(ltspice_path);
    if(ltspice_path(end) == '/')
        ltspice_path(end) ='\';
    end

    exe_file = [ltspice_path ];
    path_needs_updating = 1;
end

if(path_needs_updating)
    disp('Updating LTspice exe location ...');
    try fp = fopen(config_file,'w');
        fprintf(fp, '%s',ltspice_path );
        fclose(fp);
        disp('Update Complete.')
    catch
        disp(['Could not open the configuration file ' config_file ' to record the exe location.']);
        disp('You will need to enter the path to the LTspice manually every time you use this function')
    end
end


% Parse the .asc file to idenitfy the current and voltage sources in the
% schematic. Also sote the line numbers where the values of these sources
% are located - these values will be updated with pwl files in the event
% that the user wishes to update the sources in the schematic
fid = fopen(asc_file);

k=1;
tline{k} = fgetl(fid);
voltage_found = 0;
current_found = 0;
voltage_names = {};
current_names = {};
symbol_num = 0;
voltage_symbol_nums = [];
current_symbol_nums = [];
while ischar(tline{k})
    k = k + 1;
    tline{k} = fgetl(fid);

    if(length(findstr('SYMBOL ',tline{k})))
        voltage_found = 0;
        current_found = 0;
        symbol_num = symbol_num + 1;
    end
    if(length(findstr('SYMATTR Value',tline{k})))
        symbol_value_line(symbol_num) = k;
    end
    if(length(findstr('SYMBOL voltage',tline{k})) || length(findstr('SYMBOL Misc\\signal ',tline{k}))|| length(findstr('SYMBOL Misc\\cell ',tline{k}))|| length(findstr('SYMBOL Misc\\battery ',tline{k})))
        voltage_found = 1;
    end
    if(length(findstr('SYMBOL current',tline{k})))
        current_found = 1;
    end
    if(voltage_found && length(findstr('SYMATTR InstName', tline{k})))
        voltage_names{end+1} = tline{k}(length('SYMATTR InstName')+2:end);
        voltage_symbol_nums(end+1) = symbol_num;
    end
    if(current_found && length(findstr('SYMATTR InstName', tline{k})))
        current_names{end+1} = tline{k}(length('SYMATTR InstName')+2:end);
        current_symbol_nums(end+1) = symbol_num;
    end
end
num_asc_file_lines = k;
source_names = [voltage_names current_names];
source_symbol_nums  = [voltage_symbol_nums  current_symbol_nums] ;
fclose(fid);

%% Just need to let the user know what sources can be used and the voltages/currents that can be read. No need to update the asc file
if(read_data == 0)

    sim_result = test_asc_simulation(asc_file,raw_file);
    if(sim_result == 0)
        error(sprintf('There was a problem running the LTspice file %s. \n\n Please open the file in LTspice and verify that it can be simulated', asc_file));
    end  
    raw_data = LTspice2Matlab(raw_file);
    voltage_current_list = '';
    for k = 1: raw_data.num_variables
        voltage_current_list = [voltage_current_list raw_data.variable_name_list{k} ', '];
    end
    voltage_current_list(end-1:end) = []; %remove trailing comma
    
    source_list = '';
    for k = 1: length(source_names)
        source_list = [source_list source_names{k} ', '];
    end
    source_list(end-1:end) = [];
    
    if(nargout)
        varargout{1} = voltage_current_list;
        if(nargout ==2)
            varargout{2} = source_list;
        end
    else
      disp(sprintf('List of voltage and currents that can be read:\n\t%s\n\nList of voltage and/or current sources that can be written to:\n\t%s', voltage_current_list, source_list))
    end
        
    return;
end

%identify the variables the user wants returned
vars2read = strtrim(strread(varargin{1}, '%s','delimiter',','));
if(~length(vars2read))
    error('The list of voltages and currents to read must be contained in a comma separated string.');
end
    
%% no need to change the pwl file so just run the specified asc file and return the results
if(num_sources == 0)
    sim_result = test_asc_simulation(asc_file,raw_file);
    if(sim_result == 0)
        error(sprintf('There was a problem running the LTspice file %s. \n\n Please open the file in LTspice and verify that it can be simulated', asc_file));
    end  
    raw_data = LTspice2Matlab(raw_file);
    
    varargout = grab_specified_data(vars2read, raw_data);
    
    return;
end

%% Need to update asc file and create pwl files for each source specified if you reach this stage

% calculate the simulation duration so as to match the duration of the longest
%(in time) source signal
sim_duration = 0;
for k = 1: num_sources
  duration = length(varargin{1+(k-1)*3+2})/varargin{1+(k-1)*3+3}; % = length(data)/fs  
  if(duration > sim_duration)
      sim_duration = duration;
  end
  source_found = 0;
    for m = 1: length(source_names)
        if strcmp(sources2update{k}, source_names{m})
            sources2update_value_line(k) = symbol_value_line(source_symbol_nums(m));
            source_found = 1;
        end
    end
    if(~source_found)
        error(sprintf('Unable to find the source %s in the schematic.\n\nType runLTspice(''%s''); at the command line to see a list of valid sources', sources2update{k}, asc_file));
    end
end

%ceate pwl files for each souce
fs = 0;
for k = 1: num_sources
    % create pwl files to use to drive the voltage sources
    tmp_pwl_file{k} = [asc_path 'runLTspice_tmp_pwl_' num2str(k) '.txt'];
    create_pwl(varargin{1+(k-1)*3+2},varargin{1+(k-1)*3+3}, tmp_pwl_file{k});
    fs = varargin{1+(num_sources-1)*3+3};
end

% create a temporary file which will basically be a copy of the asc file passed
% to the function with voltage and current sources updated with pwl files.
% Also the simulation duration will be updated.

tmp_asc_file = [asc_path 'runLTspice_tmp_asc.asc'];
tmp_raw_file = [asc_path 'runLTspice_tmp_asc.raw'];


% update the temporary file with pwl files and the duration the simulation
% is to run.
f_op_id = fopen(tmp_asc_file,'w');
if(f_op_id <= 0)
    error(sprintf('This function needs to be able to write to the folder in which the LTsppice asc file exists. \nCopy the file %s to a folder you can write to and try again. ', asc_file));
end
k = 0;
trans_found = 0;
while( k < num_asc_file_lines - 1)
    k = k + 1;
    res = findstr('!.tran ', tline{k});
    if(length(res))
        trans_found = 1;
        res2 = regexp(tline{k}, '.tran (?.*?)(?(steady|startup|nodiscard|uic|$).*)', 'names');
        res_info = res2(1).info;
        tran_settings = res2(1).settings;
        num_spaces = length(findstr(' ', res_info) );
        if(num_spaces == 1 || num_spaces == 0)
            tran_info = [num2str(sim_duration) ' '];
        else
            sim_duration
            tran_info = regexprep(res_info, ' .+? ',  [' ' num2str(sim_duration) ' ']  ,'once')
            
        end
        tline{k}(res(1)+length('!.tran '):end) = [];
        tline{k} = [tline{k} tran_info tran_settings];
    end
    
    index = find(sources2update_value_line==k);
    if(length(index))
            fwrite(f_op_id, ['SYMATTR Value PWL file="' tmp_pwl_file{index} '"' ]);
            fprintf(f_op_id, '\n');
    else
        fwrite(f_op_id, tline{k});
        fprintf(f_op_id, '\n');
    end
end

fclose(f_op_id);
sim_result = test_asc_simulation(tmp_asc_file,tmp_raw_file);
if(sim_result == 0)
    error(sprintf('There was a problem running the LTspice file %s. \n\n Please open the file in LTspice and verify that it can be simulated', asc_file));
end  
if(~trans_found)
    error(['The simulation must be run in transient mode (rather than AC analysis for example). Select the Transient tab by first selecting Simulate->Edit Simulation Cmd in LTSpice'])
end
raw_data = LTspice2Matlab(tmp_raw_file);

varargout = grab_specified_data(vars2read, raw_data);
if(fs)
    varargout{1} = interpLTspice(varargout{1},fs);
end
%delete([asc_path 'runLTspice_tmp_*'])

return;

    function result = test_asc_simulation(test_filename,test_rawfile)
        % Will need to check if a raw file was created/updated to make sure
        % that the simulation ran successfully
        orig_timestamp =0;
        if(exist(test_rawfile))
            d = dir(test_rawfile);
            orig_timestamp = datenum(d.date);
        end
        % run/simulate the spice file
        system_command = ['"' exe_file '" -b -run -ascii "' test_filename '"'];
        %system_command = ['"' exe_file '" -b -run  "' test_filename '"'];
        system(system_command);
        result = 1;
        if(exist(test_rawfile))
            d = dir(test_rawfile);
            if( datenum(d.date) < orig_timestamp)
                result = 0; %unsuccessful simulation
            end
        else
            result = 0;; %unsuccessful simulation
        end
    end


    function create_pwl(data, fs, filename)

        file_id = fopen(filename,'w');
        if(file_id < 1)
            error(sprintf('This function needs to be able to write to the folder in which the LTspioce asc file exists. \nCopy the file %s to a folder you can write to and try again. ', asc_file));
        end
        for k = 1: length(data)
           fprintf(file_id, '%6.6f %6.6f ' , (k-1)/fs, data(k)); 
        end   
        fclose(file_id);
    end

    function data_details = grab_specified_data(vars, raw_info)
            for k = 1: length(vars)
                found(k) = 0;
                for m = 1:raw_info.num_variables
                    if(strcmp(vars{k},raw_info.variable_name_list{m}))
                        found(k) = m;
                    end
                end
                if(found(k))
                    data_details{1}.data_name{k} = vars{k};
                    data_details{1}.data{k} = raw_info.variable_mat(found(k),:);
                else
                    error( [ vars{k} ' not found! Check that you spelled the variable correctly. '])
                    data_details{1}.data_name{k} = [vars{k} ' not found!'];
                    data_details{1}.data{k} = [];
                end
            end
            data_details{1}.time_vec = raw_info.time_vect;
    end

end
Advertisement

locate_peaks matlab function

October 6, 2014 Leave a comment
function indices = locate_peaks(ip)
%function to find peaks
%a peak is any sample which is greater than its two nearest neighbours
    index = 1;
    num = 2;
    indices = [];
    for k = 1 + num : length(ip) - num
        seg = ip(k-num:k+num);
        [val, max_index] = max(seg);
        if max_index == num + 1
            indices(index) = k;
            index = index + 1;
        end;
    end;
Categories: matlab code

Linear Phase Filters – why they are used

October 1, 2014 Leave a comment
%% Linear phase filters - preserve shape of a filtered signal
% This is the code used during a youtube video presentation dealing with linear phase filters
%      Search for linear phase at http://youtube.com/ddorran
%
% Code available from https://dadorran.wordpress.com
% 
close all ; clear all; clc
fs = 100;
T = 1/fs; %sampling interval
N = 2000; %length of signal being synthesised
n = 0:N-1; %samples of the signal
t = n*T;
 
plot_range = [N/2-100:N/2+100];
%% synthesise a signal
x = cos(2*pi*10*t) + 0.5*cos(2*pi*20*t + 1.4); 
subplot(2,1,1);
plot(t(plot_range),x(plot_range))
xlabel('Time (seconds)');
ylabel('Amplitude')
title('Synthesised Signals') 
axis tight
 
% Add some noise
ns = randn(1,length(x)+100)*2;
    %filter the noise to synthesise band limited noise
[b a] = butter(5, [0.28 0.33],'bandpass');
ns_filtered = filter(b,a,ns);
    %add noise to clean signal
x_ns = x +ns_filtered(end-length(x)+1:end);
hold on
noisy_x = plot(t(plot_range), x_ns(plot_range),'r');
legend('clean signal', 'noisy signal')
 
%% Plot frequency Content of Noisy Signal
subplot(2,1,2)
X_ns = fft(x_ns);
fax = [0:N-1]/(N/2); % normalised frequency axis
plot(fax(1:N/2), abs(X_ns(1:N/2))/(N/2)) ; %plot first half of spectrum
xlabel('frequency ( x \pi rads/sample)')
ylabel('Magnitude')
title('Magnitude Spectrum of Noisy Signal')
 

%% Filter out the noise using an IIR filter (non-linear phase)
[b_iir a_iir] = cheby1(10, 0.5, [0.27 0.34], 'stop');
y_iir = filter(b_iir,a_iir, x_ns);

[H_iir w] = freqz(b_iir,a_iir); %determine frequency response
subplot(2,1,2);
hold on
plot(w/pi, abs(H_iir),'r')
legend('|X(\omega)|','|H(\omega)|')

pause
Y_iir = fft(y_iir);
plot(fax(1:N/2), abs(Y_iir(1:N/2))/(N/2),'g') ; %plot first half of spectrum
legend('|X(\omega)|','|H(\omega)|','|Y(\omega)|')

pause
subplot(2,1,1)
non_linear_y = plot(t(plot_range),y_iir(plot_range),'g')
legend('clean signal', 'noisy signal','filtered signal')
pause 
set(noisy_x,'visible', 'off')

 
%% Examine the magnitude and phase response of the IIR filter
figure(2)
subplot(2,1,1)
plot(w/pi,abs(H_iir))
xlabel('frequency ( x \pi rads/sample)')
ylabel('Magnitude')
title('Magnitude Response of filter')
subplot(2,1,2)
plot(w/pi,angle(H_iir))
xlabel('frequency ( x \pi rads/sample)')
ylabel('Phase Shift')
title('Phase Response of filter')
 
%% Now filter using an FIR filter (with linear phase)
b_fir = fir1(100,  [0.27 0.34],'stop');
a_fir = 1;
y_fir = filter(b_fir,a_fir, x_ns);

figure(1)
subplot(2,1,1)
plot(t(plot_range),y_fir(plot_range),'k')
legend('clean signal', 'noisy signal','filtered signal (non-linear)','filtered signal (linear)')

[H_fir, w ]= freqz(b_fir,a_fir);
subplot(2,1,2)
plot(w/pi, abs(H_fir),'k')
legend('|X(\omega)|','|H(\omega) Non-linear|','|Y(\omega)|','|H(\omega)| linear')


 
%% Compare the frequency responses of the two filter design approaches
figure(2)
subplot(2,1,1)
hold on
plot(w/pi,abs(H_fir),'g')
legend('non-linear filter','linear filter')
subplot(2,1,2)
hold on
plot(w/pi,angle(H_fir),'g')
legend('non-linear filter','linear filter')
pause

%% Why does linear phase preserve the shape??
close all
clear all; clc;
fs = 1000;
t = 0:1/fs:2;
x1 = cos(2*pi*3*t-pi/2);
x2 = cos(2*pi*5*t-(pi/2)/3*5);
 
pause
subplot(3,1,1)
plot(t,x1)
subplot(3,1,2)
plot(t,x2)
subplot(3,1,3)
plot(t,x1+x2,'g')
hold on

pitch/period tracking using autocorrelation

September 24, 2014 Leave a comment
%% Using Autocorrelation to track the local period of a signal
% This code is used as part of a youtube video demonstration 
% See http://youtube.com/ddorran
%
% Code available at https://dadorran.wordpress.com       
%
% The following wav file can be downloaded from 
%       https://www.dropbox.com/s/3y25abf1xuqpizj/speech_demo.wav
%% speech analysis example

[ip fs] = wavread('speech_demo.wav');
max_expected_period = round(1/50*fs);
min_expected_period = round(1/200*fs);
frame_len = 2*max_expected_period;

for k = 1 : length(ip)/frame_len -1;
    range = (k-1)*frame_len + 1:k*frame_len;
    frame = ip(range);
    
    %show the input in blue and the selected frame in red
    plot(ip);
    set(gca, 'xtick',[],'position',[ 0.05  0.82   0.91  0.13])
    hold on;
    temp_sig = ones(size(ip))*NaN;
    temp_sig(range) = frame;
    plot(temp_sig,'r');
    hold off
    
    %use xcorr to determine the local period of the frame
    [rxx lag] = xcorr(frame, frame);
    subplot(3,1,3)
    plot(lag, rxx,'r')
    rxx(find(rxx < 0)) = 0; %set any negative correlation values to zero
    center_peak_width = find(rxx(frame_len:end) == 0 ,1); %find first zero after center
    %center of rxx is located at length(frame)+1
    rxx(frame_len-center_peak_width : frame_len+center_peak_width  ) = min(rxx);
%     hold on
%     plot(lag, rxx,'g');
%     hold off
    [max_val loc] = max(rxx);
    period = abs(loc - length(frame)+1); 
    
    title(['Period estimate = ' num2str(period) 'samples (' num2str(fs/period) 'Hz)']);
    set(gca, 'position', [ 0.05  0.07    0.91  0.25])
    
    [max_val max_loc] = max(frame);
    num_cycles_in_frame = ceil(frame_len/period);
    test_start_positions = max_loc-(period*[-num_cycles_in_frame:num_cycles_in_frame]);
    index = find(test_start_positions > 0,1, 'last');
    start_position = test_start_positions(index);
    colours = 'rg';
    
    subplot(3,1,2)
    plot(frame);
    
    set(gca, 'position',[ 0.05 0.47 0.91 0.33])
    pause
    for g = 1 : num_cycles_in_frame
        if(start_position+period*(g) <= frame_len && period > min_expected_period)
            cycle_seg = ones(1, frame_len)*NaN;
            cycle_seg(start_position+period*(g-1):start_position+period*(g))  =...
                            frame(start_position+period*(g-1):start_position+period*(g));
            hold on
            
            plot(cycle_seg,colours(mod(g, length(colours))+1)) %plot one of the available colors
            hold off
        end
    end
    pause
end

%% synthesise a periodic signal to use as a basic demo
fs = 500;
T = 1/fs;
N = 250; % desired length of signal
t = [0:N-1]*T; %time vector 
f1 = 8; f2=f1*2; 
x = sin(2*pi*f1*t-pi/2) + sin(2*pi*f2*t);
plot(t, x)
ylabel('Amplitude')
xlabel('Time (seconds)')
title('Synthesised Signal');

%% Determine the autocorrelation function
[rxx lags] = xcorr(x,x);
figure
plot(lags, rxx)
xlabel('Lag')
ylabel('Correlation Measure')
title('Auto-correlation Function')

%% Illustrate the auto correlation process
%function available from https://dadorran.wordpress.com
illustrate_xcorr(x,x) 

%% Identify most prominent peaks
% Most prominent peak will be at the center of the correlation function
first_peak_loc = length(x) + 1;

% Lots of possible ways to identify second prominent peak. Am going to use a crude approach
% relying on some assumed prior knowledge of the signal. Am going to assume
% that the signal has a minimum possible period of .06 seconds = 30 samples;
min_period_in_samples = 30; 
half_min = min_period_in_samples/2 ;

seq = rxx;
seq(first_peak_loc-half_min: first_peak_loc+half_min) = min(seq);
plot(rxx,'rx');
hold on
plot(seq)

[max_val second_peak_loc] = max(seq);
period_in_samples =  abs(second_peak_loc -first_peak_loc)
period = period_in_samples*T
fundamental_frequency = 1/period

%% Autocorrelation of a noisy signal 
x2 = x + randn(1, length(x))*0.2;
plot(x2)
ylabel('Amplitude')
xlabel('Time (seconds)')
title('Noisy Synthesised Signal');

[rxx2 lags] = xcorr(x2,x2);
figure
plot(lags, rxx2)
xlabel('Lag')
ylabel('Correlation Measure')
title('Auto-correlation Function')

%% Autocorrelation technique can be problematic!
% Consider the following signal
f1 = 8; f2=f1*2; 
x3 = sin(2*pi*f1*t) + 5*sin(2*pi*f2*t);
plot(t, x3)
ylabel('Amplitude')
xlabel('Time (seconds)')
title('Synthesised Signal');

[rxx3 lags] = xcorr(x3,x3,'unbiased');
figure
plot(lags, rxx3)
xlabel('Lag')
ylabel('Correlation Measure')
title('Auto-correlation Function')

seq = rxx3;
seq(first_peak_loc-half_min: first_peak_loc+half_min) = min(seq);
plot(seq)

[max_val second_peak_loc] = max(seq);
period_in_samples =  abs(second_peak_loc -first_peak_loc)


illustrate_xcorr – code for cross correlation demos

September 24, 2014 1 comment
% This function illustrates the cross correlation process in action
%
% Usage:
%           fs = 1000;
%             T = 1/fs;
%             N = 500; % desired length of signal
%             t = [0:N-1]*T; %time vector 
%             f1 = 8; f2=f1*2; 
%             x = sin(2*pi*f1*t) + sin(2*pi*f2*t);
%
%           % To step though each sample use the following:
%           illustrate_xcorr(x,x)
%           
%           % to step through using 50 steps use:
%           illustrate_xcorr(x,x, 50)
%
function illustrate_xcorr(x, y, varargin)
if(length(x) > length(y))
    y(end+1:length(x)) = 0; %zero pad so the signals are the same length
else
    x(end+1:length(y)) = 0; %zero pad so the signals are the same length
end

    num_steps = 2*length(x)-1;
if(nargin ==3)
    arg = varargin{1};
    if(isnumeric(arg))
        num_steps = ceil(abs(arg));
    end
end
if(nargin > 3)
    error('See help on this function to see how to use it properly')
end

[rxy lags] = xcorr(x,y); %cross correlate signals

disp('The signal being autocorrelated is shown in blue (two instances)')
disp('As you hit the space bar the lower plot will move into different lag positions')
disp('The correlation function shown in red is updated for each lag position');
disp('keep pressing the space bar to step through the illustration ...');

figure
plot_width = 0.3; plot_height = 0.25;

top_ax_h = subplot(3,1,1);
plot(x)
axis tight
set(top_ax_h, 'visible','off', 'units', 'normalized')
set(top_ax_h,'position', [0.5-plot_width/2 5/6-plot_height/2 plot_width plot_height])

mid_ax_h = subplot(3,1,2);
plot(y)
axis tight
set(mid_ax_h, 'visible','off', 'units', 'normalized')
set(mid_ax_h,'position', [0.5-plot_width/2-plot_width 5/6-3*plot_height/2-0.01 plot_width plot_height])

bottom_ax_h = subplot(3,1,3);
corr_h = plot(lags,rxy,'r');
axis tight
set(bottom_ax_h,'units', 'normalized','Ytick',[])
set(bottom_ax_h,'position', [0.5-plot_width*3/2 0.2-plot_height/2 plot_width*3 plot_height])
set(corr_h, 'Ydata', ones(1, length(rxy))*NaN); %clear the correlation funciton plot once its set up

normalised_shift_size = 2*plot_width/(num_steps-1);
corr_seg_len = length(rxy)/num_steps;
for k = 1 : num_steps
    if(k > 1)
        new_pos = get(mid_ax_h,'position') + [normalised_shift_size 0 0 0];
        set(mid_ax_h,'position', new_pos);
    end
    set(corr_h, 'Ydata', [rxy(1:round(corr_seg_len*k)) ones(1,length(rxy)-round(corr_seg_len*k))*NaN])
    pause
end

requantise

September 17, 2014 Leave a comment
% this function takes a signal ip and modifies it so that
% occupies 2^(num_bits) quantisation levels
%
% ns = rand(1, 1000);
% op = requantise(ns, 2);
% plot(ns)
% hold on 
% plot(op,'r') % youshould be able to clearly see the 4 possible levels the
% new signal occupies
function op = requantise(ip, num_bits)
    num_levels = 2^num_bits;
    quantization_diff = (max(ip)-min(ip))/num_levels;
    quantization_levels = min(ip)+quantization_diff/2:quantization_diff:max(ip)-quantization_diff/2;
    op = zeros(1,length(ip));
    for k = 1: length(ip)
        [min_diff closest_level_index] = min(abs(quantization_levels-  ip(k)));
        op(k) = quantization_levels(closest_level_index);
    end
Categories: matlab code

Audio Time Scale Modification – Phase Vocoder Implementation in Matlab

June 2, 2014 Leave a comment
function synthSignal = pl_phaseVocoder_variable_analysis_hop(signal, tsm_factor, winSamps)
% A phase locked vocoder time-scale modification algorithm based upon Jean
% Laroche's 1999 work. The synthesis hop is fixed at one quarter the analysis window (hanning) while the analysis hop is scaled by the time scale factor; this results in more FFT computations than the bonada 2000 approach for slowing down - but provides smoother transitions frame to frame of the magnitude spectrums and, in general, a better quality of output. This code started
% out as the code provided by Tae Hong Park, but has changed significantly
% over the years
%
% David Dorran, Audio Research Group, Dublin Institute of Technology
% david.dorran@dit.ie
% http://eleceng.dit.ie/dorran
% http://eleceng.dit.ie/arg
%

% make sure input is mono and transpose if necessary
[r, c] = size(signal);
if r &amp;gt; 1
    signal = signal';
end;
[r, c] = size(signal);
if r &amp;gt; 1
    disp('Note :only works on mono signals');
    synthSignal = [];
    return
end;

% add a few zeros to stop the algorithm failing
zpad = zeros(1, 44100/4);
signal = [signal, zpad];
if nargin &amp;lt; 3
    winSamps = 2048;
end

winSampsPow2 = winSamps;
synHopSamps = winSampsPow2/4;
anHopSamps = round(synHopSamps/tsm_factor);

win = hanning(winSampsPow2);

X = specgram(signal, winSampsPow2, 100,win, winSampsPow2 - anHopSamps);

moduli = abs(X);
phases = angle(X);

[numBins, numFrames ] = size(phases);

syn_phases = zeros(numBins, numFrames); % a holder for synthesis phases

twoPi   = 2*pi;
omega   = twoPi * anHopSamps * [0:numBins-1]'/numBins/2; %the expected phase hop per frame

syn_phases(:,1) = phases(:,1) .* ( synHopSamps/ anHopSamps);

for idx =  2: numFrames
    ddx = idx - 1;
    deltaPhi = princarg(phases(:,idx) - phases(:,ddx) -omega); %calculate priciple determination of the hetrodyned phase increment
    phaseInc = (omega+deltaPhi)/anHopSamps; % phase increment per sample
    %locate the peaks
    pk_indices = [];
    pk_indices  =  locate_peaks(moduli(:,idx));
    if(~length(pk_indices))
        pk_indices = [1 10 12]; % just in case an odd situation is encountered  e.g. a sequence of zeros
    end
    %update phase of each peak channel using the phase propagation formula
    syn_phases(pk_indices,idx)    = syn_phases(pk_indices,ddx)+synHopSamps*phaseInc(pk_indices); %synthesis phase
    %update phase of channels in region of influence
    % first calculate angle of rotation
    rotation_angles = syn_phases(pk_indices,idx) - phases(pk_indices,idx);
    start_point = 1; %initialize the starting point of the region of influence

    for k = 1: length(pk_indices) -1
        peak = pk_indices(k);
        angle_rotation  = rotation_angles(k);
        next_peak = pk_indices(k+1);
        end_point = round((peak + next_peak)/2);
        ri_indices = [start_point : peak-1, (peak+1) : end_point]; %indices of the region of influence
        syn_phases(ri_indices,idx) = angle_rotation + phases(ri_indices, idx);
        start_point = end_point + 1;
    end;
end;

%Make sure that the LHS and RHS of the DFT's of the synthesis frames are a
%complex conjuget of each other
Z = moduli.*exp(i*syn_phases);
Z = Z(1:(numBins),:);
conj_Z = conj(flipud(Z(2:size(Z,1) -1,:)));
Z = [Z;conj_Z];

synthSignal = zeros(round(length(signal)*tsm_factor+length(win)), 1);

curStart = 1;
for idx = 1:numFrames-1
    curEnd   = curStart + length(win) - 1;
    rIFFT    = real(ifft(Z(:,idx)));
    synthSignal([curStart:curEnd]) = synthSignal([curStart:curEnd]) + rIFFT.*win;
    curStart = curStart + synHopSamps;
end

%--------------------------------------------------------------------------
function indices = locate_peaks(ip)
%function to find peaks
%a peak is any sample which is greater than its two nearest neighbours
	index = 1;
	num = 2;
    indices = [];
	for k = 1 + num : length(ip) - num
		seg = ip(k-num:k+num);
		[val, max_index] = max(seg);
		if max_index == num + 1
			indices(index) = k;
			index = index + 1;
		end;
	end;
    
%--------------------------------------------------------------------------
function Phase = princarg(Phasein)
    two_pi = 2*pi;
    a = Phasein/two_pi;
    k = round(a);
    Phase = Phasein-k*two_pi;


Audio time-scale modification – VSOLA algorithm in matlab

June 2, 2014 Leave a comment
function op = vsola(ip, tsm_factor, P)
% Implementation of the variable parameter  synchronised overlap add VSOLA algorithm 
% This implementation makes use of the standard SOLA algorithm (Rocus and Wilgus, ICASSP 1986) and some efficient paramter settings for the SOLA algorithm (Dorran, Lawlor and Coyle, ICASSP 2003) and (Dorran, Lawlor and Coyle, DAFX 2003)
% Given an input, ip, the longest likely pitch period, P, and  and a time scale modification factor, tsm_factor, return an output, op, that is a time scaled version of the input. The synthesis_overlap_size is the length, in samples of the lowest likely fundametal period of the input.
% for speech synthesis_overlap_size is set to (16/1000)*fs samples and for music synthesis_overlap_size is typically set to (20/1000)*fs samples 
%
% David Dorran, Audio Research Group, Dublin Institute of Technology
% david.dorran@dit.ie
% http://eleceng.dit.ie/dorran
% http://eleceng.dit.ie/arg
%

% make sure input is mono and transpose if necessary
[r, c] = size(ip);
if r > 1
    ip = ip';
end;    
[r, c] = size(ip);
if r > 1
    disp('Note :only works on mono signals');
    op = [];
    return
end;

% initialize the values of analysis_frame_length, analysis_window_offset, synthesis_frame_offset and length_of_overlap
desired_tsm_len = round(length(ip)*tsm_factor);
P = round(P); %longest likely pitch period in samples
Lmax = round(P * 1.5);% found this to a reasonable value for the Lmax- Lmax is the duration over which the correlation function is applied
stationary_length = (P * 1.5); % found this to a reasonable value for the stationary length - This is the max duration that could be discarded/repeated

analysis_window_offset = round((stationary_length - P)/abs(tsm_factor - 1)); % this equation was derived.
synthesis_window_offset = round(analysis_window_offset * tsm_factor);
analysis_frame_length = round(Lmax + synthesis_window_offset);
number_of_analysis_frames = floor((length(ip)- analysis_frame_length)/analysis_window_offset);

if number_of_analysis_frames < 2 %not much time-scaling being done just return the input
    op = ip;
    return;
end;

%the next two lines just ensure that the last frame finishes at the very end of the signal (not essential)
zpad = zeros(1, (number_of_analysis_frames*analysis_window_offset) + analysis_frame_length - length(ip));
ip = [ip zpad];

%initialize the output
op = zeros(1, desired_tsm_len);
%initialize the first output frame
op(1 : analysis_frame_length) = ip(1 : analysis_frame_length);

min_overlap = round(Lmax - P); %ensure that there is some minimum overlap
count = 0;

% Loop for the 2nd analysis frame to the number_of_analysis_frames
for m = 1 : number_of_analysis_frames
    
    %grab the mth input frame
    ip_frame = ip(analysis_window_offset * m : (analysis_window_offset * m) + analysis_frame_length - 1);
    
    %grab the maximum overlapping segments from the inout frame and the current output
    seg_1 = op(round(synthesis_window_offset*(m-1))+analysis_frame_length - Lmax : round(synthesis_window_offset*(m-1))+analysis_frame_length -1);
    seg_2 = ip_frame(1: Lmax);
    
    %compute the correlation of these segments
    correlation   = xcorr(seg_2, seg_1,'coeff');

    %Find the best point to overlap (opt_overlap_length) making sure not to exceed the maximum or go below the minimum overlap.
    correlation(length(correlation) - Lmax -1: length(correlation)) = -100;
    correlation(1: min_overlap) = -100;    
    [max_correlation, opt_overlap_length] = max(correlation);
    
    if(max_correlation == 0)
        opt_overlap_length = Lmax;
    end;
%     offset = Lmax - opt_overlap_length;
%     if ((offset + analysis_window_offset -  synthesis_window_offset) >= 0 & (offset + analysis_window_offset -  synthesis_window_offset) <= P)
%         count = count +1;
%     end;
    
    % append mth analysis frame to the current synthesised output using a linear cross fade
    ov_seg = linear_cross_fade(seg_1, ip_frame, opt_overlap_length);
    ov_len =(round(synthesis_window_offset*m)+analysis_frame_length) - (round(synthesis_window_offset*(m-1))+analysis_frame_length - Lmax) + 1;
    ov_seg = ov_seg(1:ov_len);
    op(round(synthesis_window_offset*(m-1))+analysis_frame_length - Lmax: round(synthesis_window_offset*m)+analysis_frame_length) = ov_seg;
    
end; % end of for loop

% linear cross fade the first segment with the second segment given a certain amount of overlap
% |----------seg1----------|
%			   |---------------seg2-----------------------|
%              |--overlap-|

function op = linear_cross_fade(seg_1, seg_2, overlap_length, cross_fade_duration)

    error(nargchk(3,4,nargin));
    if nargin < 4
        cross_fade_duration = overlap_length;
    end
    if cross_fade_duration > overlap_length
        cross_fade_duration = overlap_length;
    end
	% overlap the end of seg_1 with the start of seg_2 using a linear cross-fade
	if (length(seg_1) < overlap_length),
        seg_2 = seg_2(overlap_length - length(seg_1) + 1: length(seg_2));
		overlap_length = length(seg_1);
	end; % end of if statement

	if (length(seg_2) < overlap_length),
        seg_1 = seg_1(length(seg_1) - (overlap_length - overlap_length): length(seg_1));
		overlap_length = length(seg_2);
	end; % end of if statement

    
	overlapping_region = zeros(1, cross_fade_duration); % initialize the overlapping region
    seg_1 = seg_1(1: length(seg_1) - (overlap_length - cross_fade_duration));
    
	op = zeros(1, length(seg_1) + length(seg_2) - cross_fade_duration);

	if(overlap_length ~= 1)
		linear_ramp1 = (cross_fade_duration-1:-1:0) ./(cross_fade_duration-1);
		linear_ramp2 = (0:1:cross_fade_duration-1) ./(cross_fade_duration-1);
		overlapping_region = (seg_1(length(seg_1)-cross_fade_duration+1:length(seg_1)).*linear_ramp1) + (seg_2(1: cross_fade_duration).*linear_ramp2);
	end;
	op = [seg_1(1:length(seg_1)-cross_fade_duration) ,overlapping_region , seg_2(cross_fade_duration+1:length(seg_2))];
% END of linear_cross_fade function

pic18f4620 xc8 compiler bundle

May 22, 2014 Leave a comment

Here’s a small (5.2 MB) bundle that I use to compile and program c code on my pic18f4620 using a pickit2 on my PC. I’ve only tested this on XP.

It doesn’t have all the features of a full blown IDE like MPLABX (no stepping through code I’m afraid) but is a nice concise package that encourages the use of command line scripting.

Getting Started

  • Download the zip file on to your PC or, preferably, a USB key
  • Extract the files to a folder on your PC or USB key

The extracted folder contains the xc8 compiler files (modified to work with the pic18f4620 only), the pk2cmd software tool which is used to communicate with the PICkit2, and Notepad++, which can be used to write and modify code.

To create a new project do the following:

        1. First create a new folder inside the “robosumo_files” folder and copy the build.bat file into that folder.
        2.  Run the Notepad++  editor and paste the example code (flashing LED) given below. Save the code in the new folder as a file called “main.c” (don’t include quotes in the filename).
        3. Make sure the PICkit2 programmer is correctly connected to the PIC18f4620 and finally double click the build.bat file.
        4. If there are no errors in the code and the hardware is set up correctly then the PIC18f4620 will be programmed.

robosumo_build_result

Example Code – Flashing LED

This is a pared back example program for the PIC18F4620. It blinks an LED connected to pin 19 (RD0). See this circuit

//
// PIC18F4620 example program
// Written by Ted Burke (ted.burke@dit.ie)
// Last update 28-2-2013
//

#include &lt;xc.h&gt;

#pragma config OSC=INTIO67,MCLRE=OFF,WDT=OFF,LVP=OFF,BOREN=OFF

int main(void)
{
	// Make RD0 a digital output
	TRISD = 0b11111110;

	while(1)
	{
		LATDbits.LATD0 = 1; // Set pin RD0 high
		_delay(125000);     // 0.5 second delay
		LATDbits.LATD0 = 0; // Set pin RD0 low
		_delay(125000);     // 0.5 second delay
	}
}
Categories: pic18f, Uncategorized

pic18f4620 assembly bundle

May 22, 2014 Leave a comment

Here’s a small (3.6 MB) bundle that I use to program my pic18f4620 using a pickit2 with assembly code on my PC. I’ve only tested this on XP.

It doesn’t have all the features of a full blown IDE like MPLABX (no stepping through code I’m afraid) but is a nice concise package that encourages the use of command line scripting.

The bundle also includes some example code.

Getting Started

Download the bundle from here and extract the files.

pic18fassemby_extracted_files

The extracted folder contains the mpasmx assembler files, the pk2cmd software tool which is used to communicate with the PICkit2, and Notepad++ text editor, which can be used to write and modify code.

The bundle includes 6 example projects.

To create a new project do the following:

  1. First create a new folder inside the “pic18f4620_asm” folder and copy the build.bat file into that folder.
  2. Run the Notepad++ editor and paste the example code (flashing LED) given below. Save the code in the new folder as a file called “main.asm” (don’t include quotes in the filename).
  3. Make sure the PICkit2 programmer is correctly connected to the PIC18f4620 and finally double click the build.bat file.
  4. If there are no errors in the code and the hardware is set up correctly then the PIC18f4620 will be programmed.

asm_output

Example Code to work with this circuit
This code will flash the LED connected to pin 19 (RD0)

;configure the  assembler directive 'list' so as to set processor to 18f4620 and set the radix used for data expressions to decimal (can be HEX|DEC|OCT)
    list p=18f4620, r=DEC
    #include <p18f4620.inc>
; configure the micro so that the watchdog timer is off, low-voltage programming is off, master clear is off and the clock works off the internal oscillator
    config WDT=OFF, LVP=OFF, MCLRE=OFF, OSC=INTIO67
;The org directive tells the compiler where to position the code in memory
    org 0x0000 ;The following code will be programmed in reset address location i.e. This is where the micro jumps to on reset
    goto Main ;Jump to Main immediately after a reset
 
;--------------------------------------------------------------------------
; Main Program
;--------------------------------------------------------------------------

    org 0x0100 
Main
    clrf TRISD ; All PORT D IO pins are configured as outputs

    ; Set up timer 0 so it can be used to control flash rate
    bcf T0CON, T0CS; use internal instruction cycle clock
    bcf T0CON, T08BIT; use 16 bit mode
    bcf INTCON, TMR0IF; reset timer overflow flag
    bsf T0CON, TMR0ON; turn on timer 0    
        
loop  
    ; check if the timer0 overflow flag has been set
    btfss INTCON, TMR0IF
    goto loop
    bcf INTCON, TMR0IF; reset timer overflow flag

    ;invert LATD0
    movlw 0x1
    XORWF LATD,F
    
    goto loop   
    END
    
Categories: pic18f, Uncategorized