Modify

Opened 5 weeks ago

Closed 3 weeks ago

#220 closed task (fixed)

Question about code BIA states and ionosphere constraints in BNC PPP

Reported by: asdf123hb67@… Owned by: stuerze
Priority: trivial Component: BNC
Version: Keywords: BNC PPP model
Cc:

Description

Dear BNC developers,

I am currently studying the PPP/PPP-AR model implemented in BNC 2.13.6, especially the dual-frequency uncombined PPP parameterization in the src/PPP/ path. I would like to ask for clarification about the theoretical interpretation of the code BIA states and the ionosphere constraint model in BNC.

In a GPS-only test configuration, for example:

PPP/lcGPS=P12&L12
PPP/constraints=no

my understanding is that the LC string is parsed as:

cG1, cG2, lG1, lG2

At the same time, the PPP filter creates the following code-bias-like states:

BIA cG1
BIA cG2

together with per-satellite ionosphere states ION and per-satellite, per-phase-LC ambiguity states. From my current reading of the source code and the PPP output, BIA cG1/cG2 appear to be real Kalman filter states, not merely diagnostic output quantities.

We also noticed that the common mode of BIA cG1/cG2 is datum-coupled with the receiver clock. BNC seems to regularize this direction through the prior variance of the code BIA states, for example through aprSigCodeBias. Therefore, we do not think that the absolute values of BIA cG1/cG2 should be interpreted as independently observable physical receiver code hardware delays.

According to my understanding of conventional dual-frequency UDUC PPP, when no external ionosphere product is used as a constraint, receiver code biases are usually not estimated as a separate receiver DCB parameter. Instead, they are absorbed through re-parameterization into receiver clock, ionosphere states, ambiguities, and related datum definitions. Explicit handling of receiver DCB becomes necessary mainly in ionosphere-constrained PPP, where GIM, SSR/VTEC/STEC, or regional ionosphere products are used to constrain the ionosphere states. Otherwise, receiver code biases may contaminate the ionosphere states and cause datum inconsistency with the external ionosphere constraints.

Therefore, my main question is why BNC still estimates BIA cG1/cG2 for the dual-frequency code LCs even when:

PPP/constraints=no

This seems different from a conventional minimal full-rank UDUC PPP parameterization. It looks more like a code-LC-wise bias-like nuisance-state parameterization.

Under this interpretation, BIA cG1/cG2 can be re-parameterized into a receiver-clock common component and a receiver-DCB-like differential combination, for example:

DCB_like = BIA cG2 - BIA cG1

However, the absolute values of BIA cG1/cG2 would then be datum-dependent and prior-dependent.

Could you please help clarify the following points?

  1. What is the intended interpretation of BIA cG1 and BIA cG2? Should they be interpreted as physical receiver code hardware delays, or rather as datum-dependent code-bias-like nuisance states?
  2. Why does BNC estimate separate BIA cG1/cG2 states for the enabled code LCs in dual-frequency uncombined PPP even when constraints=no and no external ionosphere constraint is used?
  3. What is the recommended interpretation of aprSigCodeBias in the BNC model? Is it mainly a numerical regularization parameter, or should it also be understood as a physical prior for receiver code biases?
  4. When ionosphere constraints are enabled, for example using GIM/IONEX or real-time SSR/VTEC/STEC ionosphere corrections, what exactly does the BNC ION state represent?an absolute slant ionosphere delay, or a residual correction with respect to the external ionosphere model?
  5. Does the ionosphere pseudo-observation directly constrain the absolute value of the ION state, or does BNC first apply the external SSR/GIM ionosphere correction on the computed side and then constrain the residual ION state close to zero?
  6. If the receiver code-bias datum and the external ionosphere-model datum are not perfectly consistent, why does BNC use an absolute ionosphere pseudo-observation rather than, for example, a satellite-differenced ionosphere constraint or an explicit receiver DCB random-walk parameter?
  7. Is the BIA cG1/cG2 code BIA parameterization mainly intended to support ionosphere-constrained PPP, especially real-time SSR/VTEC/STEC ionosphere constraints, by reducing receiver-code-bias contamination in the ionosphere state? If so, why is the same code BIA parameterization retained when constraints=no?

