Free Network Adjustment with Minimal Constraints

Holding published control fixed makes an adjustment answer a question you did not ask: it mixes the quality of your observations with the quality of somebody else’s coordinates. This guide, part of least squares adjustment for control networks, sets up the free network adjustment — minimally constrained, so the datum defect is removed without distorting the geometry — and shows how to read its residuals as a statement about your survey alone.

The Datum Defect

A network of relative observations — baselines, distances, angles — determines the shape of the network and nothing about where it sits. In three dimensions with GNSS vectors that leaves three degrees of freedom undetermined: the translation of the whole network. Add distance and angle observations only, and rotation and scale become undetermined too, for up to seven in total. The normal matrix is singular by exactly that many dimensions, and the singularity is the datum defect.

Datum defect by observation type Four observation types with what they determine and the resulting defect. GNSS vectors carry orientation and scale, so only the three translations are undetermined. Distances carry scale but not orientation, adding three rotations for six. Angles carry orientation but not scale, adding one for four. A network of angles and directions alone leaves all seven undetermined, which is the classical seven-parameter datum defect. Determines Defect GNSS vectors orientation + scale 3 Distances only scale 6 Angles only orientation 4 Angles + directions shape only 7

Figure — what each observation type can see, and therefore what is left undetermined.

A constrained adjustment removes it by fixing published coordinates. That places the network in the published frame and folds the published control’s own error into the residuals. A minimally constrained or free adjustment removes it with the smallest possible constraint — an inner constraint that fixes the network’s centroid and, where needed, its orientation and scale, without preferring any station. The residuals then describe only the internal consistency of the observations.

x^=(ATWA+GGT)1ATWl\hat{\mathbf{x}} = (\mathbf{A}^{\mathsf{T}} \mathbf{W} \mathbf{A} + \mathbf{G}\mathbf{G}^{\mathsf{T}})^{-1}\mathbf{A}^{\mathsf{T}} \mathbf{W} \mathbf{l}

where the columns of G\mathbf{G} span the null space of the design matrix — the directions the observations cannot see.

Complete Runnable Implementation

from __future__ import annotations

import numpy as np
from scipy import linalg


def translation_null_space(n_stations: int) -> np.ndarray:
    """Null space of a GNSS vector network: three uniform translations.

    Each column moves every station by one unit along one axis, which no relative
    observation can detect. Normalised so the inner constraint is symmetric across
    stations — no station is privileged.
    """
    g = np.zeros((3 * n_stations, 3), dtype=np.float64)
    for axis in range(3):
        g[axis::3, axis] = 1.0
    return g / np.sqrt(n_stations)


