|
|
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. |
|
|
|
|
|
|