Sensors and bearings¶
How bearings from a platform's sensors come into a plot, how the map draws them, and how an analyst resolves a towed array's ambiguous bearings to one side. The ground TMA stands on (TMA solutions, CAP-21).
Requirements cited¶
| SRD | Requirement |
|---|---|
| CAP-22 | Import bearings and ambiguous (towed-array) bearings; the analyst resolves each ambiguous bearing to one side. Bearings are drawn as lines (product owner, 24 Oct 2026). Frequency is not imported. |
| CAP-21 | A TMA solution is rooted in a selected period of bearing data, its positions at the sensor's observation times in that period, with live bearing residuals while it is dragged. |
| CAP-28, CAP-29 | Sensor data is shown only within the plot's time window, which is the volume strategy for weeks of bearings. |
| CAP-33 | Later: a tool that proposes the side of ambiguous bearings from ownship's manoeuvres, for the analyst to confirm. Recorded, not built. |
| SUB-22, SUB-24 | Presentation is content: colour and hiding are steps. Every change is one recorded, undoable step with its parameters. |
| EXT-01, EXT-14 | REP files and legacy plot files are read as legacy Debrief read them, and what is not imported is listed. |
| NFR-04, NFR-06 | The drag budget (100 ms), and observation-heavy plots. |
| NFR-13 | Everything works from the keyboard, and by touch. |
Model¶
- A sensor belongs to its track. A sensor is a feature of its own (
kind: sensor): its name, the track that carries it (host_track_id, its host), its colour, whether it is hidden, and how legacy Debrief drew it (legacy_symbology). A towed array has an array offset too: how far astern of the ship it is (array_offset, a length) and how it follows the ship (array_mode:worm_in_hole, legacy Debrief's default, orplain); below. Each of its observations is a cut, a feature too (kind: sensor_cut): its sensor, its time, its bearing, and where the file gives them, its ambiguous bearing, which of the two was kept (resolved), its range (0 is a point, below), the position it was taken from (origin),;SENSOR3:'s bearing accuracy as written (bearing_accuracy, kept, not drawn), a label, a comment, a colour of its own, and whether it is hidden. Neither has a geometry: a cut is drawn from where its sensor was at its time (the host's position, or its array's astern of it), wherever the host is moved, split or merged; a cut with its ownoriginis drawn from there, and never moves with its host. A cut is its own feature, as a narrative entry is, so keeping a side of ten cuts is a step that stores ten small features, not a sensor of thousands. Schema 0.0.23. - Four ways in. As legacy Debrief read them, with its tests ported:
- REP and DSF files.
;SENSOR:(a bearing, a range),;SENSOR2:(a bearing, its ambiguous twin, a frequency, a range) and;SENSOR3:(a bearing, its accuracy, a frequency and its accuracy, a range) lines. A .dsf file is a REP file of sensor lines alone, read as legacy Debrief's ImportReplay read it: into a plot already open (Read sensor data into this plot (DSF, REP)…, the Outline's menu; Sensor data into the open plot under Import… in the playground), since it has no track of its own. A REP file imported as a plot brings its sensor lines in with its tracks. - Legacy plots (.dpf). Each track's sensors and their contacts: name, colour, visibility, every cut's time, bearing, ambiguous bearing (and whether legacy kept one side), range, label, comment, colour, visibility and line style. No longer listed as not imported.
- Rider's narrative (Word). A Rider entry's bearing (
273), with its ambiguous twin (92/112), becomes a cut of the sensor Rider bearings on the entry's platform's track, as legacy Debrief made it a sensor cut; the entry stays in the narrative, its bearing in its text. An arc (096-112) and No B are not cuts, and the record says so. - Agents.
debrief/import_sensor(below).
- REP and DSF files.
- Which track. A sensor line names its track. It is carried by the plot's track of that name, ignoring case, or else the first one whose name starts with the same six letters (legacy
BabelFish). A track matched by its first six letters only is offered first and asked to be confirmed (NELSON_GB matches NELSON by its first six letters only: which track carries NELSON_GB's sensors?), since a near name may be another ship;debrief/preview_sensorsaysmatched_by(nameorprefix). Where none matches, the analyst is asked which track carries it, or Do not read these (legacy Debrief asked, Sensor track not found); an agent says it inhosts. A sensor named UNKNOWN takes the file's name. A sensor of the same name already on the same track takes the new cuts (legacyTrackWrapper.add), so reading two files of one array makes one sensor. A file read into the plot before (by its content, under any name) is said first, and read again only if the analyst says so. - Read as legacy Debrief read them, quirks kept: a token starting with N (
NULL,NaN) is no value, so a line whose bearing isNULLgives no cut and is counted in the notes; a;SENSOR:range of 0 is no range, but a;SENSOR2:or;SENSOR3:range of 0 is a range of 0, drawn as a point where the cut was taken, as legacy Debrief drew it; a;SENSOR2:or;SENSOR3:line must have a label; a;SENSOR3:line has no ambiguous bearing, and from a sensor named UNKNOWN that gives a frequency, a bearing of 0 is none. A bearing outside 0 to 360° is read modulo 360 (-153.5is206.5). Ranges are yards. A sensor's colour is its symbology's second letter, legacy's colour code. Times are UTC, to the millisecond. - What is not imported, said in the step's record. Frequencies (every cut that gives one, and a .dpf sensor's base frequency): acoustic analysis stays in the acoustic replay application (CAP-22, EXT-03). Line styles: cuts are drawn solid, or dashed for a towed array's starboard bearing. An array centre from measured data (a .dpf's other array modes): its cuts are drawn from the track's position, and the notes say so. Cuts outside their host's fixes are kept but not drawn, as legacy Debrief drew none.
;SENSOR3:'s bearing accuracy is kept, as written, on each cut (bearing_accuracy), and not drawn (below). A .dpf sensor's array offset (<Offset Value="-200"/>, astern) comes in with its mode (Worm in holeorPlain; none said is legacy's default, worm in the hole), and the notes say so: its array 200 m astern (worm in the hole): its cuts are drawn, and their port and starboard told, from the array. - A legacy plot's ambiguous bearings, read right. Older legacy Debrief wrote
AmbiguousBearing="0.000"withHasAmbiguousBearing="false"on cuts that were never ambiguous: a placeholder, read as no ambiguous bearing and counted in the notes (of the 20,665 cuts in legacy Debrief's sample plots, 16,945 are such). A non-zero twin withHasAmbiguousBearing="false"is a cut legacy resolved: it comes in resolved, keeping the bearing legacy kept, and Make ambiguous again brings the other back (318 cuts in the samples; 922 still ambiguous, 2,480 with no twin). A .dpf track of overlapping legs (segments whose times overlap, so imported as a track per leg) gives each leg its own sensor of its cuts, each in the leg it falls in (else the nearest), so every cut is drawn from the leg it was taken on.
A towed array astern (decision, 24 Oct 2026)¶
A towed array is hundreds of metres behind the ship, and through a turn it is not where the ship was a moment ago along its heading: it is still on the ship's track before the turn. Legacy Debrief drew an array's cuts from the array, and so does Debrief v4 (product owner, 24 Oct 2026):
- A fixed offset per sensor, in metres unless a unit is given (300 is 300 m, whatever the display's length unit, as arrays are measured; 300 yd is yards): Towed array offset… on a sensor's menu in the Outline (or the Command Palette; in the playground, the sensor row's menu), titled Array offset: Towed array on BLUE ONE, then how it follows the ship. Past 5 km it warns: longer than any towed array, likely a unit mistyped. 0 is at the ship. One step, undone like any other: Towed array: 300 yd astern, worm in the hole. Agents:
debrief/set_array_offset. - Worm in the hole (legacy
WormInHoleOffset, its default): the array is that distance back along the ship's own past track, fix by fix, so it lags through a turn. Each leg is the geodesic between fixes, as the map draws it. Where the track before the cut is shorter than the offset, the rest is run astern of the first fix along its logged course, as legacy did. The track's along-track distance is worked out once and bisected, so a track of 200,000 fixes, or one that sat still for hours, costs a geodesic or two a cut (600 cuts on a 200,000-fix track still for three-quarters of it: 26 s before, 0.18 s in Python, 23 ms in the map). Plain (like legacy's Plain, not the same): straight astern of the ship along the course logged at the next fix (as port and starboard are told), where legacy'sgetBacktraceTotook a course blended between the fixes either side by time. On a steady course they agree; through a turn they differ: with a 500 m array, at 2,000 random times, the array is up to 56 m from legacy's onAmbig_tracks.rep(more than 25 m at 208 of them), and up to 981 m onlegs_20.rep, whose fixes log a turn of 90° or more at once. - Cuts are drawn from the array, and port and starboard told from it: the course is the one logged where the array is (below). A cut with its own
originis drawn from there. - Held to
schemas/vectors/sensor-cuts.jsonin both languages: legacyWormInHoleOffset's own test track (a 400 m array round two turns), an L-shaped track with a 600 m worm, its plain counterpart, and a dense turning track across the antimeridian.
Port and starboard: the logged course (decision, 24 Oct 2026)¶
Which side a bearing is on is told against the course logged at the first fix at or after the cut's time, as legacy isBearingToPort took it (getNearestTo, a fix at or after the time); where the track logs no course, the direction of travel between its fixes there. With an array offset, the course is the array's (logged where the array is, at the time the ship was there), a deliberate departure from legacy (product owner, 24 Oct 2026): legacy's isBearingToPort always took the ship's course at the cut's time. An array's ambiguous bearings are mirrored about the axis the array is actually lying on, which, through a turn, is the course the ship had where the array now is, not the ship's heading. It changes the side of few cuts: on legacy's Ambig_tracks.rep, 41 of its 721 cuts with a 500 m array, 183 with 2 km. So at a turn, a cut taken between the last fix on the old heading and the first on the new is told against the new heading, as legacy told it. legacy legs_20.rep's case is in the vectors: a track running north between fixes, its next fix logging 090, a bearing of 045 is port, as legacy said, where the line between the fixes would call it starboard.
On the map¶
- Lines from the host, in the time window. Every cut in the time window is drawn: from its host's position at its time, along the geodesic between fixes, exactly as the square at current time is placed (
pointAt), or from its array's position astern of it (above), or from its own position where the file gave one (wherever its host is); along the geodesic at its bearing, to its range, or else past the edge of the view (twice the view's larger side, at most five degrees of arc, legacy Debrief's length). None is drawn outside its host's span, unless it has its own position. A cut of range 0 is a dot where it was taken. Each is in its sensor's colour, or its own. A cut outside the window is not drawn at all: the window is the volume strategy (CAP-29). A hidden sensor, a hidden host, or a hidden cut is not drawn. - Two bearings until one is kept. An ambiguous cut is two lines, both in its sensor's colour: the one to port of the logged course solid, the one to starboard darker and dashed, so the sides are told apart without colour (legacy Debrief drew starboard darker). Which side a bearing is on is legacy
relBearing's: the bearing less the host's course there, within ±180°, port when below zero. A resolved cut is its kept line alone, solid. - The current cut. For each sensor, the cut current time is at (the last at or before it, in the window) is drawn wider, haloed in the map's colour, with a dot where it is drawn from. The key says what each kind of line is, once the plot has cuts.
- Pointing at a cut says its sensor and time, its bearings and the side each is on, which was kept, its range, label and comment, and, for an ambiguous one, Click for its choices; Alt-click this line to keep it. The side is the one worked out when the line was drawn, so pointing reads no fixes (it took 44 ms a frame on
ALPHA.repbefore). - A click selects (decision, 24 Oct 2026). A plain click on a cut's line, or a tap, selects the cut and shows its choices beside it: what it is (its label too), Keep 045.0° (port), Keep 135.0° (starboard), or Make ambiguous again once resolved, and More… for its menu. Tab moves between them, Enter chooses, Escape closes and returns to the map. A plain click never changes a cut: a click meant to select, or the first of a double-click to zoom, kept a side by accident before (panel finding H2).
Alt-click one of an ambiguous cut's two lines to keep that one at once, one step.Ctrl-click adds a cut to the selection, or takes it out. The second click of a double-click does nothing. Selected cuts are haloed as a selected track is, and with a selection elsewhere the rest are dimmed; selecting its sensor or its host selects all its cuts' lines. - Exported images draw the cuts as the map shows them, the current ones wider (SUB-25).
Resolving ambiguity¶
- One cut. Click a cut's line and choose Keep 045.0° (port) beside it, or
Alt-click the line to keep: one step, named Port bearing kept on Towed array cut at 09:21:00Z. Right-click it, or press and hold it on a touch screen, for its menu: Keep 045.0° (port), Keep 135.0° (starboard), Make ambiguous again once resolved, Keep a side for more cuts… and Select this cut. In the playground the menu is a short dialog of the same choices. From the keyboard, Cut at current time… on a sensor's menu in the Outline opens the menu of the cut the map emphasises, its bearings, sides and label said, so every cut can be read and kept without a pointer by moving current time to it. - A run, boxed in Bearings (product owner, 28 Oct 2026). The usual way, before any solution: select the sensor in the Outline, turn on Course/Speed in Bearings to see ownship's legs (BLUE ONE's course and speed in its colour) beside the bearings, then Select bearings and drag a box over the run of dots that is the contact. Each unresolved cut boxed keeps the side whose dot was boxed, one step for them all (Kept 27 Towed array cuts to starboard, 09:46:00Z–10:13:00Z); a cut boxed on both sides is left, and said. A box toggles (product owner, 28 Oct 2026): box a resolved run again, its kept dots, and it is made ambiguous again in one step, both faint dots back (Made 27 Towed array cuts ambiguous again, …), so a side kept wrongly is undone where it was seen; a box over both, one step saying how many of each (Kept 14 cuts to starboard and made 13 ambiguous again). A cut never ambiguous is untouched. The zoom stays, to box the next run. From the keyboard, the same step: the run selected in the Outline, Keep port, Keep starboard or Make ambiguous again in the Tools (TMA solutions).
- A period. Cuts selected in the Outline (a sensor expanded to its cuts, a run Shift-clicked): Keep port, Keep starboard or Make ambiguous again in the Tools, one step for them all (product owner, 27 Oct 2026). Or Ambiguous bearings: keep port or starboard… on a sensor's menu in the Outline (or the Command Palette): Keep port bearing, Keep starboard bearing or Make ambiguous again, then which cuts: The cut at the current time, Cuts in the time window, Selected cuts or All cuts, each saying how many cuts it holds and how many have two bearings. The playground has the same menus. A cut row's menu also has Keep port: selected cuts, Keep starboard: selected cuts and Make ambiguous again: selected cuts, in both. Port and starboard are offered for every cut, as legacy Debrief offered them: for each cut, the bearing on that side of the course logged where its sensor was at that cut's time is kept (above). A hidden cut, or a hidden sensor's, is left out of the window and all, and the notes say so (2 hidden cuts left as they are: show them to resolve them.); selected, it is resolved.
- One step, with what was asked. Every resolution is one recorded step, however many cuts: Port kept: 132 Towed array cuts 08:46:00Z–12:00:00Z; Ambiguous again: 3 Towed array cuts 09:00:00Z–09:02:00Z; cuts named of both sides, Mixed-side kept: 2 Towed array cuts … (a name too long for the History leaves out its period, said in its next note). Its record holds what was asked: the side, the sensors and the period, or how many cuts were named. Cuts with no ambiguous bearing, cuts already so, and cuts where the side cannot be told (outside the host's span, or where it never moved) are left as they are, and the step's notes say how many. A resolution that would change nothing changes nothing, and says why.
- Undo, and back to ambiguous. Undo takes a resolution back like any step. Make ambiguous again is its own step, for a cut or a period, when the analyst changes their mind after other work.
- What a cut keeps. A resolved cut records which bearing was kept (
bearingorambiguous_bearing), not the side. A side depends on the logged course at the cut's time; moving the host, or setting an array offset, later does not swap a resolved cut's bearing.
In the Outline and the Timeline¶
- The Outline. Each sensor is a row under its host track, with how many cuts it has, and how many are ambiguous and resolved: Towed array: 132 cuts · 40 ambiguous · 92 resolved. Its menu: keeping a side (above), Change colour…, Hide on map and Show on map, each one step (Colour of Bow sonar set to green). Cuts are not rows: a sensor may have thousands.
- Not in the Timeline (decision, 24 Oct 2026). The Timeline lists narrative entries, notes and periods to read in time order. Cuts would bury them: the sample's serial has 393 cuts against 46 narrative entries, a towed array gives one every 30 to 60 seconds, and a legacy DSF file gives every cut a label. The current cut is emphasised on the map instead, and the narrative already says when a contact was gained or lost. The cuts are in the Outline (product owner, 27 Oct 2026, superseding 24 Oct's cut list in the Bearings view): a sensor expands to its cuts, each its time, bearing (both, ≈, for an ambiguous pair not yet resolved, or the side kept), range and whether hidden; a run of them selected (Shift-click, Shift with the arrows) is kept to a side, hidden, shown, deleted or made a TMA solution of from the Tools, one step each (TMA solutions).
Tracks edited¶
- Splitting and merging. Splitting a track gives its sensors' later cuts to the new second track, as a sensor of the same name, and the step's note says so (Towed array: its 10 cuts from the split on are a sensor of BLUE ONE (2)'s). That sensor has a new id (
sensor-blue-one-2-towed-array), so a TMA solution rooted in the later cuts follows them by their cut ids, not the sensor's. Cuts the split would move keep where they were drawn from and the course their side was told by, pinned (their ownoriginandcourse, markedorigin_derived, so TMA can tell a derived origin from a recorded one), and the notes say how many: those between the first part's last fix and the second's first (outside both parts' spans), a towed array's whose worm reached back past the split, and those whose logged course was at a fix now in the other part. A pinned cut is drawn, and resolved, as before. Merging tracks moves their sensors to the merged track, and sensors of one name there are one again with every cut, as legacy Debrief joined them, so a track split and merged has its sensors as they were; the joined cuts keep the colour of the sensor they came from, and the sensor kept takes the other's legacy symbology if it had none. Two sensors of one name whose array offsets or modes differ are other arrays, as drawn: they are kept apart, each with its own cuts, and the notes say why (Towed array: kept as two sensors, as their arrays differ (at the ship; 300 m astern, worm in the hole), so each keeps its own cuts, drawn as before.). Moving, rotating, shearing or stretching a track moves the origin of every cut drawn from it; a cut with its ownoriginstays where it was recorded. - New data taken in (collation): a supplied item's sensors come with its tracks, carried by the plot's track each was matched to or added as (joined by name to a sensor already there), with only their cuts in the periods the item supplies, and the note says so: Towed array: 6 cuts taken with NELSON. Cuts left out because another item supplies that period are counted, not dropped silently: Towed array: 7 cuts 05:30:00Z to 06:00:00Z not taken: another item supplies NELSON then. A revision of an item that adds only cuts is new data too (4 new cuts), and choosing a period from another item in Coverage brings that item's cuts in the period with its fixes.
Agents¶
debrief/sensors(reading): the plot's sensors, each with its host, colour, whether it is hidden, how many cuts, ambiguous and resolved, and its first and last cut's time; withcuts: true, each cut inperiod(at most 20,000 in all) with its time, bearings, which was kept, the side its first bearing is on, its range, label, comment and where it is drawn from: what a TMA solution is rooted in.debrief/resolve_ambiguity(changing):keep(port,starboard,bearing,ambiguous_bearing, orambiguousfor both again) and eithercut_ids, orsensor_idsand perhaps aperiod(hidden cuts left out). Withkeepbearingorambiguous_bearing,other_cut_idsnames cuts kept on their other bearing in the same step, as a box in Bearings asks (product owner, 28 Oct 2026): a cut named in both was boxed on both sides and is left, said in the step's notes; hidden cuts are left;clear_cut_idsnames resolved cuts made ambiguous again in the same step (a box over their kept bearings). One step.debrief/set_array_offset(changing):sensor_ids,offset({value, unit}, or null for at the ship) andmode. One step.debrief/sensorsgives a sensor'sarray_offset_mandarray_mode, and each cut's origin from the array.debrief/preview_sensorsays what a file's sensor lines hold, and which track would carry each, changing nothing;debrief/import_sensorreads them in, one step, withhostsnaming the track for a track name the plot has not, andagainto read a file read before. Both are inschemas/vectors/service-methods.json, so an earlier revision shown read-only refuses the changing ones.
sequenceDiagram
actor Analyst
participant Map
participant Host as Host (VS Code or playground)
participant Python as debrief/resolve_ambiguity
Analyst->>Map: click a cut's line
Map-->>Analyst: the cut selected; beside it, Keep 045.0° (port), Keep 135.0° (starboard)
Analyst->>Map: Keep 135.0° (starboard), or Alt-click the starboard line
Map->>Host: resolve_asked {feature_id, keep: ambiguous_bearing}
Host->>Python: keep, cut_ids, base
Python-->>Host: one step: "Starboard bearing kept on Towed array cut at 09:21:00Z"
Host->>Map: the plot again
Map-->>Analyst: the kept line alone
Analyst->>Host: Towed array's menu: Keep port bearing, Cuts in the time window
Host->>Python: keep: port, sensor_ids, period (the window)
Python->>Python: each cut's side from the course logged where its array was
Python-->>Host: one step: "Port kept: 132 Towed array cuts 08:46:00Z–12:00:00Z"
Bearing error: drawn as lines (decision, 24 Oct 2026)¶
The SRD asked for a bearing error, where a file supplies one, drawn as a wedge. Of the formats legacy Debrief read:
;SENSOR:and;SENSOR2:carry no bearing error.;SENSOR3:carries a bearing accuracy in degrees (and a frequency accuracy). LegacyImportSensor3read both and threw them away, unused. Legacy Debrief's sample files giveNULLfor it, and no format or code says what it means: a standard deviation, a half-width, a 95% bound.- A .dpf plot's sensor contacts carry none.
So bearings are drawn as lines only, no wedges (product owner, 24 Oct 2026). ;SENSOR3:'s accuracy is kept on each cut as it was written (bearing_accuracy, as text, so 1.5 stays 1.5), not drawn and not interpreted, for the field to be agreed later. A wedge needs an error field agreed with the acoustic replay application's owners, its meaning included; the model can hold an optional error on a cut later without changing anything here.
Keyboard and touch¶
Everything on the map has a way without a pointer (keyboard equivalents): a sensor's menu in the Outline (Shift+F10 or the menu key; press and hold on a touch screen) keeps a side for the cut at current time, the window, the selected cuts or all, and Cut at current time… says the current cut's bearings and sides and opens its menu (a screen reader reads both, where the map's lines are a picture); Ambiguous bearings: keep port or starboard… is in the Command Palette; in the playground, Sensor's buttons under the Outline while a sensor or a cut is selected. A tap on a cut's line selects it and shows its choices, as a click does (a tap never keeps a side); pressed and held, its menu, as it is right-clicked. The choices beside a cut are a dialog: it has the keyboard first, so a screen reader reads the cut (its details are its description) before any choice and Enter keeps nothing by accident; Tab moves among the choices and stays among them, Enter chooses, and Escape goes back to the map. Towed array offset… is on the sensor's menu and in the Command Palette.
Performance budget¶
Painting is on one canvas, as the map's symbols and arrows are: a sensor of thousands of cuts is one image to pan, not thousands of elements. Each cut's lines are worked out once, along the geodesic, and kept while the host and the cut are unchanged; a line is cut into only as many pieces as keep it within a quarter of a pixel of the geodesic on screen. The current cuts are on a canvas of their own over the rest, so moving current time repaints one line per sensor.
Measured in headless Chromium with software rendering (the playground's map page; medians, with the 95th percentile where it matters):
| Plot | First draw (once) | Window moved, pan, zoom | Current time moved |
|---|---|---|---|
Legacy ALPHA.rep (200,000 fixes) and ALPHA_CUTS.dsf (611 cuts) |
46 ms | 0.7 ms (1.5 ms) | 0.1 ms |
| The sample's serial 1 (393 cuts, 132 ambiguous) | 0.9 ms | 0.2 ms | |
| 10,611 cuts in the window, half ambiguous | 330 ms | 13 ms (22 ms) | 0.1 ms |
| 50,611 cuts in the window | 1.4 s | 81 ms (134 ms) | 0.1 ms |
Before the lines were cut to fit and the current cuts given their own canvas, 10,611 cuts took 119 ms (194 ms) for every step of current time.
The budget: moving current time (playing, dragging the slider, and a TMA drag, which moves no cut) under 1 ms for any number of cuts; moving the window, panning or zooming within a frame (16 ms) for up to 10,000 cuts in the window and within NFR-04's 100 ms for up to 50,000; the first draw within half a second for each 10,000 cuts. Past 50,000 cuts in the window, narrow the window: that is what it is for (CAP-29). The playground's test holds the sample to it on every run.
In Python, resolving cuts reads only the host's fixes it needs (a track's times and positions are parsed as asked for), and a worm walks back through a dense track's short legs on the local ellipsoid (to a part in 10⁷), taking the geodesic only of the leg that holds the array; both languages do it alike. Resolving 600 cuts of a 500 m worm on ALPHA.rep (200,000 fixes, one a second), resolve() alone: 2.75 s before (every fix parsed, every leg a geodesic, 43,000 of them), 2.16 s with fixes read as asked for, 0.21 s now.
Known debt: a step's cost on a large plot is the plot's. The whole step on ALPHA.rep still takes 4 to 6 s, of which about 4 s is writing any step to a plot of 200,000 fixes: composing, validating, hashing and writing the whole plot (changing a sensor's colour there takes 4 s too; reading ALPHA_CUTS.dsf in, 6 s). That is the History's (one content-addressed plot per step), not the sensors', and is for the History to fix, by writing only the features a step changed.
History growth. A step stores each cut it changes whole: about 330 bytes per cut per step. Resolving all 611 cuts of ALPHA_CUTS.dsf adds some 200 kB to the History; resolving a towed array's cuts a window at a time adds as much in all.
Not yet¶
- Bearing-error wedges, until a field is agreed (above).
- The automatic ambiguity resolver (CAP-33): proposing each cut's side from ownship's manoeuvres (a true bearing steady across a turn is on the side whose relative bearing changed by the turn), for the analyst to confirm or change as one step. Recorded, not built.
- Array offsets that vary, or an array centre from measured data (legacy's other modes): an array is a fixed distance astern.
- Frequency, by decision (CAP-22).
Reference implementation¶
- Model:
SensorFeature,SensorCutFeature,AmbiguityResolutioninschemas/linkml/features.yaml; the rules indebrief_schemas.documents(a cut's sensor in the plot, a sensor's host track, bearings from 0 to under 360°, a resolution only of an ambiguous cut); the geometry indebrief_kernel.sensorsandts/kernel/src/sensors.ts, held toschemas/vectors/sensor-cuts.json(legacyrelBearing's own test cases, the default length, cuts across a turn, a turn logged at the next fix aslegs_20.rep, a stationary host, the antimeridian, high latitude, a range of 0, worm-in-the-hole and plain arrays). - Python:
debrief_io.rep(parse_sensor),debrief_io.sensor_import,debrief_io.dpfanddebrief_io.dpf_import,debrief_io.narrative(Rider bearings);debrief_stac.sensorsandCatalogue.add_sensors,Catalogue.resolve_ambiguity,Catalogue.set_array_offset;debrief_kernel.sensors.array_place(the worm);debrief/sensors,debrief/resolve_ambiguity,debrief/set_array_offset,debrief/preview_sensoranddebrief/import_sensorindebrief_tools.service; the sample's sensors indebrief_sample.synth(TowedArrayModel). - Hosts:
vscode/webview/sensormap.ts(the canvas, hit testing),vscode/webview/map.ts; the commandsdebrief.resolveAmbiguity,debrief.cutMenu,debrief.setArrayOffsetanddebrief.importSensorinvscode/src/extension.ts,vscode/src/sensorimport.ts, the Outline invscode/src/views/outline.ts; the playground's invscode/play/host.ts. - Tests:
python/debrief-kernel/tests/test_sensors.py,python/debrief-io/tests/test_rep_sensors.py,python/debrief-io/tests/test_dpf_sensors.py,python/debrief-stac/tests/test_sensors.py,python/debrief-tools/tests/test_sensors_service.py,ts/kernel/test/sensors.test.ts(the shared vectors),vscode/test/unit/sensors.test.ts,vscode/test/play/sensors.mjs, and a step ofvscode/test/integration/suite.ts.