# TT-001 · Berth queueing — method

**Grade: OBSERVED.** Position, speed over ground, and our own receive clock, from our
own receivers. Nothing modelled, nothing crew-entered.

**Status: NOT PUBLISHABLE.** Two independent defects push the number toward zero
exactly when congestion is worst. Both are documented below. This file describes a
working instrument that is deliberately not yet reporting.

---

## What it measures

**Average Waiting Rate**, the UNCTAD/World Bank congestion indicator:

```
epsilon  =  SUM(hours waiting for berth)  /  SUM(hours alongside berth)
```

Kek Choo Chung, *Port Performance Indicators*, World Bank, Transport No. PS-6,
December 1993, **indicator 6 (5a/3)**, codifying UNCTAD's *Manual on a Uniform
System of Port Statistics and Performance Indicators* (1983). In queueing papers the
same quantity is ε = Tw/Ts.

Two things about the definition that are easy to get wrong, and we got both wrong first:

- **The denominator is time ALONGSIDE BERTH**, not total time in port.
- **It is an aggregate ratio, never an average or median of per-call ratios.** We
  first computed a median of per-call ratios. It read 0.00 at every gateway and
  discriminated nothing, and short berth stops made individual denominators tiny so
  ratios exploded — `STAR SIRIUS` led the table at 32.97 on 1.9 hours of "work". A
  3-hour service floor appeared necessary to suppress that. **Under the aggregate
  the floor is unnecessary**: a short berth simply contributes little to the
  denominator. The floor was a symptom of the wrong estimator, and removing it
  recovered 35 calls (124 → 159).

### Benchmarks

| | |
|---|---|
| accepted design range | **0.05 – 0.20** |
| PIANC guidance | ~0.165 |
| UNCTAD overutilisation flag | berth occupancy > 70% |

**A bare ε has no fixed meaning without berth count and occupancy, and we can observe
neither.** So ε is published as a comparison across gateways and against the design
range — never as an absolute verdict on a port.

---

## How anchoring is detected — physics, not declarations

AIS has a field that says "at anchor" (`NavigationalStatus` = 1). **We do not use
it.** It is crew-set, and the archive shows why: 3,273 `BERTH_START` events against
153 `ANCHOR_START`, because crews set *moored* far more reliably than *anchored*.

Instead: **a ship at anchor swings around its anchor** as wind and tide turn it,
tracing an arc a few hundred metres across. **A ship at a berth is held against a
quay by mooring lines and cannot move.** So the spread of positions while stationary
separates the two — and position is GPS off the transponder, which no human types.

Verified 2026-07-30 on 212 stationary runs. The two mechanisms agree independently,
which is this observatory's whole method:

```
tight  (spread <60 m, arc <25 deg)  123 stops  median  25 m,   2 deg
       -> 85% had the crew declaring MOORED, 0% at anchor
swing  (spread >=60 m, arc >=25 deg)  31 stops  median 290 m, 168 deg
       -> 74% had the crew declaring AT ANCHOR
```

### Thresholds, taken from the distributions rather than chosen

```
crew said MOORED     n=173   p10   5 m   p50  24 m   p90  68 m
crew said AT ANCHOR  n= 29   p10  39 m   p50 243 m   p90 376 m
```

- `spread < 40 m` → **berth** (below the anchored p10)
- `spread >= 120 m` → **anchor** (well above the moored p90)
- between → heading arc breaks the tie; where it cannot, the stop is **ambiguous**

The distributions overlap between 39 and 68 m. A single cut would force a false
binary, so ambiguous stops are labelled and excluded, and **the count is published**
so a reader can see how much is being set aside.

### Rules enforced in code

- **Movement must PERSIST for 15 minutes to end a stop.** Closing on a single fast
  fix shredded calls: a moored ship's speed reads 0.1, then 1.2, then 0.0 from noise,
  and `ZIM MOUNT BLANC`'s single Savannah call came out as six fragments of 0.6–1.5h.
  The tell was that berth duration did not scale with ship size — 320–400 m hulls
  showed a median 5.0h against 7.7h for 150–250 m hulls, which is backwards.
  **Fragmentation also converted anchor time into berth time**, because a short
  fragment of a swinging ship measures tight. Anchor stops rose 41 → 59 once fixed.
- **A gap is not a departure.** AIS drops out constantly. A gap beyond 120 minutes
  closes the run because we cannot claim the vessel stayed put — it does not assert
  that it left.