def free_adjustment(design: np.ndarray, observed: np.ndarray, weight: np.ndarray,
                    null_space: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
    """Minimally constrained adjustment via the inner-constraint normal matrix.

    Returns (parameters, parameter covariance, residuals). The covariance is the
    pseudo-inverse of the singular normal matrix, so it describes RELATIVE
    uncertainty — absolute position is undetermined by construction.
    """
    n = design.T @ weight @ design
    if null_space.shape[0] != n.shape[0]:
        raise ValueError("null space and normal matrix disagree on the parameter count")
    augmented = n + null_space @ null_space.T
    c, low = linalg.cho_factor(augmented)
    x = linalg.cho_solve((c, low), design.T @ weight @ observed)
    inv_aug = linalg.cho_solve((c, low), np.eye(n.shape[0]))
    # Remove the artificial constraint from the covariance: project it out.
    p = np.eye(n.shape[0]) - null_space @ null_space.T
    cov = p @ inv_aug @ p.T
    return x, cov, observed - design @ x


def datum_defect(design: np.ndarray, weight: np.ndarray, tol: float = 1e-8) -> int:
    """How many dimensions the observations genuinely cannot determine."""
    n = design.T @ weight @ design
    eig = np.linalg.eigvalsh(n)
    return int(np.sum(eig < tol * max(eig.max(), 1.0)))

Parameter Reference

Name Type Note
design np.ndarray (m, 3n) for m observation components and n stations
null_space np.ndarray (3n, d) with d the datum defect; columns must be orthonormal
weight np.ndarray Block diagonal, from the observation covariances
cov np.ndarray Relative uncertainty; absolute position is undetermined
datum_defect int 3 for GNSS vectors; up to 7 for distance/angle networks

Worked Example

import numpy as np

# Four stations, three independent baselines: a chain with a 3-dimensional defect.
n_st = 4
A = np.zeros((9, 12))
for k, (i, j) in enumerate([(0, 1), (1, 2), (2, 3)]):
    A[3 * k:3 * k + 3, 3 * i:3 * i + 3] = -np.eye(3)
    A[3 * k:3 * k + 3, 3 * j:3 * j + 3] = np.eye(3)
W = np.eye(9) / (0.002 ** 2)

print("datum defect:", datum_defect(A, W))
# datum defect: 3

l = np.array([100.001, 0.002, -0.001, 99.998, 0.001, 0.003, 100.002, -0.002, 0.001])
x, cov, v = free_adjustment(A, l, W, translation_null_space(n_st))
print(np.round(v * 1000, 3))          # residuals in mm
# [ 0.  0.  0.  0.  0.  0.  0.  0.  0.]

A chain with no redundancy fits its observations exactly, which is the correct and unhelpful answer — and it is exactly what the residuals should say. Add a closing baseline and the residuals become informative, which is the practical argument for closing every traverse.

Three ways to remove a datum defect Three constraint choices. Fixing several published stations places the network in the published frame and distorts its shape by whatever error that control carries. Fixing one station removes the translation defect and privileges that station, so its own error radiates outward and the residual pattern shows a bullseye centred on it. An inner constraint fixes the network centroid without preferring any station, removing the defect and leaving the shape untouched. A network with a datum defect Fix several stations distorts the shape Fix one station privileges that mark Inner constraint no station preferred Residuals describe the SURVEY only under the inner constraint

Figure — the same network under three constraint choices; only one leaves the shape alone.

Validation Check

def assert_constraint_is_minimal(design: np.ndarray, weight: np.ndarray,
                                 null_space: np.ndarray) -> None:
    """The constraint must span the null space and nothing else."""
    n = design.T @ weight @ design
    residual = n @ null_space
    assert np.allclose(residual, 0.0, atol=1e-6), (
        "the constraint directions are not in the null space; this constraint "
        "distorts the network rather than merely placing it"
    )
    assert np.linalg.matrix_rank(null_space) == null_space.shape[1], (
        "the constraint columns are not independent"
    )

That first assertion is the definition of minimal: a constraint direction that the observations can see is a constraint that changes the shape of the network, not just its position.

Common Mistakes

Fixing one station and calling it a free adjustment. Fixing a single station does remove a three-dimensional translation defect, and it is not the same thing: it privileges that station, so its own error propagates outward and the residual pattern shows a bullseye centred on it. An inner constraint distributes the placement across every station.

Reading absolute uncertainties off a free adjustment. The parameter covariance from a minimally constrained solution describes relative geometry. Quoting it as absolute positional uncertainty understates the truth by exactly the uncertainty of the datum you never introduced.

Skipping the free adjustment because the constrained one passed. The constrained residuals contain both your observations and the published control’s error, so a pass can hide a poor survey placed onto forgiving control — or condemn a good survey held to a distorted network. Running both, in that order, is what separates the two.

Reading the Two Sets of Residuals Together

The free and constrained adjustments produce two residual sets from the same observations, and the comparison between them is more informative than either alone.

Reading the free and constrained residuals together Four combinations. Both small means the survey is consistent and agrees with the control, and the constrained result is the deliverable. Free small with constrained large means a good survey disagreeing with the control — a disturbed monument, a different realisation, or a distorted older network. Free large means the observations disagree with each other and no control will fix it. Free large with constrained small means the constraints are absorbing observation error. Conclusion Both small accept; deliver the constrained result Free small, constrained large good survey, control disagrees Free large investigate the survey first Free large, constrained small constraints absorbing error

Figure — four combinations of free and constrained residuals, four conclusions.

Both small. The survey is internally consistent and agrees with the published control. Nothing further to do, and the constrained result is the deliverable.

Free small, constrained large. The survey is good and disagrees with the control. Causes, in rough order of frequency: a disturbed or misidentified monument, control published in a different realisation or at a different epoch, or a genuine distortion in the older network. The pattern of the constrained residuals distinguishes them — one bad monument shows as an isolated outlier, a realisation mismatch as a uniform offset, and a network distortion as a smooth spatial trend.

Free large. The observations disagree with each other, and no choice of control will fix it. Investigate the survey before anything else; a constrained adjustment run on top of this only obscures which half of the problem is which.

Free large, constrained small. Unusual and worth understanding rather than celebrating. It normally means the constraints are absorbing observation error — too many fixed stations, or fixed stations that are themselves inconsistent — so the fit is being forced rather than achieved.

Reporting both sets, with a sentence naming which of these four cases applies, is a more complete account of a network than any single statistic.

Frequently Asked Questions

What order should the two adjustments run in?

Free first, then constrained. The free adjustment judges the survey: if its residuals and variance factor are unacceptable, no amount of good control will fix them, and constraining first would only obscure it. The constrained adjustment then places the accepted survey into the published frame, and its residuals say how well your network agrees with that control — a different and equally useful statement.

How do I know the datum defect is three and not seven?

From what the observations can see. GNSS vectors carry orientation and scale, so only translation is undetermined and the defect is three. A network of distances alone cannot see orientation, adding three; a network of angles alone cannot see scale, adding one. The eigenvalue count above measures it directly rather than relying on the classification, which is worth doing when the network mixes observation types.

Can I use a free adjustment as the deliverable?

Rarely, because a deliverable normally has to be in a published frame. Its role is diagnostic: it establishes the internal quality of the survey, and that statement belongs in the report alongside the constrained result, as validating datum alignment with control points describes.

What if the constrained adjustment fits much worse than the free one?

That is informative and common: it means your survey is internally consistent but disagrees with the published control. The causes are a disturbed monument, control published in a different realisation or epoch, or a genuine distortion in the older network. All three are investigable, and none is fixed by loosening the tolerance.