|
|
|
|
function [xHat,z,svPos,H,Wpr,Wrr] = WlsPvt(prs,gpsEph,xo)
|
|
|
|
|
% [xHat,z,svPos,H,Wpr,Wrr] = WlsPvt(prs,gpsEph,xo)
|
|
|
|
|
% calculate a weighted least squares PVT solution, xHat
|
|
|
|
|
% given pseudoranges, pr rates, and initial state
|
|
|
|
|
%
|
|
|
|
|
% Inputs:
|
|
|
|
|
% prs: matrix of raw pseudoranges, and pr rates, each row of the form:
|
|
|
|
|
% [trxWeek,trxSeconds,sv,prMeters,prSigmaMeters,prrMps,prrSigmaMps]
|
|
|
|
|
% trxWeek, trxSeconds: Rx time of measurement
|
|
|
|
|
% where trxSeconds = seconds in the current GPS week
|
|
|
|
|
% sv: satellite id number
|
|
|
|
|
% prMeters, prSigmaMeters: pseudorange and standard deviation (meters)
|
|
|
|
|
% prrMps, prrSigmaMps: pseudorange rate and standard deviation (m/s)
|
|
|
|
|
% gpsEph: matching vector of GPS ephemeris struct, defined in ReadRinexNav
|
|
|
|
|
% xo: initial (previous) state, [x,y,z,bc,xDot,yDot,xDot,bcDot]'
|
|
|
|
|
% in ECEF coordinates(meters and m/s)
|
|
|
|
|
%
|
|
|
|
|
% Outputs: xHat: estimate of state update
|
|
|
|
|
% z = [zPr; zPrr] a-posteriori residuals (measured-calculated)
|
|
|
|
|
% svPos: matrix of calculated sv positions and sv clock error:
|
|
|
|
|
% [sv prn, x,y,z (ecef m), dtsv (s),xDot,yDot,zDot, dtsvDot]
|
|
|
|
|
% H: H observation matrix corresponding to svs in svPos(:,1)
|
|
|
|
|
% Wpr,Wrr Weights used in WlsPvt = 1/diag(sigma measurements)
|
|
|
|
|
% use these matrices to compute variances of xHat
|
|
|
|
|
%
|
|
|
|
|
% The PVT solution = xo + xHat, in ECEF coordinates
|
|
|
|
|
% For unweighted solution, set all sigmas = 1
|
|
|
|
|
|
|
|
|
|
%Author: Frank van Diggelen
|
|
|
|
|
%Open Source code for processing Android GNSS Measurements
|
|
|
|
|
|
|
|
|
|
jWk=1; jSec=2; jSv=3; jPr=4; jPrSig=5; jPrr=6; jPrrSig=7;%index of columns
|
|
|
|
|
|
|
|
|
|
[bOk,numVal] = checkInputs(prs, gpsEph, xo);
|
|
|
|
|
if ~bOk
|
|
|
|
|
error('inputs not right size, or not properly aligned with each other')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
xHat=[]; z=[]; H=[]; svPos=[];
|
|
|
|
|
xyz0 = xo(1:3);
|
|
|
|
|
bc = xo(4);
|
|
|
|
|
|
|
|
|
|
if numVal<4
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
ttxWeek = prs(:,jWk); %week of tx. Note - we could get a rollover, when ttx_sv
|
|
|
|
|
%goes negative, and it is handled in GpsEph2Pvt, where we work with fct
|
|
|
|
|
ttxSeconds = prs(:,jSec) - prs(:,jPr)/GpsConstants.LIGHTSPEED; %ttx by sv clock
|
|
|
|
|
% this is accurate satellite time of tx, because we use actual pseudo-ranges
|
|
|
|
|
% here, not corrected ranges
|
|
|
|
|
% write the equation for pseudorange to see the rx clock error exactly cancel
|
|
|
|
|
% to get precise GPS time: we subtract the satellite clock error from sv time,
|
|
|
|
|
% as done next:
|
|
|
|
|
dtsv = GpsEph2Dtsv(gpsEph,ttxSeconds);
|
|
|
|
|
dtsv = dtsv(:); %make into a column for compatibility with other time vectors
|
|
|
|
|
ttx = ttxSeconds - dtsv; %subtract dtsv from sv time to get true gps time
|
|
|
|
|
|
|
|
|
|
%calculate satellite position at ttx
|
|
|
|
|
[svXyzTtx,dtsv,svXyzDot,dtsvDot]=GpsEph2Pvt(gpsEph,[ttxWeek,ttx]);
|
|
|
|
|
svXyzTrx = svXyzTtx; %initialize svXyz at time of reception
|
|
|
|
|
|
|
|
|
|
%Compute weights ---------------------------------------------------
|
|
|
|
|
Wpr = diag(1./prs(:,jPrSig));
|
|
|
|
|
Wrr = diag(1./prs(:,jPrrSig));
|
|
|
|
|
|
|
|
|
|
%iterate on this next part tilL change in pos & line of sight vectors converge
|
|
|
|
|
xHat=zeros(4,1);
|
|
|
|
|
dx=xHat+inf;
|
|
|
|
|
whileCount=0; maxWhileCount=100;
|
|
|
|
|
%we expect the while loop to converge in < 10 iterations, even with initial
|
|
|
|
|
%position on other side of the Earth (see Stanford course AA272C "Intro to GPS")
|
|
|
|
|
while norm(dx) > GnssThresholds.MAXDELPOSFORNAVM
|
|
|
|
|
whileCount=whileCount+1;
|
|
|
|
|
assert(whileCount < maxWhileCount,...
|
|
|
|
|
'while loop did not converge after %d iterations',whileCount);
|
|
|
|
|
for i=1:length(gpsEph)
|
|
|
|
|
% calculate tflight from, bc and dtsv
|
|
|
|
|
dtflight = (prs(i,jPr)-bc)/GpsConstants.LIGHTSPEED + dtsv(i);
|
|
|
|
|
% Use of bc: bc>0 <=> pr too big <=> tflight too big.
|
|
|
|
|
% i.e. trx = trxu - bc/GpsConstants.LIGHTSPEED
|
|
|
|
|
% Use of dtsv: dtsv>0 <=> pr too small <=> tflight too small.
|
|
|
|
|
% i.e ttx = ttxsv - dtsv
|
|
|
|
|
svXyzTrx(i,:) = FlightTimeCorrection(svXyzTtx(i,:), dtflight);
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
%calculate line of sight vectors and ranges from satellite to xo
|
|
|
|
|
v = xyz0(:)*ones(1,numVal,1) - svXyzTrx';%v(:,i) = vector from sv(i) to xyz0
|
|
|
|
|
range = sqrt( sum(v.^2) );
|
|
|
|
|
v = v./(ones(3,1)*range); % line of sight unit vectors from sv to xo
|
|
|
|
|
|
|
|
|
|
svPos=[prs(:,3),svXyzTrx,dtsv(:)];
|
|
|
|
|
|
|
|
|
|
%calculate the a-priori range residual
|
|
|
|
|
prHat = range(:) + bc -GpsConstants.LIGHTSPEED*dtsv;
|
|
|
|
|
% Use of bc: bc>0 <=> pr too big <=> rangehat too big
|
|
|
|
|
% Use of dtsv: dtsv>0 <=> pr too small
|
|
|
|
|
|
|
|
|
|
zPr = prs(:,jPr)-prHat;
|
|
|
|
|
H = [v', ones(numVal,1)]; % H matrix = [unit vector,1]
|
|
|
|
|
|
|
|
|
|
%z = Hx, premultiply by W: Wz = WHx, and solve for x:
|
|
|
|
|
dx = pinv(Wpr*H)*Wpr*zPr;
|
|
|
|
|
|
|
|
|
|
% update xo, xhat and bc
|
|
|
|
|
xHat=xHat+dx;
|
|
|
|
|
xyz0=xyz0(:)+dx(1:3);
|
|
|
|
|
bc=bc+dx(4);
|
|
|
|
|
|
|
|
|
|
%Now calculate the a-posteriori range residual
|
|
|
|
|
zPr = zPr-H*dx;
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
% Compute velocities ---------------------------------------------------------
|
|
|
|
|
rrMps = zeros(numVal,1);
|
|
|
|
|
for i=1:numVal
|
|
|
|
|
%range rate = [satellite velocity] dot product [los from xo to sv]
|
|
|
|
|
rrMps(i) = -svXyzDot(i,:)*v(:,i);
|
|
|
|
|
end
|
|
|
|
|
prrHat = rrMps + xo(8) - GpsConstants.LIGHTSPEED*dtsvDot;
|
|
|
|
|
zPrr = prs(:,jPrr)-prrHat;
|
|
|
|
|
%z = Hx, premultiply by W: Wz = WHx, and solve for x:
|
|
|
|
|
vHat = pinv(Wrr*H)*Wrr*zPrr;
|
|
|
|
|
xHat = [xHat;vHat];
|
|
|
|
|
|
|
|
|
|
z = [zPr;zPrr];
|
|
|
|
|
|
|
|
|
|
end %end of function WlsPvt
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function [bOk,numVal] = checkInputs(prs, gpsEph, xo)
|
|
|
|
|
%utility function for WlsPvt
|
|
|
|
|
jWk=1; jSec=2; jSv=3; jPr=4; jPrSig=5; jPrr=6; jPrrSig=7;%index of columns
|
|
|
|
|
|
|
|
|
|
bOk=false;
|
|
|
|
|
%check inputs
|
|
|
|
|
numVal=size(prs,1);
|
|
|
|
|
if (max(prs(:,jSec))-min(prs(:,jSec)))> eps
|
|
|
|
|
return
|
|
|
|
|
elseif length(gpsEph)~=numVal
|
|
|
|
|
return
|
|
|
|
|
elseif any(prs(:,jSv) ~= [gpsEph.PRN]')
|
|
|
|
|
return
|
|
|
|
|
elseif any(size(xo) ~= [8,1])
|
|
|
|
|
return
|
|
|
|
|
elseif size(prs,2)~=7
|
|
|
|
|
return
|
|
|
|
|
else
|
|
|
|
|
bOk = true;
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
%We insist that gpsEph and prs are aligned first.
|
|
|
|
|
%ClosestGpsEph.m does this, and passes back indices for prs - this is the way to
|
|
|
|
|
%do it right, so we don't have nested searches for svId
|
|
|
|
|
|
|
|
|
|
end %end of function checkInputs
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
|
|
|
|
|
% 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.
|
|
|
|
|
|