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('GpsAdrResiduals needs the true position: llaDegDegM\n') 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.