|
|
|
|
function [gnssRaw,gnssAnalysis] = ReadGnssLogger(dirName,fileName,dataFilterIn,gnssAnalysis)
|
|
|
|
|
%% [gnssRaw,gnssAnalysis]=ReadGnssLogger(dirName,fileName,dataFilter,gnssAnalysis);
|
|
|
|
|
% Read the log file created by Gnss Logger App in Android
|
|
|
|
|
% Compatible with Android release N
|
|
|
|
|
%
|
|
|
|
|
% Input:
|
|
|
|
|
% dirName = string with directory of fileName,
|
|
|
|
|
% e.g. '~/Documents/MATLAB/Pseudoranges/2016-03-28'
|
|
|
|
|
% fileName = string with filename
|
|
|
|
|
% dataFilter = cell array, dataFilter.{i}=string with a valid matlab expression
|
|
|
|
|
% e.g. dataFilter{1} = 'ConstellationType==1'
|
|
|
|
|
%
|
|
|
|
|
% Output:
|
|
|
|
|
% gnssRaw, all GnssClock and GnssMeasurement fields from log file, including:
|
|
|
|
|
% .TimeNanos (int64)
|
|
|
|
|
% .FullBiasNanos (int64)
|
|
|
|
|
% ...
|
|
|
|
|
% .Svid
|
|
|
|
|
% .ReceivedSvTimeNanos (int64)
|
|
|
|
|
% .PseudorangeRateMetersPerSecond
|
|
|
|
|
% ...
|
|
|
|
|
% and data fields created by this function:
|
|
|
|
|
% .allRxMillis (int64), full cycle time of measurement (milliseconds)
|
|
|
|
|
% accurate to one millisecond, it is convenient for matching up time
|
|
|
|
|
% tags. For computing accurate location, etc, you must still use
|
|
|
|
|
% TimeNanos and gnssMeas.tRxSeconds
|
|
|
|
|
%
|
|
|
|
|
% gnssAnalysis, structure containing analysis, including list of missing fields
|
|
|
|
|
%
|
|
|
|
|
% see also: SetDataFilter, ProcessGnssMeas
|
|
|
|
|
|
|
|
|
|
%Author: Frank van Diggelen
|
|
|
|
|
%Open Source code for processing Android GNSS Measurements
|
|
|
|
|
|
|
|
|
|
%factored into a few main sub-functions:
|
|
|
|
|
% MakeCsv()
|
|
|
|
|
% ReadRawCsv()
|
|
|
|
|
% FilterData()
|
|
|
|
|
% PackGnssRaw()
|
|
|
|
|
% CheckGnssClock()
|
|
|
|
|
% ReportMissingFields()
|
|
|
|
|
|
|
|
|
|
%% Initialize outputs and inputs
|
|
|
|
|
gnssAnalysis.GnssClockErrors = 'GnssClock Errors.';
|
|
|
|
|
gnssAnalysis.GnssMeasurementErrors = 'GnssMeasurement Errors.';
|
|
|
|
|
gnssAnalysis.ApiPassFail = '';
|
|
|
|
|
if nargin<3, dataFilterIn = []; end
|
|
|
|
|
|
|
|
|
|
%% check we have the right kind of fileName
|
|
|
|
|
extension = fileName(end-3:end);
|
|
|
|
|
if ~any(strcmp(extension,{'.txt','.csv'}))
|
|
|
|
|
error('Expecting file name of the form "*.txt", or "*.csv"');
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
%% read log file into a numeric matrix 'S', and a cell array 'header'
|
|
|
|
|
rawCsvFile = MakeCsv(dirName,fileName);
|
|
|
|
|
[header,C] = ReadRawCsv(rawCsvFile);
|
|
|
|
|
|
|
|
|
|
%% apply dataFilter
|
|
|
|
|
[dataFilter] = CheckDataFilter(dataFilterIn,header);
|
|
|
|
|
C = FilterData(C,dataFilter,header);
|
|
|
|
|
|
|
|
|
|
%% pack data into gnssRaw structure
|
|
|
|
|
[gnssRaw,missing] = PackGnssRaw(C,header);
|
|
|
|
|
|
|
|
|
|
%% check clock and measurements
|
|
|
|
|
[gnssRaw,gnssAnalysis] = CheckGnssClock(gnssRaw,gnssAnalysis);
|
|
|
|
|
gnssAnalysis = ReportMissingFields(gnssAnalysis,missing);
|
|
|
|
|
|
|
|
|
|
%TBD on any early return, return gnssAnalysis.ApiPassFail = 'explanation'
|
|
|
|
|
% so that reporting tool reports why
|
|
|
|
|
|
|
|
|
|
end %end of function ReadGnssLogger
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
|
|
|
|
|
function csvFileName = MakeCsv(dirName,fileName)
|
|
|
|
|
%% make csv file, if necessary.
|
|
|
|
|
%And return extended csv file name (i.e. with full path in the name)
|
|
|
|
|
|
|
|
|
|
%TBD, maybe, do this entirely with Matlab read/write functions, make independent
|
|
|
|
|
%from grep and sed
|
|
|
|
|
|
|
|
|
|
%make extended file name
|
|
|
|
|
if dirName(end)~='/'
|
|
|
|
|
dirName = [dirName,'/']; %add /
|
|
|
|
|
end
|
|
|
|
|
csvFileName = [dirName,'prs.csv'];
|
|
|
|
|
if strcmp(fileName(end-3:end),'.csv')
|
|
|
|
|
return %input file is a csv file, nothing more to do here
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
extendedFileName = [dirName,fileName];
|
|
|
|
|
fprintf('\nReading file %s\n',extendedFileName)
|
|
|
|
|
|
|
|
|
|
%% read version
|
|
|
|
|
txtfileID = fopen(extendedFileName,'r');
|
|
|
|
|
if txtfileID<0
|
|
|
|
|
error('file ''%s'' not found',extendedFileName);
|
|
|
|
|
end
|
|
|
|
|
line='';
|
|
|
|
|
while isempty(strfind(lower(line),'version'))
|
|
|
|
|
line = fgetl(txtfileID);
|
|
|
|
|
if ~ischar(line) %eof or error occurred
|
|
|
|
|
if isempty(line)
|
|
|
|
|
error('\nError occurred while reading file %s\n',fileName)
|
|
|
|
|
end
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if line==-1
|
|
|
|
|
fprintf('\nCould not find "Version" in input file %s\n',fileName)
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
%look for the beginning of the version number, e.g. 1.4.0.0
|
|
|
|
|
iDigits = regexp(line,'\d'); %index into the first number found in line
|
|
|
|
|
v = sscanf(line(iDigits(1):end),'%d.%d.%d.%d',4);
|
|
|
|
|
if length(v)<4
|
|
|
|
|
v(end+1:4,1)=0; %make v into a length 4 column vector
|
|
|
|
|
end
|
|
|
|
|
%Now extract the platform
|
|
|
|
|
k = strfind(line,'Platform:');
|
|
|
|
|
if any(k)
|
|
|
|
|
sPlatform = line(k+9:end);
|
|
|
|
|
else
|
|
|
|
|
sPlatform = '';%set empty if 'Platform:' not found
|
|
|
|
|
end
|
|
|
|
|
if isempty(strfind(sPlatform,'N'))
|
|
|
|
|
%add || strfind(platform,'O') and so on for future platforms
|
|
|
|
|
fprintf('\nThis version of ReadGnssLogger supports Android N\n')
|
|
|
|
|
error('Found "%s" in log file, expected "Platform: N"',line)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
v1 = [1;4;0;0];
|
|
|
|
|
sCompare = CompareVersions(v,v1);
|
|
|
|
|
%Note, we need to check both the logger version (e.g. v1.0.0.0) and the
|
|
|
|
|
%Platform version "e.g. Platform: N" for any logic based on version
|
|
|
|
|
if strcmp(sCompare,'before')
|
|
|
|
|
fprintf('\nThis version of ReadGnssLogger supports v1.4.0.0 onwards\n')
|
|
|
|
|
error('Found "%s" in log file',line)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
%% write csv file with header and numbers
|
|
|
|
|
%We could use grep and sed to make a csv file
|
|
|
|
|
%fclose(txtfileID);
|
|
|
|
|
% system(['grep -e ''Raw,'' ',extendedFileName,...
|
|
|
|
|
% ' | sed -e ''s/true/1/'' -e ''s/false/0/'' -e ''s/# //'' ',...
|
|
|
|
|
% ' -e ''s/Raw,//'' ',... %replace "Raw," with nothing
|
|
|
|
|
% '-e ''s/(//g'' -e ''s/)//g'' > ',csvFileName]);
|
|
|
|
|
% On versions from v1.4.0.0 N:
|
|
|
|
|
% grep on "Raw," replace alpha characters amongst the numbers,
|
|
|
|
|
% remove parentheses in the header,
|
|
|
|
|
% note use of /g for "global" so sed acts on every occurrence in each line
|
|
|
|
|
% csv file "prs.csv" now contains a header row followed by numerical data
|
|
|
|
|
%
|
|
|
|
|
%But we'll do the same thing using Matlab, so people don't need grep/sed:
|
|
|
|
|
csvfileID = fopen(csvFileName,'w');
|
|
|
|
|
while ischar(line)
|
|
|
|
|
line = fgetl(txtfileID);
|
|
|
|
|
if isempty(strfind(line,'Raw,'))
|
|
|
|
|
continue %skip to next line
|
|
|
|
|
end
|
|
|
|
|
%Now 'line' contains the raw measurements header or data
|
|
|
|
|
line = strrep(line,'Raw,','');
|
|
|
|
|
line = strrep(line,'#',''); line = strrep(line,' ','');%remove '#' and spaces
|
|
|
|
|
%from versions v1.4.0.0 N we actually dont need to look for '(',')','true'
|
|
|
|
|
%or 'false' anymore. So we are done with replacing. That was easy.
|
|
|
|
|
fprintf(csvfileID,'%s\n',line);
|
|
|
|
|
end
|
|
|
|
|
fclose(txtfileID);
|
|
|
|
|
fclose(csvfileID);
|
|
|
|
|
if isempty(line) %line should be -1 at eof
|
|
|
|
|
error('\nError occurred while reading file %s\n',fileName)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
end %end of function MakeCsv
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
|
|
|
|
|
function [header,C] = ReadRawCsv(rawCsvFile)
|
|
|
|
|
%% read data from csv file into a numerical matrix 'S' and cell array 'header'
|
|
|
|
|
S = csvread(rawCsvFile,1,0);%read numerical data from second row onwards
|
|
|
|
|
%Note csvread fills ,, with zero, so we will need a lower level read function to
|
|
|
|
|
%tell the difference between empty fields and valid zeros
|
|
|
|
|
%T = readtable(csvFileName,'FileType','text'); %use this to debug
|
|
|
|
|
|
|
|
|
|
%read header row:
|
|
|
|
|
fid = fopen(rawCsvFile);
|
|
|
|
|
if fid<0
|
|
|
|
|
error('file ''%s'' not found',rawCsvFile);
|
|
|
|
|
end
|
|
|
|
|
headerString = fgetl(fid);
|
|
|
|
|
if isempty(strfind(headerString,'TimeNanos'))
|
|
|
|
|
error('\n"TimeNanos" string not found in file %s\n',fileName)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
header=textscan(headerString,'%s','Delimiter',',');
|
|
|
|
|
header = header{1}; %this makes header a numFieldsx1 cell array
|
|
|
|
|
numFields = size(header,1);
|
|
|
|
|
|
|
|
|
|
%check that numFields == size(S,2)
|
|
|
|
|
[~,M] = size(S); %M = number of columns
|
|
|
|
|
assert(numFields==M,...
|
|
|
|
|
'# of header names is different from # of columns of numerical data')
|
|
|
|
|
|
|
|
|
|
%read lines using formatSpec so we get TimeNanos and FullBiasNanos as
|
|
|
|
|
%int64, everything else as doubles, and empty values as NaN
|
|
|
|
|
formatSpec='';
|
|
|
|
|
for i=1:M
|
|
|
|
|
%lotsa || here, because we are comparing a vector, 'header'
|
|
|
|
|
%to a specific string each time. Not sure how to do this another way
|
|
|
|
|
%and still be able to easily read and debug. Better safe than too clever.
|
|
|
|
|
|
|
|
|
|
%longs
|
|
|
|
|
if i == find(strcmp(header,'TimeNanos')) || ...
|
|
|
|
|
i == find(strcmp(header,'FullBiasNanos')) || ...
|
|
|
|
|
i == find(strcmp(header,'ReceivedSvTimeNanos')) || ...
|
|
|
|
|
i == find(strcmp(header,'ReceivedSvTimeUncertaintyNanos')) || ...
|
|
|
|
|
i == find(strcmp(header,'CarrierCycles'))
|
|
|
|
|
formatSpec = sprintf('%s %%d64',formatSpec);
|
|
|
|
|
elseif 0
|
|
|
|
|
%ints
|
|
|
|
|
% TBD maybe %d32 for ints: AccumulatedDeltaRangeState, ...
|
|
|
|
|
% ConstellationType, MultipathIndicator, State, Svid
|
|
|
|
|
formatSpec = sprintf('%s %%d32',formatSpec);
|
|
|
|
|
else
|
|
|
|
|
%everything else doubles
|
|
|
|
|
formatSpec = sprintf('%s %%f',formatSpec);
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
C = textscan(fid,formatSpec,'Delimiter',',','EmptyValue',NaN);
|
|
|
|
|
fclose(fid);
|
|
|
|
|
|
|
|
|
|
end% of function ReadRawCsv
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function [dataFilter] = CheckDataFilter(dataFilterIn,header)
|
|
|
|
|
%% check that dataFilter has matching values in the header,
|
|
|
|
|
% extract this header value and add it as a second column for datafilter
|
|
|
|
|
dataFilter = dataFilterIn(:); %make dataFilter a column array
|
|
|
|
|
if isempty(dataFilterIn)
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
N = length(dataFilter);
|
|
|
|
|
%check that each value of dataFilter is valid: i.e. it contains one of the
|
|
|
|
|
%header types from the GnssLogger file
|
|
|
|
|
L = length(header);
|
|
|
|
|
for i=1:N
|
|
|
|
|
foundInHeader = zeros(1,L);
|
|
|
|
|
for j=1:L
|
|
|
|
|
foundInHeader(j) = any(strfind(dataFilter{i,1},header{j}));
|
|
|
|
|
end
|
|
|
|
|
if ~any(foundInHeader) %no match found (too coold)
|
|
|
|
|
error('dataFilter value ''%s'' has no matches in log file header',...
|
|
|
|
|
dataFilter{i,1});
|
|
|
|
|
elseif sum(foundInHeader)>1 % too many matches found (tooo hot)
|
|
|
|
|
error('dataFilter value ''%s'' has >1 match in log file header',...
|
|
|
|
|
dataFilter{i,1});
|
|
|
|
|
else %one match found (juust right)
|
|
|
|
|
k = find(foundInHeader);%index into where we found the matching header
|
|
|
|
|
dataFilter{i,2} = header{k};%save in second column
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
%dataFilter now has two columns: second one contains the matching header type
|
|
|
|
|
end% of function CheckDataFilter
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
|
|
|
|
|
function C = FilterData(C,dataFilter,header)
|
|
|
|
|
%% filter C based on contents of dataFilter
|
|
|
|
|
|
|
|
|
|
iS = ones(size(C{1})); %initialize index into rows of C
|
|
|
|
|
for i=1:size(dataFilter,1)
|
|
|
|
|
j=find(strcmp(header,dataFilter{i,2}));%j = index into header
|
|
|
|
|
%we should always be a value of j, because checkDataFilter checks for this:
|
|
|
|
|
assert(any(j),'dataFilter{i} = %s not found in header\n',dataFilter{i,1})
|
|
|
|
|
|
|
|
|
|
%now we must evaluate the expression in dataFilter{i}, for example:
|
|
|
|
|
% 'BiasUncertaintyNanos < 1e7'
|
|
|
|
|
%assign the relevant cell of C to a variable with same name as the header
|
|
|
|
|
ts = sprintf('%s = C{%d};',header{j},j);
|
|
|
|
|
eval(ts);
|
|
|
|
|
%create an index vector from the expression in dataFilter{i}
|
|
|
|
|
ts = sprintf('iSi = %s;',dataFilter{i,1});
|
|
|
|
|
eval(ts);
|
|
|
|
|
|
|
|
|
|
%AND the iS index values on each iteration of i
|
|
|
|
|
iS = iS & iSi;
|
|
|
|
|
end
|
|
|
|
|
%Check if filter removes all values,
|
|
|
|
|
if ~any(iS) %if all zeros
|
|
|
|
|
fprintf('\nAll measurements removed. Specify dataFilter less strictly, ')
|
|
|
|
|
dataFilter(:,1)
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
%Now remove all values of C indexed by iS
|
|
|
|
|
for i=1:length(C)
|
|
|
|
|
C{i} = C{i}(iS);
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end %end of function FilterDataS
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function [gnssRaw,missing] = PackGnssRaw(C,header)
|
|
|
|
|
%% pack data into gnssRaw, and report missing fields
|
|
|
|
|
assert(length(C)==length(header),...
|
|
|
|
|
'length(C) ~= length(header). This should have been checked before here')
|
|
|
|
|
|
|
|
|
|
gnssRaw = [];
|
|
|
|
|
%report clock fields present/missing, based on:
|
|
|
|
|
gnssClockFields = {...
|
|
|
|
|
'TimeNanos'
|
|
|
|
|
'TimeUncertaintyNanos'
|
|
|
|
|
'LeapSecond'
|
|
|
|
|
'FullBiasNanos'
|
|
|
|
|
'BiasUncertaintyNanos'
|
|
|
|
|
'DriftNanosPerSecond'
|
|
|
|
|
'DriftUncertaintyNanosPerSecond'
|
|
|
|
|
'HardwareClockDiscontinuityCount'
|
|
|
|
|
'BiasNanos'
|
|
|
|
|
};
|
|
|
|
|
missing.ClockFields = {};
|
|
|
|
|
|
|
|
|
|
%report measurements fields present/missing, based on:
|
|
|
|
|
gnssMeasurementFields = {...
|
|
|
|
|
'Cn0DbHz'
|
|
|
|
|
'ConstellationType'
|
|
|
|
|
'MultipathIndicator'
|
|
|
|
|
'PseudorangeRateMetersPerSecond'
|
|
|
|
|
'PseudorangeRateUncertaintyMetersPerSecond'
|
|
|
|
|
'ReceivedSvTimeNanos'
|
|
|
|
|
'ReceivedSvTimeUncertaintyNanos'
|
|
|
|
|
'State'
|
|
|
|
|
'Svid'
|
|
|
|
|
'AccumulatedDeltaRangeMeters'
|
|
|
|
|
'AccumulatedDeltaRangeUncertaintyMeters'
|
|
|
|
|
};
|
|
|
|
|
%leave these out for now, 'cause we dont care (for now), or they're deprecated,
|
|
|
|
|
% or they could legitimately be left out (because they are not computed in
|
|
|
|
|
% a particular GNSS implementation)
|
|
|
|
|
% SnrInDb, TimeOffsetNanos, CarrierFrequencyHz, CarrierCycles, CarrierPhase,
|
|
|
|
|
% CarrierPhaseUncertainty
|
|
|
|
|
missing.MeasurementFields = {};
|
|
|
|
|
|
|
|
|
|
%pack data into vector variables, if the fields are not NaNs
|
|
|
|
|
for j = 1:length(header)
|
|
|
|
|
if any(isfinite(C{j})) %not all NaNs
|
|
|
|
|
%TBD what if there are some NaNs, but not all. i.e. some missing
|
|
|
|
|
%data in the log file - TBD deal with this
|
|
|
|
|
eval(['gnssRaw.',header{j}, '=C{j};']);
|
|
|
|
|
elseif any(strcmp(header{j},gnssClockFields))
|
|
|
|
|
missing.ClockFields{end+1} = header{j};
|
|
|
|
|
elseif any(strcmp(header{j},gnssMeasurementFields))
|
|
|
|
|
missing.MeasurementFields{end+1} = header{j};
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
%TBD look for all zeros that can not legitimately be all zero,
|
|
|
|
|
%e.g. AccumulatedDeltaRangeMeters, and report these as missing data
|
|
|
|
|
|
|
|
|
|
end %end of function PackGnssRaw
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
|
|
|
|
|
function [gnssRaw,gnssAnalysis] = CheckGnssClock(gnssRaw,gnssAnalysis)
|
|
|
|
|
%% check clock values in gnssRaw
|
|
|
|
|
|
|
|
|
|
N = length(gnssRaw.ReceivedSvTimeNanos);
|
|
|
|
|
%Insist on the presence of TimeNanos (time from hw clock)
|
|
|
|
|
if ~isfield(gnssRaw,'TimeNanos')
|
|
|
|
|
error('TimeNanos data missing from GnssLogger file\n');
|
|
|
|
|
end
|
|
|
|
|
if ~isfield(gnssRaw,'FullBiasNanos')
|
|
|
|
|
error('FullBiasNanos is missing or zero, we need it to get gnssRaw week\n')
|
|
|
|
|
%TBD change to fatal warning, so a report is still generated, with warning
|
|
|
|
|
end
|
|
|
|
|
if ~isfield(gnssRaw,'BiasNanos')
|
|
|
|
|
gnssRaw.BiasNanos = zeros(N,1);
|
|
|
|
|
end
|
|
|
|
|
if ~isfield(gnssRaw,'HardwareClockDiscontinuityCount')
|
|
|
|
|
gnssRaw.HardwareClockDiscontinuityCount = zeros(N,1);
|
|
|
|
|
fprintf('WARNING: Added HardwareClockDiscontinuityCount because it is missing from GNSS Logger file\n');
|
|
|
|
|
end
|
|
|
|
|
%auto-detect sign of FullBiasNanos, if it is positive, give warning and change
|
|
|
|
|
if ~all(gnssRaw.FullBiasNanos<=0)
|
|
|
|
|
gnssRaw.FullBiasNanos = -1*gnssRaw.FullBiasNanos;
|
|
|
|
|
fprintf('WARNING: FullBiasNanos wrong sign. Should be negative. Auto changing inside ReadGpsLogger\n');
|
|
|
|
|
gnssAnalysis.GnssClockErrors = [gnssAnalysis.GnssClockErrors,...
|
|
|
|
|
sprintf(' FullBiasNanos wrong sign.')];
|
|
|
|
|
end
|
|
|
|
|
%now all FullBiasNanos should be negative - if there are any are > 0 it means
|
|
|
|
|
%something is very wrong with the log file, because FullBiasNanos has changed
|
|
|
|
|
%sign from a large negative to large positive number, and we must assert
|
|
|
|
|
assert(all(gnssRaw.FullBiasNanos<=0),...
|
|
|
|
|
'FullBiasNanos changes sign within log file, this should never happen')
|
|
|
|
|
|
|
|
|
|
%compute full cycle time of measurement, in milliseonds
|
|
|
|
|
gnssRaw.allRxMillis = int64((gnssRaw.TimeNanos - gnssRaw.FullBiasNanos)*1e-6);
|
|
|
|
|
%allRxMillis is now accurate to one millisecond (because it's an integer)
|
|
|
|
|
|
|
|
|
|
end %end of function CheckGnssClock
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
|
|
|
|
|
function gnssAnalysis = ReportMissingFields(gnssAnalysis,missing)
|
|
|
|
|
%% report missing clock and measurement fields in gnssAnalysis
|
|
|
|
|
|
|
|
|
|
%report missing clock fields
|
|
|
|
|
if ~isempty(missing.ClockFields)
|
|
|
|
|
gnssAnalysis.GnssClockErrors = sprintf(...
|
|
|
|
|
'%s Missing Fields:',gnssAnalysis.GnssClockErrors);
|
|
|
|
|
for i=1:length(missing.ClockFields)
|
|
|
|
|
gnssAnalysis.GnssClockErrors = sprintf(...
|
|
|
|
|
'%s %s,',gnssAnalysis.GnssClockErrors,missing.ClockFields{i});
|
|
|
|
|
end
|
|
|
|
|
gnssAnalysis.GnssClockErrors(end) = '.';%replace final comma with period
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
%report missing measurement fields
|
|
|
|
|
if ~isempty(missing.MeasurementFields)
|
|
|
|
|
gnssAnalysis.GnssMeasurementErrors = sprintf(...
|
|
|
|
|
'%s Missing Fields:',gnssAnalysis.GnssMeasurementErrors);
|
|
|
|
|
for i=1:length(missing.MeasurementFields)
|
|
|
|
|
gnssAnalysis.GnssMeasurementErrors = sprintf(...
|
|
|
|
|
'%s %s,',gnssAnalysis.GnssMeasurementErrors,...
|
|
|
|
|
missing.MeasurementFields{i});
|
|
|
|
|
end
|
|
|
|
|
gnssAnalysis.GnssMeasurementErrors(end) = '.';%replace last comma with period
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
%assign pass/fail
|
|
|
|
|
if isempty(missing.ClockFields) && isempty(missing.MeasurementFields)
|
|
|
|
|
gnssAnalysis.ApiPassFail = 'PASS';
|
|
|
|
|
else
|
|
|
|
|
gnssAnalysis.ApiPassFail = 'FAIL BECAUSE OF MISSING FIELDS';
|
|
|
|
|
end
|
|
|
|
|
end %end of function ReportMissingFields
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
|
|
|
|
|
% Copyright 2016 Google Inc.
|
|
|
|
|
%
|
|
|
|
|
% Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
% you may not use this file except in compliance with the License.
|
|
|
|
|
% You may obtain a copy of the License at
|
|
|
|
|
%
|
|
|
|
|
% http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
%
|
|
|
|
|
% Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
% distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
% See the License for the specific language governing permissions and
|
|
|
|
|
% limitations under the License.
|