I am asking these questions because this implementation appears to differ from some common IC-PPP formulations in the literature, where receiver DCB is explicitly handled mainly when external ionosphere constraints are introduced. In BNC, the BIA cG* states look more like code-LC-wise bias-like nuisance states, and they seem to be used independently of whether ionosphere constraints are enabled. I would like to understand and document the BNC model correctly and avoid misinterpreting these states as directly observable physical receiver hardware delays.

Thank you very much for providing BNC as open-source software, and thank you in advance for your help in clarifying these model details.

Best regards,
Melissa Grove

Attachments (0)

Change History (3)

comment:1 by stuerze, 4 weeks ago

Dear Melissa,

Thank you for your request.

We will add as soon as possible a more detailed description of the implemented approach and come back to your again.

Best regards, Andrea

comment:2 by stuerze, 4 weeks ago

Dear Melissa,

you are right, the handling of ionospheric pseudo-observations was destroyed somehow.
It is resolved now and the result will be available within the next release of BNC.

Regarding your Questions:

Q1 — What is BIA cG1 / BIA cG2?

From pppParlist.cpp:

case bias:
  _epoSpec = true;            // re-created every epoch
  _sigma0  = OPT->_aprSigCodeBias;
  break;

And from pppParlist.cpp:

case bias:
  return (_LC == obsLC ? 1.0 : 0.0);

BIA cG1/cG2 are epoch-specific (white noise), per-constellation nuisance states — not physical receiver hardware delays.

_epoSpec=true 

means the parameter is deleted and re-created from scratch every epoch; it carries no information between epochs. What they absorb depends on configuration:

Without pseudoObsIono: the under-determined code datum left open by the phase-ambiguity parameterization — effectively the receiver inter-frequency code bias projected onto the code LCs

With pseudoObsIono: the receiver code bias referenced to the reference satellite's ionosphere (see Q4 below)

Q2 — Why BIA cG1/cG2 even when constraints=no?

From pppParlist.cpp:

int maxSkip = (OPT->_pseudoObsIono ? 1 : 2);
...
if (ar) {
  if (skip < maxSkip && lc.includesPhase()) { skip += 1; }
  else { required.push_back(new t_pppParam(t_pppParam::bias, ...)); }
}

When AR is active, phase LCs serve as datums (up to maxSkip). All remaining LCs — including all code LCs — get a BIA.
For dual-frequency GPS with [cG1, lG1, cG2, lG2] and maxSkip=2 (no iono):

LC action
cG1 no phase → BIA cG1
lG1 phase, skip=0 → datum (skip=1)
cG2 no phase → BIA cG2
lG2 phase, skip=1 → datum (skip=2)

Without BIA cG1/cG2, the code observations still have a rank deficit: you cannot separate the inter-frequency code bias from the (f₁²/f₂² − 1) × ION ionosphere scaling. The biases provide the missing rank. This is equally true with or without an external ionosphere constraint.

Q3 — Is aprSigCodeBias a physical prior or a regularization parameter?

From pppParlist.cpp:

_sigma0 = OPT->_aprSigCodeBias

From pppParlist.cpp:

_epoSpec = true, with _noise=0

The filter re-initializes the BIA variance every epoch. There is no time propagation, no physical delay model, no external calibration anchor. aprSigCodeBias is a pure numerical regularization parameter: it sets how freely the bias absorbs code residuals each epoch. A large value lets the bias soak up large code offsets; a small value forces it toward zero, spreading the cost to ION and clock. It has no physical meaning as a hardware delay measurement.

Q4 — What does the ION state represent after the fix?

After the re-implementation:

obsValue(GIM):

return _stecRefSat - _stecSat;        // satellite-differenced model STEC
// partial for GIM:
if (obs->prn() == _prn)                          return -1.0;   // ION_i
if (_prn == _refPrn && _prn.system() == ...)     return +1.0;   // ION_ref

The observation equation for satellite i is:

stecRefSat − stecSat_i  =  ION_ref − ION_i  ±  sigmaGIM

The reference satellite's own pseudo-obs has

obsValue = stecRef − stecRef = 0

, anchoring

ION_ref ≈ 0

Consequently:

ION_ref ≈ 0

(anchored to zero by its own GIM pseudo-obs)