- **Anchor time counts as waiting ONLY if a berth follows it in the same visit.**
  Ships also anchor for bunkers, stores, crew changes, repairs, weather and awaiting
  orders, and none of that is queueing. Measured 2026-07-30: of 1,241 observed anchor
  hours, **only 92 h (7%) are verified as queueing** — the other 1,149 h had no
  subsequent berth at that gateway. Both figures are reported; only the verified hours
  enter the rate.
- **A vessel-gateway group is not one call.** 38 of 206 groups held more than one berth
  stop, so an anchor from one visit could be counted as queueing for a berth taken days
  later on another. Stops are split into **visits**, and the boundary is not elapsed
  time — observed anchor-to-berth transitions ran from 1.2 h to 37.8 h, so any time
  threshold would discard real waiting. The physical test is whether the ship **left**:
  seen beyond 40 nm from the berth centre between two stops, those stops belong to
  different visits.
- **Anchoring after the berth is never waiting.** It is waiting to sail, or shifting.
- **The merchant filter stays**: `ship_type 70–79 AND LOA >= 150 m`. Unfiltered,
  tugs made "berthings per day" read 745 against an honest 35.
- **Zero wait is published as zero** — a measurement, not a missing value.

---

## Why this is not publishable yet

### 1. The geofence cannot see an offshore queue

Our bounding boxes reach **5 to 17 nautical miles** from centre — Oakland 5 nm,
LA/Long Beach 8 nm, Houston 17 nm.

**In November 2021 LA/Long Beach required waiting ships to hold 150 nautical miles
offshore.** UMAS notes that a 25 nm AIS radius would have reported approximately zero
congestion at the peak of the worst US port crisis on record. For scale: LA's average
wait reached **8.7 days in September 2021**, with 86 ships anchored or loitering on
16 November 2021.

So if that event recurred, **this metric would not merely miss it — it would fall**,
because ships pushed offshore leave the box entirely and never register as anchored.

### 2. Right-censoring deletes the longest waits

Of 195 reconstructed calls, **35 anchored but had not berthed when the window closed,
holding 800 of 1,202 observed anchor hours — two thirds of all waiting.** Requiring a
completed call preferentially discards the most congested ships.

**The bias grows exactly when congestion is worst. In a crisis this metric sags
rather than spikes.** That is the worst possible property for a congestion index, and
on its own is sufficient reason for suspension.

Fixes, in order of preference: enough calendar time that the archive is long relative
to typical waits; or an estimator built for censoring (Kaplan–Meier) rather than
dropping incomplete cases.

### 3. Virtual arrival is invisible

Where arrival protocols are modern, a ship slow-steams and berths on arrival — **real
delay, zero anchor time.** ε then partly measures how modern a port's arrival protocol
is rather than how congested it is. Not fixable by widening boxes; only by measuring
speed reduction on the final approach leg.

### 4. The Ready Reserve Fleet is in the denominator

Found 2026-07-30 by building the live board. `CAPE DUCATO`, `CAPE ARUNDEL`, `CAPE
SABLE`, `CAPE STARR` and USNS hulls are US Maritime Administration Ready Reserve
Force vessels. They are AIS type 70–79 and over 150 m, so they pass the merchant
filter, and they sit alongside for months without working cargo.

**They hold 345 of 3,515 berth hours — 9.8% of the denominator.** Since ε is
wait ÷ berth, padding the denominator deflates ε by roughly a tenth, and more at
Norfolk where three of seventeen berthed hulls are Cape-class. Same class of defect as
the tugboats: a filter that admits the wrong fleet.

Fix: exclude by hull identity, not by name pattern alone — `CAPE ` would also match
legitimate commercial vessels. The vessel registry's `operator` field already names
the US Navy and MARAD on some of these.

### 5. Berth durations are clipped by the observation window

Every long stop reads 63–66 hours, which is the archive length, not a call length. A
genuine container call runs 12–48 hours. So these are **floors, not measurements** —
"at berth for at least 66 hours" — and they inflate S, which again deflates ε.

**Three independent mechanisms now push ε toward zero**: right-censoring deletes the
longest waits, window-clipping inflates berth time, and laid-up reserve hulls pad the
denominator. All three flatter the ports. That consistency is the strongest argument
for keeping the series suspended until the archive is long enough for the first two to
become second-order.

### 6. Liner schedules contain deliberate buffer

