function gpsPvt = GpsWlsPvt(gnssMeas,allGpsEph,bRaw) %gpsPvt = GpsWlsPvt(gnssMeas,allGpsEph,bRaw) %compute PVT from gnssMeas % Input: gnssMeas, structure of pseudoranges, etc. from ProcessGnssMeas % allGpsEph, structure with all ephemeris % [bRaw], default true, true => use raw pr, false => use smoothed % % Output: % gpsPvt.FctSeconds Nx1 time vector, same as gnssMeas.FctSeconds % .allLlaDegDegM Nx3 matrix, (i,:) = [lat (deg), lon (deg), alt (m)] % .sigmaLlaM Nx3 standard deviation of [lat,lon,alt] (m) % .allBcMeters Nx1 common bias computed with llaDegDegM % .allVelMps Nx3 (i,:) = velocity in NED coords % .sigmaVelMps Nx3 standard deviation of velocity (m/s) % .allBcDotMps Nx1 common freq bias computed with velocity % .numSvs Nx1 number of satellites used in corresponding llaDegDegM % .hdop Nx1 hdop of corresponding fix % %Algorithm: Weighted Least Squares %Author: Frank van Diggelen %Open Source code for processing Android GNSS Measurements if nargin<3 bRaw = true; else %check that smoothed pr fields exists in input if any(~isfield(gnssMeas,{'PrSmM','PrSmSigmaM'})) error('If bRaw is false, gnssMeas must have fields gnssMeas.PrSmM and gnssMeas.PrSmSigmaM') end end xo =zeros(8,1);%initial state: [center of the Earth, bc=0, velocities = 0]' weekNum = floor(gnssMeas.FctSeconds/GpsConstants.WEEKSEC); %TBD check for week rollover here (it is checked in ProcessGnssMeas, but %this function should stand alone, so we should check again, and adjust %tRxSeconds by +- a week if necessary) %btw, Q. why not just carry around fct and not worry about the hassle of %weeknumber, and the associated week rollover problems? % A. because you cannot get better than 1000ns (1 microsecond) precsision % when you put fct into a double. And that would cause errors of ~800m/s * 1us % (satellite range rate * time error) ~ 1mm in the range residual computation % So what? well, if you start processing with carrier phase, these errors % could accumulate. N = length(gnssMeas.FctSeconds); gpsPvt.FctSeconds = gnssMeas.FctSeconds; gpsPvt.allLlaDegDegM = zeros(N,3)+NaN; gpsPvt.sigmaLLaM = zeros(N,3)+NaN; gpsPvt.allBcMeters = zeros(N,1)+NaN; gpsPvt.allVelMps = zeros(N,3)+NaN; gpsPvt.sigmaVelMps = zeros(N,3)+NaN; gpsPvt.allBcDotMps = zeros(N,1)+NaN; gpsPvt.numSvs = zeros(N,1); gpsPvt.hdop = zeros(N,1)+inf; for i=1:N iValid = find(isfinite(gnssMeas.PrM(i,:))); %index into valid svid svid = gnssMeas.Svid(iValid)'; [gpsEph,iSv] = ClosestGpsEph(allGpsEph,svid,gnssMeas.FctSeconds(i)); svid = svid(iSv); %svid for which we have ephemeris numSvs = length(svid); %number of satellites this epoch gpsPvt.numSvs(i) = numSvs; if numSvs<4 continue;%skip to next epoch end %% WLS PVT ----------------------------------------------------------------- %for those svIds with valid ephemeris, pack prs matrix for WlsNav prM = gnssMeas.PrM(i,iValid(iSv))'; prSigmaM= gnssMeas.PrSigmaM(i,iValid(iSv))'; prrMps = gnssMeas.PrrMps(i,iValid(iSv))'; prrSigmaMps = gnssMeas.PrrSigmaMps(i,iValid(iSv))'; tRx = [ones(numSvs,1)*weekNum(i),gnssMeas.tRxSeconds(i,iValid(iSv))']; prs = [tRx, svid, prM, prSigmaM, prrMps, prrSigmaMps]; xo(5:7) = zeros(3,1); %initialize speed to zero [xHat,~,~,H,Wpr,Wrr] = WlsPvt(prs,gpsEph,xo);%compute WLS solution xo = xo + xHat; %extract position states llaDegDegM = Xyz2Lla(xo(1:3)'); gpsPvt.allLlaDegDegM(i,:) = llaDegDegM; gpsPvt.allBcMeters(i) = xo(4); %extract velocity states RE2N = RotEcef2Ned(llaDegDegM(1),llaDegDegM(2)); %NOTE: in real-time code compute RE2N once until position changes vNed = RE2N*xo(5:7); %velocity in NED gpsPvt.allVelMps(i,:) = vNed; gpsPvt.allBcDotMps(i) = xo(8); %compute HDOP H = [H(:,1:3)*RE2N', ones(numSvs,1)]; %observation matrix in NED P = inv(H'*H);%unweighted covariance gpsPvt.hdop(i) = sqrt(P(1,1)+P(2,2)); %compute variance of llaDegDegM %inside LsPvt the weights are used like this: % z = Hx, premultiply by W: Wz = WHx, and solve for x: % x = pinv(Wpr*H)*Wpr*zPr; % the point of the weights is to make sigma(Wz) = 1 % therefore, the variances of x come from diag(inv(H'Wpr'WprH)) P = inv(H'*(Wpr'*Wpr)*H); %weighted covariance gpsPvt.sigmaLLaM(i,:) = sqrt(diag(P(1:3,1:3))); %similarly, compute variance of velocity P = inv(H'*(Wrr'*Wrr)*H); %weighted covariance gpsPvt.sigmaVelMps(i,:) = sqrt(diag(P(1:3,1:3))); %%end WLS PVT -------------------------------------------------------------- end end %end of function GpsWlsPvt %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % 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.