ION_i ≈ iono_i − iono_ref

(differential ionosphere relative to the reference satellite)

The ION state is not absolute STEC and not a residual correction to the external model. It is the satellite-differenced slant ionosphere delay in L1-equivalent units, with the reference satellite's absolute ionosphere absorbed into the BIA states (see Q7).

For code and phase LCs (non-GIM), the ion partial is unchanged:

case ion:
  if (obs->prn() == _prn) {
    return ionoCoeff[obsLC._frq1];   // ≈ +1 for G1 code, −1 for G2 code, etc.
  }

So ION_i directly enters the code/phase observation equations with the correct ionosphere scaling factor.


Q5 — How does the external ionosphere constraint enter the filter?

The Kalman innovation is:

ll = obsValue(GIM) − cmpValue(GIM) − x0 · A

After the fix,

cmpValue(GIM) = 0.0;

Both ION states are epoch-specific (_epoSpec=true) and start at 0. So

x0 · A = ION_ref(0) · (+1) + ION_i(0) · (−1) = 0.
ll = (stecRefSat − stecSat_i) − 0 − 0 = stecRefSat − stecSat_i

The external model STEC difference is directly injected as the Kalman innovation. The filter adjusts ION_ref and ION_i until their difference matches the model's differential STEC, weighted by sigmaGIM. The external model does not enter cmpValue for real code/phase LCs — _model._ionoCodeDelay is absent from cmpValue for non-GIM LCs.

Q6 — Why satellite-differenced instead of absolute pseudo-observation?

This is exactly the design that was restored. The original code in SVN r8905 used satellite-differenced STEC, and the re-implementation restores it. The reason is fundamental:

The ION state shares a datum with the receiver code bias. In an uncombined PPP model, the equation for code on L1 of satellite i is:

C_i = ρ + clk_rec + clk_sat + trp + ION_i + rcv_code_bias_L1 + ...

ION_i and rcv_code_bias_L1 are linearly dependent on the code observations — the filter cannot separate them without an additional constraint.

An absolute pseudo-observation

ION_i = stecSat_i

would require that the receiver code bias datum is consistent with the GIM model's absolute TEC reference, which is generally not the case (GIM absolute accuracy is ±2–5 TECU).

A satellite-differenced constraint

ION_ref − ION_i = stecRef − stecSat_i

is datum-free on the receiver code bias side: the receiver code bias cancels in the difference. What remains is absorbed into BIA_cG1 (the reference satellite's absolute ionosphere plus receiver code bias), which is a free epoch-specific parameter anyway.

An explicit receiver DCB random-walk would add a persistent cross-frequency code bias state, complicating the parameter space and making the filter inconsistent with the existing BIA white noise design. The satellite-differenced approach achieves the same rank regularization within the existing parameterization.


Q7 — Is BIA primarily for iono-constrained PPP, or always needed with AR?

Always needed when AR is active, regardless of pseudoObsIono. The iono constraint changes maxSkip (1 vs 2), which changes how many and which biases appear:

Configuration maxSkip BIA states (dual-freq GPS) What ION_i absorbs
AR, no iono 2 BIA cG1, BIA cG2 total slant iono (free-floating)
AR + pseudoObsIono 1 BIA cG1, BIA cG2, BIA lG2 differential iono (iono_i − iono_ref)
no AR, no iono 2 none (code LCs are datums)
no AR + pseudoObsIono 1 BIA cG2 differential iono

With pseudoObsIono=true, the GIM constraint occupies one extra datum slot (reducing maxSkip from 2 to 1), which frees one phase LC to become a BIA (BIA lG2 appears). This is counterintuitive but correct: the iono constraint takes over the datum role previously held by the second phase LC.

After the fix, the relationship between BIA and ION is clean: BIA_cG1 absorbs receiver code bias + iono_ref (the reference satellite's absolute ionosphere), while ION_i carries only the differential part (iono_i − iono_ref). The receiver code-bias contamination that plagued the absolute-constraint design is eliminated by construction.

comment:3 by stuerze, 3 weeks ago

Resolution: fixed
Status: newclosed

Modify Ticket

Change Properties
Action
as closed The owner will remain stuerze.
The resolution will be deleted. Next status will be 'reopened'.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.