CLAS DD PPP-RTK Filter A0 Audit¶
This note audits the CLASLIB DD PPP-RTK filter that Option A will port behind
GNSS_PPP_CLAS_DD_FILTER, and maps it to the native CLAS PPP path in
libgnss++. A0 only adds the gated scaffold; A1 is the DD measurement-update
slice.
CLASLIB State Layout¶
CLASLIB's CLAS PPP-RTK path lives in /tmp/claslib/src/ppprtk.c and uses the
RTKLIB/CLAS state index macros from /tmp/claslib/src/cssr2osr.h.
The relevant macros are:
NP(opt) = dynamics ? 9 : 3, position-only for static mode and position/velocity/acceleration for dynamics (/tmp/claslib/src/cssr2osr.h:14).NF(opt) = opt->nf(/tmp/claslib/src/cssr2osr.h:15).IC(s,opt) = NP(opt) + sandIT(opt) = IC(0,opt) + NSYS, inherited receiver-clock/trop macros from the PPP family (/tmp/claslib/src/cssr2osr.h:16and/tmp/claslib/src/cssr2osr.h:17).NI(opt) = MAXSATonly forIONOOPT_ESTorIONOOPT_EST_ADPT; otherwise zero (/tmp/claslib/src/cssr2osr.h:18).NT(opt) = 0/1/3for no trop, ZTD, or ZTD+gradients (/tmp/claslib/src/cssr2osr.h:19).NL(opt) = NFREQGLOonly for GLONASS autocal mode (/tmp/claslib/src/cssr2osr.h:20).NR(opt) = NP + NI + NT + NL, andNX(opt) = NR + MAXSAT * NF(/tmp/claslib/src/cssr2osr.h:21-23).II(s,opt) = NP + s - 1for per-satellite ionosphere, 1-based satno (/tmp/claslib/src/cssr2osr.h:24).ITT(opt) = NP + NIfor the active troposphere block (/tmp/claslib/src/cssr2osr.h:25).IB(s,f,opt) = NR + MAXSAT * f + s - 1for per-satellite, per-frequency phase-bias/ambiguity states (/tmp/claslib/src/cssr2osr.h:26).
Audit finding: although IC()/IT() are defined in the shared header,
ppprtk.c's active NX() layout does not include receiver-clock states because
the CLAS measurement update is double-differenced and receiver clocks cancel.
The active non-ambiguity block is position/dynamics + II + ITT + optional NL;
ppprtk.c traces x(0) through NR(opt) before the ambiguity block
(/tmp/claslib/src/ppprtk.c:1558), and ppp_rtk_nx() returns NX(opt)
(/tmp/claslib/src/ppprtk.c:1360-1364).
CLASLIB State Time Update¶
initx() sets one state value and zeros all covariance cross terms for that
state (/tmp/claslib/src/ppprtk.c:181-187).
Position and dynamics:
- Fixed mode pins position to
opt.ruwith tiny variance (/tmp/claslib/src/ppprtk.c:201-204). - First epoch initializes position from
rtk->sol.rrwithVAR_POS; dynamic mode also initializes velocity and acceleration (/tmp/claslib/src/ppprtk.c:207-214). - Large mean position variance resets position/velocity/acceleration
(
/tmp/claslib/src/ppprtk.c:217-228). - Dynamic mode propagates position/velocity/acceleration with an
Fmatrix (/tmp/claslib/src/ppprtk.c:230-295). - Dynamic process noise uses
prn[3]andprn[4]for acceleration (/tmp/claslib/src/ppprtk.c:297-304). - Static/non-dynamic position process noise uses
prn[5]andprn[6]; ifprnadptis enabled, the accumulated adaptivertk->Qblock is used instead (/tmp/claslib/src/ppprtk.c:305-318).
Troposphere:
udtrop()initializesITT()toINIT_ZWDwithstd[2]^2; gradients, when enabled, initialize to1E-6withVAR_GRA(/tmp/claslib/src/ppprtk.c:323-338).- Existing ZTD gets
prn[2]^2 * tt; gradients get(0.3 * prn[2])^2 * |tt|(/tmp/claslib/src/ppprtk.c:339-345). - When CLAS trop SSR is valid at first reset,
ppp_rtk_pos()can zero the ZTD state variance/process noise (/tmp/claslib/src/ppprtk.c:1594-1601).
Ionosphere:
udion()resetsII(s)to zero if an observed satellite exceeds the outage counter (/tmp/claslib/src/ppprtk.c:357-371).- New ionosphere states initialize to
1E-6withstd[1]^2(/tmp/claslib/src/ppprtk.c:374-379). - Normal iono process noise is
prn[1]^2 * cos(el)^2.IONOOPT_EST_ADPTkeeps an adaptive per-statertk->Q, clamped betweenprn[1]^2andprn[7]^2before addingqi * ttto covariance (/tmp/claslib/src/ppprtk.c:381-401). - After the float update,
IONOOPT_EST_ADPTupdates each iono process-noise entry from the posterior covariance usingforgetionandafgainion(/tmp/claslib/src/ppprtk.c:1653-1659). - A CLAS grid/network change zeros every
II()state (/tmp/claslib/src/ppprtk.c:1506-1511).
Ambiguities / phase bias:
udbias_ppp()clears slip/reset flags, detects LLI and geometry-free slips, then increments outage counters (/tmp/claslib/src/ppprtk.c:470-489).- Instantaneous AR or max-outage resets an
IB(s,f)state to zero and resets lock count (/tmp/claslib/src/ppprtk.c:491-509). - Each observed ambiguity gets
prn[0]^2 * ttprocess noise, and slip resets the state to zero (/tmp/claslib/src/ppprtk.c:511-520). - Initial ambiguity value is
(phase_m - code_m - common_bias) / wavelength, so theIB()state is in cycles;ddres()later multiplies it by wavelength (/tmp/claslib/src/ppprtk.c:524-548and/tmp/claslib/src/ppprtk.c:985-991).
Top-level reset semantics:
- Time gaps beyond
maxobslosszero every state (/tmp/claslib/src/ppprtk.c:1471-1475). - Periodic reset zeros every state, clears CSSR state, and returns a single
solution (
/tmp/claslib/src/ppprtk.c:1477-1489). nav->filresetzeros all non-position states in static mode, or all states in dynamics mode (/tmp/claslib/src/ppprtk.c:1493-1503).udstate_ppp()applies position, trop, ambiguity, then ionosphere time updates in that order (/tmp/claslib/src/ppprtk.c:557-578).
CLASLIB DD Residual Model¶
ddres() is the core CLAS DD measurement builder
(/tmp/claslib/src/ppprtk.c:794-1032).
Reference-satellite selection:
- Rows are grouped by system group
mand byfover phase and code (/tmp/claslib/src/ppprtk.c:832-847). - For each group, the reference satellite is the valid satellite with highest
elevation; stale SSR channels and QZSS mode rules are filtered before
selection (
/tmp/claslib/src/ppprtk.c:847-880). - The selected reference is remembered in
refsat/refsat2; reference changes are logged and can trigger phase-bias reinitialization in merged L6 channel mode (/tmp/claslib/src/ppprtk.c:884-946).
Row formation:
- Each row is a single/double difference of zero-difference residuals:
reference minus target satellite (
/tmp/claslib/src/ppprtk.c:954-959). - Position partials are
-e_ref + e_sat(/tmp/claslib/src/ppprtk.c:961-963). - Ionosphere contributes opposite signs for phase/code and scales by
(lambda_f/lambda_L1)^2 * ionmapf;II(ref)andII(sat)get opposite columns (/tmp/claslib/src/ppprtk.c:965-975). - Troposphere subtracts mapped reference-target trop and writes derivatives
into
ITT()+k(/tmp/claslib/src/ppprtk.c:977-984). - Phase rows subtract
lambda_ref * IB(ref,f) - lambda_sat * IB(sat,f)and write the same signed wavelengths intoH(/tmp/claslib/src/ppprtk.c:985-991). - Code rows have no ambiguity column.
- Phase and code variances are elevation-weighted through
varerr(); L2 phase gets an extra(2.55/1.55)^2factor (/tmp/claslib/src/ppprtk.c:997-1008). ddcov()combines per-satelliteRi/Rjterms into the DD covariance (/tmp/claslib/src/ppprtk.c:1026-1027).
varerr() itself switches between extended code/phase error models and the
normal RTKLIB model, adds iono/trop error terms when those states are not
estimated, and weights by 1/sin(el)^2
(/tmp/claslib/src/ppprtk.c:130-178).
Filter and fixed-state publication:
ppp_rtk_pos()builds zero-difference OSR residuals, callsddres()for the DD design matrix, runsfilter2(), re-evaluates postfit DD rows, and accepts the float update when chi-square passes (/tmp/claslib/src/ppprtk.c:1575-1640).- On success it commits
xp/Ppback tortk->x/P(/tmp/claslib/src/ppprtk.c:1670-1675). resamb_LAMBDA()builds a single-to-DD ambiguity transform, extractsQbandQab, runs LAMBDA, conditionsxa/Pa, and restores single-difference ambiguity states (/tmp/claslib/src/ppprtk.c:1166-1265).- Fixed postfit residuals call
ddres()again onxa; accepted fixed states becomeSOLQ_FIX, and hold feedback can apply ambiguity constraints (/tmp/claslib/src/ppprtk.c:1702-1768).
Native CLAS Path¶
Native PPP state:
PPPStatestores position, velocity, receiver clocks, ZTD, optional ionosphere states, and dynamically appended ambiguity maps (include/libgnss++/algorithms/ppp_shared.hpp:282-303).- Standard PPP initialization reserves receiver clock states first, then
optional ionosphere states, then ambiguity states
(
src/algorithms/ppp_state.cpp:300-389). - Standard prediction applies process noise to position/velocity, receiver
clocks, ISBs, ZTD, ambiguities, and ionosphere maps
(
src/algorithms/ppp_state.cpp:399-467).
Native CLAS float filter:
processEpochCLAS()drives CLAS epochs: SPP seed, cycle slips, CLASprepareEpochState(), OSR context construction, ambiguity-state allocation, native measurement update, then optional WLNL AR (src/algorithms/ppp_clas_epoch.cpp:177-486).prepareEpochState()initializes and predicts the CLAS filter state, using residual ionosphere satellites collected from observations/SSR products (src/algorithms/ppp_clas.cpp:459-517).- CLAS initialization uses
[pos, GPS clock, GLO clock, ZTD, iono...], not the CLASLIB fixedII/IBlayout (src/algorithms/ppp_clas.cpp:519-548). - Native CLAS prediction uses white receiver-clock variance, ZTD process noise,
optional iono process noise, ambiguity process noise, and clock/position
decoupling (
src/algorithms/ppp_clas.cpp:604-647). - OSR-corrected code/phase rows are built as undifferenced rows with receiver
clock columns, ZTD columns, iono columns, and ambiguity columns in meters
(
src/algorithms/ppp_clas.cpp:746-1005). - Native CLAS adds trop and STEC pseudo-observation constraints
(
src/algorithms/ppp_clas.cpp:1008-1062). - Carrier phase rows are single-differenced by system/signal by default, but
code rows stay undifferenced unless a parity gate requests code SD
(
src/algorithms/ppp_clas.cpp:1064-1140). - The Kalman update is a normal linear update on native rows
(
src/algorithms/ppp_clas.cpp:1145-1205).
Current native AR:
- CLAS per-frequency mode forces
DD_WLNLinprocessEpochCLAS()(src/algorithms/ppp_clas_epoch.cpp:187-195). resolveAmbiguitiesWLNL()fixes WL integers from MW averages, then callsppp_ar::resolveWlnlFix()using native ambiguity states and OSR-derived NL information (src/algorithms/ppp_wlnl.cpp:113-212).ppp_ar::tryDirectDdFix()forms DD ambiguity pairs from the native ambiguity map, builds DD covariance from native covariance, runs LAMBDA, and conditions the native head state (src/algorithms/ppp_ar.cpp:155-350).- The current fixed-position WLS is post-hoc:
solveFixedPosition()builds fixed NL observations after the float filter state exists (src/algorithms/ppp_wlnl.cpp:214-240).
Architectural gap:
- Native CLAS is an undifferenced/partly single-differenced float PPP filter with receiver-clock states, meter-valued ambiguity states, and post-hoc DD-WLNL AR.
- CLASLIB
ppprtk.cis a DD PPP-RTK filter: phase and code rows are DD before the Kalman update, receiver-clock states cancel,II()and cycle-valuedIB()have fixed RTKLIB satno-indexed slots, and LAMBDA publishes a constrainedxafrom that same DD covariance. - Therefore the A1 port should not only adjust AR. It must build the DD design matrix and covariance first, then make AR consume that DD-native ambiguity covariance.
A1 Entry Points¶
src/algorithms/ppp_clas_epoch.cpp: the A0 hook returns immediately after the native float update whenppp_config_.use_clas_dd_filteris true. A1 should replace this passthrough return with a DD prediction/update call fed byepoch_context,osr_corrections, andepoch_atmos.include/libgnss++/algorithms/ppp_clas_dd.hppandsrc/algorithms/ppp_clas_dd.cpp: extendDdFilterScaffoldfrom a typed layout/snapshot into a persistent DD filter with CLASLIB-styleudpos/udtrop/udion/udbiasequivalents.src/algorithms/ppp_clas_dd.cpp: add an A1 DD row builder matchingddres()reference selection, phase+code differencing,varerr()weights, andII/ITT/IBcolumns.src/algorithms/ppp_atmosphere.cpp/prepareClasEpochContext()wiring: consume lifecycle atmosphere tokens already exposed asepoch_atmos; this is where A1 should source the CLAS STEC/trop inputs instead of adding another correction path.- Keep
src/algorithms/ppp_wlnl.cppandsrc/algorithms/ppp_ar.cppunchanged until A2, when LAMBDA should be moved to the DD filter covariance rather than the old native ambiguity datum.