|
|
function [adrResid]= GpsAdrResiduals(gnssMeas,allGpsEph,llaDegDegM) |
|
|
% [adrResid]= GpsAdrResiduals(gnssMeas,allGpsEph,llaDegDegM) |
|
|
% Compute residuals from GPS Accumulated Delta Ranges |
|
|
% |
|
|
% Inputs: |
|
|
% gnssMeas.FctSeconds = Nx1 vector. Rx time tag of measurements. |
|
|
% .ClkDCount = Nx1 vector. Hw clock discontinuity count |
|
|
% .Svid = 1xM vector of all svIds found in gnssRaw. |
|
|
% ... |
|
|
% .tRxSeconds = NxM time of reception, seconds of gps week |
|
|
% .tTxSeconds = NxM time of tranmission, seconds of gps week |
|
|
% .AdrM = NxM accumulated delta range (= -k*carrier phase) |
|
|
% ... |
|
|
% |
|
|
% allGpsEph, structure with all ephemeris |
|
|
% llaDegDegM [1x3] true position |
|
|
% |
|
|
% Output: |
|
|
% adrResid.FctSeconds = Nx1 time vector, same as gnssMeas.FctSeconds |
|
|
% .Svid0 = reference satellite for single differences |
|
|
% .Svid = 1xM vector of all svid |
|
|
% .ResidM = [NxM] adr residuals |
|
|
% |
|
|
%Algorithm: compute single difference from sv to reference satellite svid0, then |
|
|
% diff from reference time: tk - t0 (where t0 is the first common epoch for |
|
|
% sv & svid0), then subtract expected values |
|
|
|
|
|
%Author: Frank van Diggelen |
|
|
%Open Source code for processing Android GNSS Measurements |
|
|
|
|
|
adrResid = []; |
|
|
if ~any(any(isfinite(gnssMeas.AdrM) & gnssMeas.AdrM~=0)) |
|
|
%Nothing in AdrM but NaNs and zeros |
|
|
return |
|
|
end |
|
|
if nargin<3 || isempty(llaDegDegM) |
|
|
fprintf('\nGpsAdrWlsPvt needs the true position: llaDegDegM') |
|
|
return |
|
|
end |
|
|
xyz0M = Lla2Xyz(llaDegDegM); |
|
|
|
|
|
M = length(gnssMeas.Svid); |
|
|
N = length(gnssMeas.FctSeconds); |
|
|
|
|
|
weekNum = floor(gnssMeas.FctSeconds/GpsConstants.WEEKSEC); |
|
|
|
|
|
adrResid.FctSeconds = gnssMeas.FctSeconds; |
|
|
adrResid.Svid0 = []; |
|
|
adrResid.Svid = gnssMeas.Svid; |
|
|
adrResid.ResidM = zeros(N,M)+NaN; |
|
|
|
|
|
%From gps.h: |
|
|
%/* However, it is expected that the data is only accurate when: |
|
|
% * 'accumulated delta range state' == GPS_ADR_STATE_VALID. |
|
|
%*/ |
|
|
% #define GPS_ADR_STATE_UNKNOWN 0 |
|
|
% #define GPS_ADR_STATE_VALID (1<<0) |
|
|
% #define GPS_ADR_STATE_RESET (1<<1) |
|
|
% #define GPS_ADR_STATE_CYCLE_SLIP (1<<2) |
|
|
|
|
|
%choose Svid0 as the satellite that has most valid adr |
|
|
numValidAdr = zeros(1,M); |
|
|
for j=1:M |
|
|
numValidAdr(j) = length(find(bitand(gnssMeas.AdrState(:,j),2^0))); |
|
|
end |
|
|
[~,j0] = max(numValidAdr); |
|
|
adrResid.Svid0 = gnssMeas.Svid(j0); |
|
|
svid = gnssMeas.Svid; |
|
|
|
|
|
%% Compute expected pseudoranges |
|
|
prHatM = zeros(N,M)+NaN; %to store expected pseudoranges |
|
|
%"pseudo" here refers to the clock error in the satellite, not the receiver |
|
|
%compute expected pr at each epoch |
|
|
for i=1:N |
|
|
for j=1:M |
|
|
ttxSeconds = gnssMeas.tTxSeconds(i,j); |
|
|
if isnan(ttxSeconds) |
|
|
continue %skip to next |
|
|
end |
|
|
[gpsEph,iSv]= ClosestGpsEph(allGpsEph,svid(j),gnssMeas.FctSeconds(i)); |
|
|
if isempty(iSv) |
|
|
continue; %skip to next |
|
|
end |
|
|
%compute pr for this sv |
|
|
dtsv = GpsEph2Dtsv(gpsEph,ttxSeconds); |
|
|
ttxSeconds = ttxSeconds - dtsv;%subtract dtsv from sv time to get true gps time |
|
|
|
|
|
%calculate satellite position at ttx: |
|
|
[svXyzTtxM,dtsv]=GpsEph2Xyz(gpsEph,[weekNum(i),ttxSeconds]); |
|
|
%in ECEF coordinates at trx: |
|
|
dtflightSeconds = norm(xyz0M - svXyzTtxM)/GpsConstants.LIGHTSPEED; |
|
|
svXyzTrxM = FlightTimeCorrection(svXyzTtxM, dtflightSeconds); |
|
|
prHatM(i,j) = norm(xyz0M - svXyzTrxM) - GpsConstants.LIGHTSPEED*dtsv; |
|
|
% Use of dtsv: dtsv>0 <=> pr too small |
|
|
end |
|
|
end |
|
|
|
|
|
%% Compute single difference, then delta from t0, then residuals |
|
|
iT0 = zeros(1,M); %to store common start index for svid and svid0 |
|
|
for i=1:N %loop over time |
|
|
if ~bitand(gnssMeas.AdrState(i,j0),2^0) |
|
|
continue; %skip to next epoch |
|
|
end |
|
|
%now we have valid Adr for reference satellite, svid(j0) at this epoch |
|
|
for j=1:M %loop over svid |
|
|
if j==j0 |
|
|
continue; %skip over reference satellite |
|
|
end |
|
|
if ~bitand(gnssMeas.AdrState(i,j),2^0) %no valid adr for this svid |
|
|
iT0(j) = 0;%reset common start index for this svid |
|
|
continue %skip to next svid |
|
|
elseif ~iT0(j) %we haven't yet set a common start, |
|
|
if all(isfinite(prHatM(i,[j,j0]))) %we have valid prHat at this epoch |
|
|
iT0(j) = i;%set common start index |
|
|
end |
|
|
end |
|
|
%Now we have valid Adr for svid(j) and svid(j0) |
|
|
|
|
|
i0 = iT0(j); %common start index for svid(j) and svid(j0) |
|
|
if i>i0 && all(isfinite(prHatM(i,[j,j0])))%valid prHat at this epoch |
|
|
delAdrM = (gnssMeas.AdrM(i,j) - gnssMeas.AdrM(i,j0)) - ... |
|
|
(gnssMeas.AdrM(i0,j) - gnssMeas.AdrM(i0,j0)); |
|
|
delPrHatM = (prHatM(i,j) - prHatM(i,j0)) - ... |
|
|
(prHatM(i0,j) - prHatM(i0,j0)); |
|
|
adrResid.ResidM(i,j) = delAdrM - delPrHatM; |
|
|
%by all the above 'isfinite' checks, residM should not be NaN |
|
|
assert(isfinite(adrResid.ResidM(i,j)),... |
|
|
'Residual should be finite, check the above logic'); |
|
|
end |
|
|
end |
|
|
end |
|
|
|
|
|
end %end of function GpsAdrResiduals |
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
|
|
|
|
|
% 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.
|
|
|
|