Waiting inside the buffer costs almost nothing; only waiting that breaks the schedule
costs money. **AIS cannot distinguish them.** Stated as a permanent limit.

---

## Reporting

Report the **triplet**, because no single figure is sufficient:

| | |
|---|---|
| **incidence** | share of calls that waited at all — a long-standing port KPI ("berth-on-arrival %"), and directly testable against Erlang C |
| **epsilon** | the aggregate waiting rate — the UNCTAD/World Bank indicator |
| **p90** | the tail, in **hours** not ratio — the bad experience |

**The median is deliberately absent** and the page must say so: it is 0 by
construction in a low-congestion regime and carries no information.

Print the **call count beside every figure.** About 15 calls per gateway is too thin
for a stable p90; pool to port-quarter or report incidence and ε only.

**Never publish a composite rank.** The World Bank's own rank aggregation placed
Beirut 11th on one method and 63rd on the other — same port, same data.

**Never publish ε as a bare league table.** LA/Long Beach moved *record volumes* in
2021; the queue was demand exceeding capacity, not port failure, and attributing loss
to the port is a category error. Publish volume beside ε so slow-because-busy is
visibly distinguishable from slow-because-inefficient.

---

## Scope limit to state proactively

**This measures waterside berth queueing only.** We cannot see crane moves, gate
transactions, yard density or labour. Bichou's standing critique of the World Bank
CPPI — that examining berth performance alone biases toward shipping-line interests
and ignores landside congestion — applies to this instrument too. Say it in the
method rather than waiting to be told.

---

## Baseline for "excess", not gross

UMAS/UCL (December 2024, AIS-derived) find ships spend **4–6% of operational time —
15–22 days a year — waiting at anchor before berthing, as the 2018–2022 norm.**
Container ships sit below that average, and waiting falls with ship size.

So the honest framing is **excess above a published baseline**, not gross waiting.

**Consequence that needs testing:** if waiting falls with ship size while service time
rises with it, ε falls *doubly* with size — so ε could differ between gateways because
of **fleet mix** rather than performance. `calls.gross_tonnage` is stored for exactly
this test. It is not yet runnable: only 3 gateways show any waiting at all.

---

## Data model

```
stops       one stationary period: kind, minutes, spread_m, heading_arc_deg, fixes
calls       one port call: wait_minutes, service_minutes, ws_ratio, excluded_because
build_runs  every rebuild, with the thresholds in force
```

Derived from the **raw archive** (`ais_position`), not by hooking the live detector.
The recorder archives; the analyser derives. A threshold change is therefore a re-run,
not a migration, and nothing is lost to a bug in a state machine. Idempotent: the same
archive produces the same rows.

```
node calls.js build     rebuild stops and calls from the archive, then report
node calls.js report    report without rebuilding
```

---

## Checks to register before anything publishes

| check | question |
|---|---|
| **coverage** | our monthly call count at LA against POLA's published vessel calls — the one gateway with a public denominator |
| **fleet mix** | does ε correlate with mean vessel size across gateways? If so it is measuring fleet composition |
| **incidence vs theory** | measured incidence against Erlang C at plausible occupancy. Ours implies 23–44% occupancy; US gateways run 50–70%. Part is genuine (berth windows make arrivals scheduled, not Poisson), part is the geofence gap |
| **censoring** | median wait of gap-closed against movement-closed calls |

### Threshold sensitivity — RUN 2026-07-30, passed

The 40 m / 120 m spread cut is our judgment, so it was tested across seven plausible
alternatives (berth-max 30–68 m against anchor-min 80–150 m) by reclassifying from the
persisted `spread_m` and recomputing ε:

| gateway | ε across all seven settings |
|---|---|
| savannah | **0.059 – 0.062** |
| la_longbeach | **0.028 – 0.034** |
| oakland | 0.000 – 0.038 — **unstable** |
| all others | 0.000 throughout |

**The threshold choice is not load-bearing** where the sample is adequate. Oakland
flips only because its whole figure rests on a single call whose swing fell between
120 and 150 m — a sample-size problem, not a threshold one. **Oakland's ε must not be
quoted.** Incidence is more sensitive (1.3%–3.9%) but stays in the same very-low
regime throughout.

The service-floor sensitivity check (1h/3h/6h) became unnecessary when ε moved to the
aggregate: the floor was removed entirely, so there is no longer a floor to vary.
