Course 02637 Advanced MatLab Programming Project 1 … · Course 02637 Advanced MatLab Programming...
Transcript of Course 02637 Advanced MatLab Programming Project 1 … · Course 02637 Advanced MatLab Programming...
Course 02637
Advanced MatLab Programming
Project 1 – GPS data processing
Daniel Dittlau Pedersen, s051899
2
Abstract
In this project a MatLab GUI is programmed
to manipulate GPS data. Operations include
reading and saving from/to txt, csv and mat
files and converting the GPS data into
Cartesian coordinates, as well as
interpolation of the datasets using either
spline or pchip, computation of key values
(distance, duration, speed and pace),
selection of partial datasets and zooming on
the plotted data. The resultant plots can also
be exported to pdf or jpeg formats as well as
printed, both in cartesian or in GPS
coordinate systems.
3
Contents Introduction ....................................................................................................................................................... 5
Overview of the GUI Window ............................................................................................................................ 5
Functions Used .................................................................................................................................................. 6
Initialization ................................................................................................................................................... 6
Loading a data set .......................................................................................................................................... 7
Read function ............................................................................................................................................ 7
GPS to Cartesian coordinates conversion function ................................................................................... 7
Load button ............................................................................................................................................... 8
Selecting type and number of points for interpolation ................................................................................. 9
Interpolating the data set .............................................................................................................................. 9
Interpolation function ............................................................................................................................... 9
Interpolation Button ................................................................................................................................ 10
Computing key values.................................................................................................................................. 11
Selecting a subset of the data ..................................................................................................................... 11
Zoom function ............................................................................................................................................. 12
Saving a data set .......................................................................................................................................... 13
Store Function ......................................................................................................................................... 13
Cartesian to GPS coordinates conversion function ................................................................................. 13
Save Button ............................................................................................................................................. 14
Exporting plots ............................................................................................................................................. 14
User guide for the GPS GUI ............................................................................................................................. 16
The Visual Outputs ...................................................................................................................................... 16
The Selectable User Inputs .......................................................................................................................... 17
Interpolation Type ................................................................................................................................... 17
Interpolation Points ................................................................................................................................. 17
The Buttons ................................................................................................................................................. 17
Load ......................................................................................................................................................... 18
Save.......................................................................................................................................................... 18
Export ...................................................................................................................................................... 19
Interpolate ............................................................................................................................................... 20
Select ....................................................................................................................................................... 21
4
Zoom ........................................................................................................................................................ 22
Appendix .......................................................................................................................................................... 23
Speed Tests .................................................................................................................................................. 23
cos/sin vs .cosd/sind ................................................................................................................................ 23
Vectorized vs loops .................................................................................................................................. 23
Spline vs pchip ......................................................................................................................................... 23
Source Code ................................................................................................................................................. 24
gps.m ....................................................................................................................................................... 24
read.m ..................................................................................................................................................... 28
Store.m .................................................................................................................................................... 28
convert2Cart.m ........................................................................................................................................ 28
convert2GPS.m ........................................................................................................................................ 29
interpol.m ................................................................................................................................................ 30
compute.m .............................................................................................................................................. 30
prepareExport.m ..................................................................................................................................... 30
5
Introduction
In this project a GUI is to be designed, which allow the user to import, manipulate and export GPS
data. As the code is of primary interest in this report, the various functions are included in the main
text, as opposed to being left for the appendix. This ensures a better overview of the text, as well as
saves time for the reader.
Attached to this report is a zip file containing the various MatLab files, with gps.m being the main
file. Also attached is a compiled standalone application of the program.
Overview of the GUI Window
Figure 1 – The main window
The main window consists of the elements seen in Figure 1. As can be seen, the upper part consists
of 4 axes: One large to the left containing a map of the route (with proper North/South/East/West
directions), and 3 smaller on the right containing the speed, pace and total distance covered as
functions of time.
The user interface is located in the lower part of the window in the center and to the right. In the
rightmost part are located buttons for loading, saving, exporting and interpolating, as well as
buttons to select a subset of the route and a button to toggle the zoom function on and off. In the
center of the window the type of interpolation method used can be specified using the two radio
buttons, and the number of interpolation points to be used can be selected using either the slider or
directly input as a value, ranging from 300-3300. This particular interval is used, as the initial
6
dataset contained 300 points and an interpolation using less than the original data set would
decrease the smoothness of the initial curve1. The maximum value is chosen, as it allows an easy
way to get slider step increments of 10 and 100 using either the arrows or clicking on the bar
respectively.
In the lower left part, the computed key values can be seen in a table.
Functions Used
Initialization
The opening function gps_OpeningFcn is used to set up the GUI prior to opening.
As it is unknown what screen size the end-user will use, the GUI itself will center in the middle of
the screen regardless of that, by using information extracted from the users screen size. Furthermore
all the axes will be initialized with titles and labels using empty data sets. The position and size of
the GUI is also stored as a handle for later use in exporting the plots.
function gps_OpeningFcn(hObject, eventdata, handles, varargin)
% Choose default command line output for gps
handles.output = hObject;
% Update handles structure
guidata(hObject, handles);
% Centers the GUI window
movegui(hObject,'center')
handles.positionGUI = get(hObject,'Position'); % Get the position for exporting
% Prepare the axes
plot(handles.plotroute,NaN,NaN);
title(handles.plotroute,'Map of Route','FontSize',14,'FontWeight','bold')
ylabel(handles.plotroute,'South -> North')
xlabel(handles.plotroute,'West -> East')
plot(handles.plotVel,NaN,NaN);
title(handles.plotVel,'Speed','FontSize',14,'FontWeight','bold')
xlabel(handles.plotVel,'Time [s]')
ylabel(handles.plotVel,'Km/h')
plot(handles.plotPace,NaN,NaN);
title(handles.plotPace,'Pace','FontSize',14,'FontWeight','bold')
xlabel(handles.plotPace,'Time [s]')
ylabel(handles.plotPace,'Min/Km')
plot(handles.plotDist,NaN,NaN);
title(handles.plotDist,'Total distance covered','FontSize',14,'FontWeight','bold')
xlabel(handles.plotDist,'Time [s]')
ylabel(handles.plotDist,'Km')
% Prepare the uitable with key data
set(handles.keytable,'Data',{'Time spent' '' ' sec';
'Distance covered' '' ' km';
'Average speed' '' ' km/h';
'Average pace' '' ' min/km'});
guidata(hObject,handles); % Save the handle data
1 This could be used to save space and decrease computation time in larger data set, but in this particular setup, where a
smooth curve is needed (2), this is not considered as an option.
7
Loading a data set
Read function
The read function, responsible for the extraction of the dataset from a file can be seen below. As can
be seen, the user is able to select the file using uigetfile, set up as to only allow .txt, .mat or csv files
to be selected. Filterindex returns the number of the selection, allowing for the different extraction
methods in the switch-statement (Case 0 is used when the user presses cancel instead of selecting a
file).
function [ dataset ] = read()
%read Reads a datafile
% Read a 3 column datafile and stores it as a cell containing a 3 columns (and n rows)
[filename,path,filterindex] = uigetfile(...
{'*.txt', 'Text file (*.txt)';...
'*.mat','Mat file (*.mat)';...
'*.csv','Comma separated file (*.csv)'},...
'Pick a file');
switch filterindex
case 0,
dataset = 0;
return
case 1,
dataset = dlmread(fullfile(path,filename));
case 2,
dataset = open(fullfile(path,filename));
case 3,
dataset = csvread(fullfile(path,filename));
end
end
GPS to Cartesian coordinates conversion function
The data set originating from the read function has to be converted from GPS coordinates to
Cartesian coordinates. This is done by the Convert2Cart function seen below.
In the start of the function various constant variables are defined, and the latitude, longitude and
time are identified and converted into radians, as this pre-conversion yield the fastest speed, as
opposed to using cosd/sind functions2.
The x, y and z components are then calculated using vectorized calculations to take advantage of
MatLabs preference for these. To get a glimpse of the speed increase in this particular function, a
method of doing the conversion using a loop is inserted (and commented out in the final version),
and the difference in calculation speed is found using tic-toc. The speed increase is found to be
more than 10 times (see the appendix for the measurements).
2 See the appendix – Speed Tests for the measured speeds using tic-toc.
8
function [ xyzt ] = convert2Cart( WGS85 )
%convert2Cart Convert from WGS84 to Cartesian coordinates
% Converting an array containing data from GPS coordinate system WGS84 to Cartesian coordinates
% using the formulas:
%
% X = (v+h)*cos(theta)*cos(lambda)
% Y = (v+h)*cos(theta)*sin(lambda)
% Z = (v*(1-exp(2))+h)*sin(theta)
%
% Be aware that the input data should be in format [langtitude longitude timestamp]
a = 6378137;
f = 1/298.257223563;
e_2 = f*(2-f);
h = 0; % Assuming zero height
latitude = WGS85(:,1)*pi/180;
longitude = WGS85(:,2)*pi/180;
T = WGS85(:,3);
% Slow way
% for k = 1:length(latitude)
% v = a/sqrt(1-e_2*sin(latitude(k))^2);
% X(k) = (v+h)*cos(latitude(k))*cos(longitude(k));
% Y(k) = (v+h)*cos(latitude(k))*sin(longitude(k));
% Z(k) = (v*(1-e_2)+h)*sin(latitude(k));
% end
%
% xyzt = [X' Y' Z' T];
% Fast way
v = (a./sqrt(1-e_2.*sin(latitude).^2));
X = (v+h).*cos(latitude).*cos(longitude);
Y = (v+h).*cos(latitude).*sin(longitude);
Z = (v.*(1-e_2)+h).*sin(latitude);
xyzt = [X Y Z T];
end
Load button
The read and convert2Cart functions are incorporated into the load button of the GUI in the function
loadbutton_Callback shown below. After the imported data has been stored in a handle, the data
points are plotted (as circles) and the axes are properly set up like in the initialization of the GUI.
The reason for doing this again, is to clear plots and values from any older data set when a new one
is loaded into the GUI. Again the handles added in this function are saved in the end.
function loadbutton_Callback(hObject, eventdata, handles)
handles.datasetGPS = read();
if handles.datasetGPS == 0
return;
end
handles.datasetxyz = convert2Cart(handles.datasetGPS);
plot(handles.plotroute,handles.datasetxyz(:,2),handles.datasetxyz(:,1),'o');
set(handles.plotroute,'YDir','reverse'); % reverse the second axis to get
correct mapping
plot(handles.plotVel,NaN,NaN);
plot(handles.plotPace,NaN,NaN);
plot(handles.plotDist,NaN,NaN);
title(handles.plotroute,'Map of Route','FontSize',14,'FontWeight','bold')
ylabel(handles.plotroute,'South -> North')
9
xlabel(handles.plotroute,'West -> East')
title(handles.plotVel,'Speed','FontSize',14,'FontWeight','bold')
xlabel(handles.plotVel,'Time [s]')
ylabel(handles.plotVel,'Km/h')
title(handles.plotPace,'Pace','FontSize',14,'FontWeight','bold')
xlabel(handles.plotPace,'Time [s]')
ylabel(handles.plotPace,'Min/Km')
title(handles.plotDist,'Total distance
covered','FontSize',14,'FontWeight','bold')
xlabel(handles.plotDist,'Time [s]')
ylabel(handles.plotDist,'Km')
guidata(hObject,handles); % save the data into the GUI
Selecting type and number of points for interpolation
The two different types of interpolation methods available are the spline and pchip. The method is
selected using 2 radio buttons, which will call the interpolatebutton_Callback function immediately
if changed.
The number of interpolation points can be set both using a slider and using an editable text box.
These work together, using the method described in (1) as seen in the two functions
inter_points_Callback and inter_points_slider_Callback below.
function inter_points_Callback(hObject, eventdata, handles)
val = round(eval(get(hObject, 'string')));
interval = [get(handles.inter_points_slider, 'min'), get(handles.inter_points_slider, 'max')];
if val <= interval(2) & interval(1) <= val
set(handles.inter_points_slider,'value',val);
else
uiwait(warndlg('Interpolation steps must be between 300 and 3000','Warning','modal'))
uiresume
end
function inter_points_slider_Callback(hObject, eventdata, handles)
set(handles.inter_points, 'string',num2str(round(get(hObject,'value'))))
Interpolating the data set
Interpolation function
The interpolation of the data points is done using the interpol function, which then either uses the
spline or pchip function according to what is specified by the user. Speed measurements are done
on each of the interpolation methods, to see which one is faster in our case. As can be seen from the
measurements (see appendix) the pchip function seems to be the fastest for this particular data set,
which is most likely due to the less expensive set up of this function3. Had the data set been larger,
the two functions would in theory take approximately the same time to evaluate.
function [ dataout ] = interpol( datain, type, n )
%interpol interpolate the data points of a given set
3 As stated in the help description in MatLab of pchip.
10
% This function allows for 2 different interpolation methods, spline and
% pchip, selectable by setting the 2. argument to 1 or 2 respectively.
% Furthermore it allows the user to specify the number of interpolation
% points using the 3. argument.
x = datain(:,1)';
y = datain(:,2)';
z = datain(:,3)';
t = datain(:,4)';
tt = linspace(min(t),max(t),n);
switch type
case 1,
xx = spline(t,x,tt);
yy = spline(t,y,tt);
zz = spline(t,z,tt);
case 2,
xx = pchip(t,x,tt);
yy = pchip(t,y,tt);
zz = pchip(t,z,tt);
end
dataout(:,1)=xx;
dataout(:,2)=yy;
dataout(:,3)=zz;
dataout(:,4)=tt;
end
Interpolation Button
The interpolatebutton_Callback takes the values from the slider and the radio buttons to decide the
parameters with which to call the Interpol function. The computation of key values from the
interpolated data set is then carried out using the compute function and shown in the table, and the
interpolated values is plotted in the same axes as the original data points.
function interpolatebutton_Callback(hObject, eventdata, handles)
points = get(handles.inter_points_slider,'value');
radio = get(handles.interpol_type_panel,'SelectedObject');
switch get(radio,'tag')
case 'radiobutton_spline',
handles.interpol_data = interpol(handles.datasetxyz,1,points);
case 'radiobutton_pchip',
handles.interpol_data = interpol(handles.datasetxyz,2,points);
end
handles.keyvalues = compute(handles.interpol_data);
% Plot the data
plot(handles.plotroute,handles.datasetxyz(:,2),handles.datasetxyz(:,1),'o',handles.interpol_data(:,2
),handles.interpol_data(:,1));
set(handles.plotroute,'YDir','reverse') % reverse the second axis to get correct mapping
plot(handles.plotVel,handles.interpol_data(4:end-1,4),handles.keyvalues.inst_speed(4:end-1))
plot(handles.plotPace,handles.interpol_data(4:end-1,4),handles.keyvalues.inst_pace(4:end-1))
plot(handles.plotDist,handles.interpol_data(:,4),handles.keyvalues.dist_array./1000)
title(handles.plotroute,'Map of Route','FontSize',14,'FontWeight','bold')
ylabel(handles.plotroute,'South -> North')
xlabel(handles.plotroute,'West -> East')
title(handles.plotVel,'Speed','FontSize',14,'FontWeight','bold')
xlabel(handles.plotVel,'Time [s]')
ylabel(handles.plotVel,'Km/h')
title(handles.plotPace,'Pace','FontSize',14,'FontWeight','bold')
xlabel(handles.plotPace,'Time [s]')
ylabel(handles.plotPace,'Min/Km')
title(handles.plotDist,'Total distance covered','FontSize',14,'FontWeight','bold')
xlabel(handles.plotDist,'Time [s]')
ylabel(handles.plotDist,'Km')
11
% Fill the uitable with key data
set(handles.keytable,'Data',{'Time spent' handles.keyvalues.totaltime/60 ' min';
'Distance covered' handles.keyvalues.totaldist/1000 ' km';
'Average speed' handles.keyvalues.avg_speed ' km/h';
'Average pace' handles.keyvalues.avg_pace ' min/km'});
guidata(hObject,handles); % Save the handle data
Computing key values
The key values is extracted using the compute function seen below, which is called when either the
interpolation button is pressed or a subset is selected.
The function calculate the covered distance at each time interval using the cumulative sum function
cumsum, with the point-to-point distance calculated as
√ ) )) ) )) ) ))
where the difference between the two values are calculated using the diff function. Instantaneous
speed (the speed at a single point) is calculated using the formula
where the distance traveled and the time it took is again found using the diff function. The rest of
the key values are easily found using trivial calculations.
function [ keyvalues ] = compute( datain )
%Compute Computes the key values from a given data set
% Computes distance as well as both average and instantaneous speed and pace
keyvalues.dist_array = [0;cumsum(sqrt(diff(datain(:,1)).^2 + diff(datain(:,2)).^2 +
diff(datain(:,3)).^2))];
keyvalues.totaltime = (datain(end,4)-datain(1,4)); % sec
keyvalues.totaldist = keyvalues.dist_array(end); % m
keyvalues.inst_speed = [0;diff(keyvalues.dist_array)./diff(datain(:,4)).*3.6]; % km/h
keyvalues.avg_speed = ((keyvalues.totaldist)/(keyvalues.totaltime))*3.6; % km/h
keyvalues.inst_pace = 1./(keyvalues.inst_speed./60); % min/km
keyvalues.avg_pace = 1/(keyvalues.avg_speed/60); % min/km
end
Selecting a subset of the data
The Select button allows the user to select start and end points for a subset of the data set using the
mouse. To do this the ginput function is used, which stores the 2 points as an x- and y value
(keeping in mind, that the plotted route has its x- and y values switched, to get correct positioning.
As the ginput allows the user to click everywhere on the axis, the actual point in the data set with
the least distance to the user selected point is found using the min function with the index returned.
To ensure that the user can select everywhere in the data set regardless of the first index (point)
12
selected is higher or lower than the second, the min and max functions are used to access the data
set when extracting the subset.
After the extraction the new key values are calculated and shown and the new subset is plotted in
the axes.
function selectbutton_Callback(hObject, eventdata, handles)
[y,x] = ginput(2); % reversed axis
[C I] = min(sqrt((handles.interpol_data(:,1)-x(1)).^2+(handles.interpol_data(:,2)-y(1)).^2));
start_stop(1) = I;
[C I] = min(sqrt((handles.interpol_data(:,1)-x(2)).^2+(handles.interpol_data(:,2)-y(2)).^2));
start_stop(2) = I;
handles.selected_data = handles.interpol_data(min(start_stop):max(start_stop),:);
handles.keyvalues = compute(handles.selected_data);
% Plot the data
plot(handles.plotroute,handles.datasetxyz(:,2),handles.datasetxyz(:,1),'o',...
handles.interpol_data(:,2),handles.interpol_data(:,1),handles.selected_data(:,2),...
handles.selected_data(:,1),'red');
set(handles.plotroute,'YDir','reverse') % reverse the second axis to get correct mapping
plot(handles.plotVel,handles.selected_data(3:end-1,4),handles.keyvalues.inst_speed(3:end-1))
plot(handles.plotPace,handles.selected_data(3:end-1,4),handles.keyvalues.inst_pace(3:end-1))
plot(handles.plotDist,handles.selected_data(:,4),handles.keyvalues.dist_array)
title(handles.plotroute,'Map of Route','FontSize',14,'FontWeight','bold')
ylabel(handles.plotroute,'South -> North')
xlabel(handles.plotroute,'West -> East')
title(handles.plotVel,'Speed','FontSize',14,'FontWeight','bold')
xlabel(handles.plotVel,'Time [s]')
ylabel(handles.plotVel,'Km/h')
title(handles.plotPace,'Pace','FontSize',14,'FontWeight','bold')
xlabel(handles.plotPace,'Time [s]')
ylabel(handles.plotPace,'Min/Km')
title(handles.plotDist,'Total distance covered','FontSize',14,'FontWeight','bold')
xlabel(handles.plotDist,'Time [s]')
ylabel(handles.plotDist,'Km')
% Fill the uitable with key data
set(handles.keytable,'Data',{'Time spent' handles.keyvalues.totaltime/60 ' min';
'Distance covered' handles.keyvalues.totaldist/1000 ' km';
'Average speed' handles.keyvalues.avg_speed ' km/h';
'Average pace' handles.keyvalues.avg_pace ' min/km'});
guidata(hObject,handles); % Save the handle data
Zoom function
The function allowing the user to zoom on the presented axes is made with a toggle button and
shown below.
function togglebutton_zoom_Callback(hObject, eventdata, handles)
if get(hObject,'Value')==1
zoom on
else
zoom off
end
13
Saving a data set
Store Function
The store function first checks in what coordinate format the user has selected to save in, and calls
the conversion function if GPS coordinate format is chosen. The function then uses the built-in
uiputfile to allow the user to select a name and one of 3 different formats. Based on the selected
format, the correct write-function is called,
function store( xyzdata, dataselect )
%store Stores data in a file using selected format
switch dataselect % if saving gps format
case 'Cartesian',
data = xyzdata; % do nothing
case 'GPS',
data = convert2GPS(xyzdata); % Convert to gps coordinates
end
[filename,path,filterindex] = uiputfile(...
{'*.txt', 'Text file (*.txt)';...
'*.mat','Mat file (*.mat)';...
'*.csv','Comma separated file (*.csv)';...
'*.*', 'All Files (*.*)'},...
'Save as');
switch filterindex
case 1,
dlmwrite(fullfile(path,filename),data,'delimiter', ' ')
case 2,
save(fullfile(path,filename),'data')
case 3,
csvwrite(fullfile(path,filename),data)
end
end
Cartesian to GPS coordinates conversion function
If the user opts for saving in GPS coordinates, the convert2GPS function takes care of this. The
method for calculating back to GPS coordinates are found in (2), using the closed formula.
function [ WGS84 ] = convert2GPS( xyzt )
%convert2GPS Convert from Cartesian to WGS84 coordinates
% Converting an array containing data from Cartesian coordinate system to GPS
% coordinate system WGS84 using the formulas:
%
% longitude = arctan(y/z)
% Latitude = latitude = atan2((z+e_m2*b*sin(phi).^3),(p-e2*a*cos(phi).^3))
% where
% a = 6378137;
% f = 1/298.257223563;
% b = a*(1-f);
% e2 = (a^2-b^2)/a^2;
% e_m2 = f*(2-f)/(1-f)^2;
% p = sqrt(x.^2+y.^2)
% phi = atan2(z*a,(p*b));
%
% Be aware that the input data should be in format [x y z timestamp]
% Extracting the values
x = xyzt(:,1);
y = xyzt(:,2);
z = xyzt(:,3);
t = xyzt(:,4);
14
% Defining constants
a = 6378137;
f = 1/298.257223563;
b = a*(1-f);
e2 = (a^2-b^2)/a^2;
e_m2 = f*(2-f)/(1-f)^2;
% Calculating auxiliary constant
p = sqrt(x.^2+y.^2);
phi = atan2(z*a,(p*b));
% Calculating latitude and longitude
latitude = atan2((z+e_m2*b*sin(phi).^3),(p-e2*a*cos(phi).^3));
longitude = atan2(y,x);
WGS84(:,1) = latitude*180/pi;
WGS84(:,2) = longitude*180/pi;
WGS84(:,3) = t;
end
Save Button
The save button consists of 2 question dialogues and 2 switch cases, allowing the user to select
whether to save the whole interpolated data set or just the selected subset. Furthermore it allows the
user to select the format in which the data will be stored, either Cartesian coordinates or GPS
coordinates.
function savebutton_Callback(hObject, eventdata, handles)
% Ask whether to save in Cartesian or GPS coordinates
choice2 = questdlg ('Do you want to save in Cartesian or GPS coordinates?',...
'Format of save-file','Cartesian','GPS','Cancel','Cartesian');
switch choice2
case 'Cancel',
return;
otherwise
end
% Then ask whether to save whole data set or just the selected data
choice1 = questdlg ('Do you want to save the whole dataset or just the selected data?',...
'Choose dataset','Whole set','Selected subset','Cancel','Whole set');
switch choice1
case 'Whole set',
store(handles.interpol_data,choice2);
case 'Selected subset',
store(handles.selected_data,choice2);
case 'Cancel',
return;
end
Exporting plots
In order to exporting the plotted data, the axes need to be made into a figure. This is done using the
prepareExport function seen below. This function first creates an invisible figure with the same size
as the original GUI figure, and then copies the axes into this new figure. It then sets various
parameters to make sure the new figure is printable.
function preparedFig = prepareExport( route,speed,pace,dist,figSize )
%prepareExport
% This function prepares the 4 axes from the gps GUI to be printed or
% exported to an image file. It takes the 4 axes and turn them into a
15
% figure instead. It also sets various parameters to ensure the right
% setup of the printing.
preparedFig = figure('visible','off');
set(preparedFig,'position',figSize)
copyobj(route,gcf)
copyobj(speed,gcf)
copyobj(pace,gcf)
copyobj(dist,gcf)
set(gcf,'PaperPositionMode','auto');
set(gcf,'InvertHardcopy','off')
set(gcf,'PaperType','A4')
set(gcf,'PaperUnits','centimeters')
set(gcf,'Paperorientation','landscape')
set(gcf,'PaperPositionMode','auto')
end
When pressing the export button, the user is first subjected to a quest dialogue asking whether to
save as pdf of jpg image, or print the figure. If print is selected the function opens the print dialogue
and if the user selects pdf format, the function uses the print function to save the prepared figure in
pdf format. If jpg is selected, the user is presented with the choice of resolution in the image: Low,
Medium and Optimal, where Optimal uses the resolution of the users screen and the other 2
represents resolutions of 90 and 150 dots per inch respectively.
function exportbutton_Callback(hObject, eventdata, handles)
preparedFig = prepareExport(handles.plotroute,handles.plotVel,handles.plotPace,...
handles.plotDist,handles.positionGUI)
choice = questdlg ('Do you want to save or print the figures?',...
'Save or print?','Save as PDF','Save as JPG','Print','Save as JPG');
switch choice
case 'Save as PDF',
filename = inputdlg('Select a filename:','Name')
print(preparedFig,'-dpdf','-loose',filename{1})
case 'Save as JPG',
filename = inputdlg('Select a filename:','Name')
res = questdlg ('Select resolution of
image','Resolution?','Low','Medium','Optimal','optimal');
switch res
case 'Low'
print(preparedFig,'-djpeg','-r90','-loose',filename{1})
case 'Medium'
print(preparedFig,'-djpeg','-r150','-loose',filename{1})
case 'Optimal'
print(preparedFig,'-djpeg','-r0','-loose',filename{1})
end
case 'Print',
printdlg(preparedFig)
end
16
User guide for the GPS GUI
The Visual Outputs
figure 2 – The visual outputs
The graphs are located in the upper part of the window as can be seen in figure 2. The one to the left
shows the route and the 3 to the right shows the speed, pace and distance covered, all as functions
of the time. The last 3 graphs are only available after interpolation has taken place.
At the bottom left of the window, the time spent and the total distance covered are shown as well as
the average speed and pace, calculated either from the interpolated data or a subset of this.
17
The Selectable User Inputs
figure 3 – The location of editable user inputs
Interpolation Type
The radio buttons here are used to select the interpolation type. Available selections are between the
spline method or the pchip method.
Interpolation Points
Here the amount of interpolation points is selected either by using the slider or typing in a value in
the box above. Allowed values range between 300 and 3300.
The Buttons
figure 4 – The location of the user buttons
18
Load
Pressing this button allows for the import of GPS coordinate data, which can be selected from .txt,
.csv and .mat files as seen in figure 5.
figure 5 – Loading a data set
Save
The save button allows for storage of the interpolated data set. The user has the selection of whether
to store the whole data set or just a subsection, and whether the data set should be saved in
Cartesian coordinates or GPS coordinates, and will be prompted as seen in figure 6 and figure 7.
figure 6 – Selection of Cartesian vs. GPS
19
figure 7 – Select dataset to be saved
Export
The export button allow for exporting the plotted data either to a file, pdf or jpg, or to a printer. The
user will be asked whether to print or save as shown in figure 8. If the jpg format is chosen, the user
will also be asked to select the resolution for the image file as seen in figure 9, with the options of
low, medium or optimal, where the optimal setting is based on the resolution of the users screen.
The resultant image can be seen in figure 10.
figure 8 – Save or print
20
figure 9 – Choose resolution
figure 10 – image file
Interpolate
The interpolate button will interpolate the imported data set, and show the new data set as a curve
on top of the old data points. Furthermore a number of key values will be calculated and shown in
the box in the lower left corner of the window, and these will also be plotted as functions of time
and shown in the upper right side of the window.
21
Select
The select button allows for the selection of a subset of the plotted data. The user selects this with
the mouse by first clicking at one point (or close by), and then clicking on a second point to
complete the selection which is now marked in red. The procedure is shown in figure 11 and figure
12
figure 11 – Selecting the interval
figure 12 – The completed selection
22
Zoom
The zoom button allows the user to zoom on any of the plotted data sets, shown in figure 13. To
enter zoom mode press the zoom button. When the zooming is completed press the zoom button
again to exit zoom mode.
figure 13 - Zooming
23
Appendix
Speed Tests
All the tests below are conducted using an Intel Core2Duo E7400 processor with 2 GB DDR2
RAM available.
cos/sin vs .cosd/sind
Below can be seen tic-toc responses from convert2Cart() when using the cosd/sind functions as
opposed to converting the values into radians and then using the cos/sin functions. First 3 runs are
cosd/sind, the last 3 is using precalculation:
Elapsed time is 0.000307 seconds.
Elapsed time is 0.000304 seconds.
Elapsed time is 0.000302 seconds.
Elapsed time is 0.000111 seconds.
Elapsed time is 0.000097 seconds.
Elapsed time is 0.000106 seconds.
Vectorized vs loops
Tic-toc measurements for the coordinate conversion function convert2Cart(). Upper 3 runs are from
the loop, bottom 3 is from vectorized calculations:
Elapsed time is 0.001838 seconds.
Elapsed time is 0.001089 seconds.
Elapsed time is 0.001107 seconds.
Elapsed time is 0.000121 seconds.
Elapsed time is 0.000083 seconds.
Elapsed time is 0.000089 seconds.
Spline vs pchip
400 points:
Spline Pchip
Elapsed time is 0.005938 seconds.
Elapsed time is 0.005766 seconds.
Elapsed time is 0.005865 seconds.
Elapsed time is 0.002170 seconds.
Elapsed time is 0.002080 seconds.
Elapsed time is 0.002197 seconds.
3300 points
Spline Pchip
Elapsed time is 0.005345 seconds.
Elapsed time is 0.005282 seconds.
Elapsed time is 0.005131 seconds.
Elapsed time is 0.003492 seconds.
Elapsed time is 0.003881 seconds.
Elapsed time is 0.003494 seconds.
24
Source Code
gps.m
function varargout = gps(varargin)
%GPS M-file for gps.fig
%
% This GUI allows the user to import, manipulate and save GPS data.
% Operations include reading and saving from/to txt, csv and mat
% files and converting the GPS data into Cartesian coordinates, as
% well as interpolation of the datasets using either spline or pchip,
% computation of key values (distance, duration, speed and pace),
% selection of partial datasets and zooming on the plotted data.
% The resultant plots can also be exported to pdf or jpeg formats.
% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @gps_OpeningFcn, ...
'gui_OutputFcn', @gps_OutputFcn, ...
'gui_LayoutFcn', [], ...
'gui_Callback', []);
if nargin && ischar(varargin{1})
gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT
% --- Executes just before gps is made visible.
function gps_OpeningFcn(hObject, eventdata, handles, varargin)
% Choose default command line output for gps
handles.output = hObject;
% Update handles structure
guidata(hObject, handles);
% Centers the GUI window
movegui(hObject,'center')
handles.positionGUI = get(hObject,'Position'); % Get the position for exporting
% Prepare the axes
plot(handles.plotroute,NaN,NaN);
title(handles.plotroute,'Map of Route','FontSize',14,'FontWeight','bold')
ylabel(handles.plotroute,'South -> North')
xlabel(handles.plotroute,'West -> East')
plot(handles.plotVel,NaN,NaN);
title(handles.plotVel,'Speed','FontSize',14,'FontWeight','bold')
xlabel(handles.plotVel,'Time [s]')
ylabel(handles.plotVel,'Km/h')
plot(handles.plotPace,NaN,NaN);
title(handles.plotPace,'Pace','FontSize',14,'FontWeight','bold')
xlabel(handles.plotPace,'Time [s]')
ylabel(handles.plotPace,'Min/Km')
plot(handles.plotDist,NaN,NaN);
title(handles.plotDist,'Total distance covered','FontSize',14,'FontWeight','bold')
xlabel(handles.plotDist,'Time [s]')
ylabel(handles.plotDist,'Km')
% Prepare the uitable with key data
set(handles.keytable,'Data',{'Time spent' '' ' sec';
'Distance covered' '' ' km';
'Average speed' '' ' km/h';
'Average pace' '' ' min/km'});
25
guidata(hObject,handles); % Save the handle data
% UIWAIT makes gps wait for user response (see UIRESUME)
% uiwait(handles.figure1);
% --- Outputs from this function are returned to the command line.
function varargout = gps_OutputFcn(hObject, eventdata, handles)
% Get default command line output from handles structure
varargout{1} = handles.output;
% --- Executes on button press in loadbutton.
function loadbutton_Callback(hObject, eventdata, handles)
handles.datasetGPS = read();
if handles.datasetGPS == 0
return;
end
handles.datasetxyz = convert2Cart(handles.datasetGPS);
plot(handles.plotroute,handles.datasetxyz(:,2),handles.datasetxyz(:,1),'o');
set(handles.plotroute,'YDir','reverse'); % reverse the second axis to get correct mapping
plot(handles.plotVel,NaN,NaN);
plot(handles.plotPace,NaN,NaN);
plot(handles.plotDist,NaN,NaN);
title(handles.plotroute,'Map of Route','FontSize',14,'FontWeight','bold')
ylabel(handles.plotroute,'South -> North')
xlabel(handles.plotroute,'West -> East')
title(handles.plotVel,'Speed','FontSize',14,'FontWeight','bold')
xlabel(handles.plotVel,'Time [s]')
ylabel(handles.plotVel,'Km/h')
title(handles.plotPace,'Pace','FontSize',14,'FontWeight','bold')
xlabel(handles.plotPace,'Time [s]')
ylabel(handles.plotPace,'Min/Km')
title(handles.plotDist,'Total distance covered','FontSize',14,'FontWeight','bold')
xlabel(handles.plotDist,'Time [s]')
ylabel(handles.plotDist,'Km')
guidata(hObject,handles); % save the data into the GUI
% --- Executes on button press in savebutton.
function savebutton_Callback(hObject, eventdata, handles)
% Ask whether to save in Cartesian or GPS coordinates
choice2 = questdlg ('Do you want to save in Cartesian or GPS coordinates?',...
'Format of save-file','Cartesian','GPS','Cancel','Cartesian');
switch choice2
case 'Cancel',
return;
otherwise
end
% Then ask whether to save whole data set or just the selected data
choice1 = questdlg ('Do you want to save the whole dataset or just the selected data?',...
'Choose dataset','Whole set','Selected subset','Cancel','Whole set');
switch choice1
case 'Whole set',
store(handles.interpol_data,choice2);
case 'Selected subset',
store(handles.selected_data,choice2);
case 'Cancel',
return;
end
% --- Executes on button press in interpolatebutton.
function interpolatebutton_Callback(hObject, eventdata, handles)
points = get(handles.inter_points_slider,'value');
26
radio = get(handles.interpol_type_panel,'SelectedObject');
switch get(radio,'tag')
case 'radiobutton_spline',
handles.interpol_data = interpol(handles.datasetxyz,1,points);
case 'radiobutton_pchip',
handles.interpol_data = interpol(handles.datasetxyz,2,points);
end
handles.keyvalues = compute(handles.interpol_data);
% Plot the data
plot(handles.plotroute,handles.datasetxyz(:,2),handles.datasetxyz(:,1),'o',handles.interpol_data(:,2
),handles.interpol_data(:,1));
set(handles.plotroute,'YDir','reverse') % reverse the second axis to get correct mapping
plot(handles.plotVel,handles.interpol_data(4:end-1,4),handles.keyvalues.inst_speed(4:end-1))
plot(handles.plotPace,handles.interpol_data(4:end-1,4),handles.keyvalues.inst_pace(4:end-1))
plot(handles.plotDist,handles.interpol_data(:,4),handles.keyvalues.dist_array./1000)
title(handles.plotroute,'Map of Route','FontSize',14,'FontWeight','bold')
ylabel(handles.plotroute,'South -> North')
xlabel(handles.plotroute,'West -> East')
title(handles.plotVel,'Speed','FontSize',14,'FontWeight','bold')
xlabel(handles.plotVel,'Time [s]')
ylabel(handles.plotVel,'Km/h')
title(handles.plotPace,'Pace','FontSize',14,'FontWeight','bold')
xlabel(handles.plotPace,'Time [s]')
ylabel(handles.plotPace,'Min/Km')
title(handles.plotDist,'Total distance covered','FontSize',14,'FontWeight','bold')
xlabel(handles.plotDist,'Time [s]')
ylabel(handles.plotDist,'Km')
% Fill the uitable with key data
set(handles.keytable,'Data',{'Time spent' handles.keyvalues.totaltime/60 ' min';
'Distance covered' handles.keyvalues.totaldist/1000 ' km';
'Average speed' handles.keyvalues.avg_speed ' km/h';
'Average pace' handles.keyvalues.avg_pace ' min/km'});
guidata(hObject,handles); % Save the handle data
% --- Executes on button press in exportbutton. function exportbutton_Callback(hObject, eventdata, handles)
preparedFig = prepareExport(handles.plotroute,handles.plotVel,handles.plotPace,...
handles.plotDist,handles.positionGUI)
choice = questdlg ('Do you want to save or print the figures?',...
'Save or print?','Save as PDF','Save as JPG','Print','Save as JPG');
switch choice
case 'Save as PDF',
filename = inputdlg('Select a filename:','Name')
print(preparedFig,'-dpdf','-loose',filename{1})
case 'Save as JPG',
filename = inputdlg('Select a filename:','Name')
res = questdlg ('Select resolution of
image','Resolution?','Low','Medium','Optimal','optimal');
switch res
case 'Low'
print(preparedFig,'-djpeg','-r90','-loose',filename{1})
case 'Medium'
print(preparedFig,'-djpeg','-r150','-loose',filename{1})
case 'Optimal'
print(preparedFig,'-djpeg','-r0','-loose',filename{1})
end
case 'Print',
printdlg(preparedFig)
end
% --- Executes during object creation, after setting all properties.
function inter_points_CreateFcn(hObject, eventdata, handles)
% Hint: edit controls usually have a white background on Windows.
% See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor','white');
27
end
% --- Executes on slider movement.
function inter_points_slider_Callback(hObject, eventdata, handles)
set(handles.inter_points, 'string',num2str(round(get(hObject,'value'))))
% --- Executes during object creation, after setting all properties.
function inter_points_slider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor',[.9 .9 .9]);
end
% --- Executes on button press in selectbutton.
function selectbutton_Callback(hObject, eventdata, handles)
[y,x] = ginput(2); % reversed axis
[C I] = min(sqrt((handles.interpol_data(:,1)-x(1)).^2+(handles.interpol_data(:,2)-y(1)).^2));
start_stop(1) = I;
[C I] = min(sqrt((handles.interpol_data(:,1)-x(2)).^2+(handles.interpol_data(:,2)-y(2)).^2));
start_stop(2) = I;
handles.selected_data = handles.interpol_data(min(start_stop):max(start_stop),:);
handles.keyvalues = compute(handles.selected_data);
% Plot the data
plot(handles.plotroute,handles.datasetxyz(:,2),handles.datasetxyz(:,1),'o',...
handles.interpol_data(:,2),handles.interpol_data(:,1),handles.selected_data(:,2),...
handles.selected_data(:,1),'red');
set(handles.plotroute,'YDir','reverse') % reverse the second axis to get correct mapping
plot(handles.plotVel,handles.selected_data(3:end-1,4),handles.keyvalues.inst_speed(3:end-1))
plot(handles.plotPace,handles.selected_data(3:end-1,4),handles.keyvalues.inst_pace(3:end-1))
plot(handles.plotDist,handles.selected_data(:,4),handles.keyvalues.dist_array)
title(handles.plotroute,'Map of Route','FontSize',14,'FontWeight','bold')
ylabel(handles.plotroute,'South -> North')
xlabel(handles.plotroute,'West -> East')
title(handles.plotVel,'Speed','FontSize',14,'FontWeight','bold')
xlabel(handles.plotVel,'Time [s]')
ylabel(handles.plotVel,'Km/h')
title(handles.plotPace,'Pace','FontSize',14,'FontWeight','bold')
xlabel(handles.plotPace,'Time [s]')
ylabel(handles.plotPace,'Min/Km')
title(handles.plotDist,'Total distance covered','FontSize',14,'FontWeight','bold')
xlabel(handles.plotDist,'Time [s]')
ylabel(handles.plotDist,'Km')
% Fill the uitable with key data
set(handles.keytable,'Data',{'Time spent' handles.keyvalues.totaltime/60 ' min';
'Distance covered' handles.keyvalues.totaldist/1000 ' km';
'Average speed' handles.keyvalues.avg_speed ' km/h';
'Average pace' handles.keyvalues.avg_pace ' min/km'});
guidata(hObject,handles); % Save the handle data
% --- Executes on button press in togglebutton_zoom.
function togglebutton_zoom_Callback(hObject, eventdata, handles)
if get(hObject,'Value')==1
zoom on
else
zoom off
end
% --- Executes when selected object is changed in interpol_type_panel.
function interpol_type_panel_SelectionChangeFcn(hObject, eventdata, handles)
interpolatebutton_Callback(handles.interpolatebutton,0,handles)
28
read.m
function [ dataset ] = read()
%read Reads a datafile
% Read a 3 column datafile and stores it as a cell containing a 3 columns (and n rows)
[filename,path,filterindex] = uigetfile(...
{'*.txt', 'Text file (*.txt)';...
'*.mat','Mat file (*.mat)';...
'*.csv','Comma separated file (*.csv)'},...
'Pick a file');
switch filterindex
case 0,
dataset = 0;
return
case 1,
dataset = dlmread(fullfile(path,filename));
case 2,
dataset = open(fullfile(path,filename));
case 3,
dataset = csvread(fullfile(path,filename));
end
end
Store.m
function store( xyzdata, dataselect )
%store Stores data in a file using selected format
switch dataselect % if saving gps format
case 'Cartesian',
data = xyzdata; % do nothing
case 'GPS',
data = convert2GPS(xyzdata); % Convert to gps coordinates
end
[filename,path,filterindex] = uiputfile(...
{'*.txt', 'Text file (*.txt)';...
'*.mat','Mat file (*.mat)';...
'*.csv','Comma separated file (*.csv)';...
'*.*', 'All Files (*.*)'},...
'Save as');
switch filterindex
case 1,
dlmwrite(fullfile(path,filename),data,'delimiter', ' ')
case 2,
save(fullfile(path,filename),'data')
case 3,
csvwrite(fullfile(path,filename),data)
end
end
convert2Cart.m
function [ xyzt ] = convert2Cart( WGS84 )
%convert2Cart Convert from WGS84 to Cartesian coordinates
% Converting an array containing data from GPS coordinate system WGS84 to Cartesian coordinates
% using the formulas:
%
% X = (v+h)*cos(theta)*cos(lambda)
% Y = (v+h)*cos(theta)*sin(lambda)
% Z = (v*(1-exp(2))+h)*sin(theta)
%
% Be aware that the input data should be in format [langtitude longitude timestamp]
a = 6378137;
f = 1/298.257223563;
e_2 = f*(2-f);
h = 0; % Assuming zero height
29
latitude = WGS84(:,1)*pi/180;
longitude = WGS84(:,2)*pi/180;
T = WGS84(:,3);
% Slow way
% for k = 1:length(latitude)
% v = a/sqrt(1-e_2*sin(latitude(k))^2);
% X(k) = (v+h)*cos(latitude(k))*cos(longitude(k));
% Y(k) = (v+h)*cos(latitude(k))*sin(longitude(k));
% Z(k) = (v*(1-e_2)+h)*sin(latitude(k));
% end
%
% xyzt = [X' Y' Z' T];
% Fast way
v = (a./sqrt(1-e_2.*sin(latitude).^2));
X = (v+h).*cos(latitude).*cos(longitude);
Y = (v+h).*cos(latitude).*sin(longitude);
Z = (v.*(1-e_2)+h).*sin(latitude);
xyzt = [X Y Z T];
end
convert2GPS.m
function [ WGS84 ] = convert2GPS( xyzt )
%convert2GPS Convert from Cartesian to WGS84 coordinates
% Converting an array containing data from Cartesian coordinate system to GPS
% coordinate system WGS84 using the formulas:
%
% longitude = arctan(y/z)
% Latitude = latitude = atan2((z+e_m2*b*sin(phi).^3),(p-e2*a*cos(phi).^3))
% where
% a = 6378137;
% f = 1/298.257223563;
% b = a*(1-f);
% e2 = (a^2-b^2)/a^2;
% e_m2 = f*(2-f)/(1-f)^2;
% p = sqrt(x.^2+y.^2)
% phi = atan2(z*a,(p*b));
%
% Be aware that the input data should be in format [x y z timestamp]
% Extracting the values
x = xyzt(:,1);
y = xyzt(:,2);
z = xyzt(:,3);
t = xyzt(:,4);
% Defining constants
a = 6378137;
f = 1/298.257223563;
b = a*(1-f);
e2 = (a^2-b^2)/a^2;
e_m2 = f*(2-f)/(1-f)^2;
% Calculating auxiliary constant
p = sqrt(x.^2+y.^2);
phi = atan2(z*a,(p*b));
% Calculating latitude and longitude
latitude = atan2((z+e_m2*b*sin(phi).^3),(p-e2*a*cos(phi).^3));
longitude = atan2(y,x);
WGS84(:,1) = latitude*180/pi;
WGS84(:,2) = longitude*180/pi;
WGS84(:,3) = t;
end
30
interpol.m
function [ dataout ] = interpol( datain, type, n )
%interpol interpolate the data points of a given set
% This function allows for 2 different interpolation methods, spline and
% pchip, selectable by setting the 2. argument to 1 or 2 respectively.
% Furthermore it allows the user to specify the number of interpolation
% points using the 3. argument.
x = datain(:,1)';
y = datain(:,2)';
z = datain(:,3)';
t = datain(:,4)';
tt = linspace(min(t),max(t),n);
switch type
case 1,
xx = spline(t,x,tt);
yy = spline(t,y,tt);
zz = spline(t,z,tt);
case 2,
xx = pchip(t,x,tt);
yy = pchip(t,y,tt);
zz = pchip(t,z,tt);
end
dataout(:,1)=xx;
dataout(:,2)=yy;
dataout(:,3)=zz;
dataout(:,4)=tt;
end
compute.m
function [ keyvalues ] = compute( datain )
%Compute Computes the key values from a given data set
% Computes distance as well as both average and instantaneous speed and pace
keyvalues.dist_array = [0;cumsum(sqrt(diff(datain(:,1)).^2 + diff(datain(:,2)).^2 +
diff(datain(:,3)).^2))];
keyvalues.totaltime = (datain(end,4)-datain(1,4)); % sec
keyvalues.totaldist = keyvalues.dist_array(end); % m
keyvalues.inst_speed = [0;diff(keyvalues.dist_array)./diff(datain(:,4)).*3.6]; % km/h
keyvalues.avg_speed = ((keyvalues.totaldist)/(keyvalues.totaltime))*3.6; % km/h
keyvalues.inst_pace = 1./(keyvalues.inst_speed./60); % min/km
keyvalues.avg_pace = 1/(keyvalues.avg_speed/60); % min/km
end
prepareExport.m
function preparedFig = prepareExport( route,speed,pace,dist,figSize )
%prepareExport
% This function prepares the 4 axes from the gps GUI to be printed or
% exported to an image file. It takes the 4 axes and turn them into a
% figure instead. It also sets various parameters to ensure the right
% setup of the printing.
preparedFig = figure('visible','off');
set(preparedFig,'position',figSize)
copyobj(route,gcf)
copyobj(speed,gcf)
copyobj(pace,gcf)
copyobj(dist,gcf)
set(gcf,'PaperPositionMode','auto');
set(gcf,'InvertHardcopy','off')
set(gcf,'PaperType','A4')
set(gcf,'PaperUnits','centimeters')
set(gcf,'Paperorientation','landscape')
set(gcf,'PaperPositionMode','auto')
end