diff --git a/CLAUDE.md b/CLAUDE.md index 6f649cf..e313c1b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -266,8 +266,8 @@ legitimate height is 14,630 m, so no bound separates them — `fly_flying_height backstop for digital frames (whose `scale` is not an image scale; 94% would be refused on it) and catches nothing in today's catalogue. The rule is **per frame, never per roll**: 1,208 clean frames share a slipped roll. The band's **upper edge sits in a trough** before a mass at r = 2 -that is a 305 mm lens catalogued as 153, which the DEM route drew at twice its width and nominal -gets right — the lower edge is the same factor by symmetry, not a trough. And the slip's +— half of it a 305 mm lens catalogued as 153, which the DEM route drew at twice its width and +nominal gets right, and half a wrong `scale`, which fly#72 tables — the lower edge is the same factor by symmetry, not a trough. And the slip's apparent mirror image is **deliberately not repaired**: x10.764, x10 and x2 each bring ~730-800 of the lower tail into the band and the terrain cannot choose (fly#60). Do not "finish" that by picking one, and do not re-derive the band without the sweep. Classification happens after the @@ -330,6 +330,17 @@ outcome today. ×10.764 is named so a neighbour confirming #54 *can* contradict one naming ×10 — without it the tolerance guarded nothing. Blind to a roll whose every frame carries the defect, which is most of the upper tail +- **The r ≈ 2 mass is two defects that `r` cannot separate, and neither is applied unwitnessed** +(v0.18.0, #72) — a 305 mm lens catalogued as 153 and a right height beside a `scale` at half its +denominator both put a frame at r = 2. Settled per roll-height over the 252 sampled frames beyond +the band: 24 roll-heights (120 frames; keys reaching at most 3,227 in the catalogue — terrain lowers r, so +some are already in band) table at factor 1 under +`tail = "near_upper"`; 21 carry a logbook writing a 12" lens and stay on nominal. **The logbook +height is not a discriminator here** — both readings predict the crew flew the catalogued height — +so the near_upper rule also requires spacing to **reject** nominal, fixed before the run. Logbook +`focal_mm` on these pages is filled only where a focal length is written, never inferred from a +camera model. A sample, not a census; read `inst/notes/terrain-correction.md` + ## Gotchas - `.lintr` must be single-line DCF format — multi-line breaks newer lintr versions diff --git a/NEWS.md b/NEWS.md index c93595d..61a311b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # fly (development version) +- **Half the r ≈ 2 mass was a wrong scale, not a mislabelled lens, and it is now drawn at its true width** ([#72](https://github.com/NewGraphEnvironment/fly/issues/72)). v0.12.0 said the film frames around twice their nominal scale were a 305 mm lens catalogued as 153, so falling back to nominal scale was right. The flight logbooks and the spacing between adjacent frames, read per roll-height over all 252 sampled frames beyond the band, split it: **24 roll-heights (120 sampled frames; their keys reach at most 3,227 catalogue frames, fewer once terrain brings some inside the band, where they were already sized from their height) carry the height the crew flew beside a `scale` recorded too small** — mostly 1972–76 rolls at half the true denominator — and ship in `flying_height_rolls.csv` at factor 1, `scale_wrong`, under a new `tail` value, `"near_upper"`. The fallback drew them at 1/r of their width, half at r = 2; they are now `"corrected_roll_table"` and sized from their height. On 21 roll-heights (82 frames) the logbook writes a 12" lens, which confirms the lens reading there, and those stay on nominal scale, as do the rest; each is listed in `flying_height_rolls_excluded.csv` with its reason. +- **The logbook height alone cannot tell the two defects apart** — a wrong lens and a wrong scale both mean the crew flew the catalogued height — so for this tail the rule, fixed before the run, also requires the spacing to *reject* nominal scale, not merely fit the reported height. The near_upper set is a 600-frame sample, not a census; `inst/notes/terrain-correction.md` states what it does not reach. +- `data-raw/flying_height_logbooks.csv` gains 185 rows from 142 newly read page files covering 54 roll labels. The readers worked blind; two things were added at merge and say so in their `note`: the 18 heights on 1984–86 sheets that write whole feet rather than thousands, and two rows marking the interior of a strip whose height changes along it as covered but unread. On these pages `focal_mm` is filled only where a focal length is written, never inferred from the camera model. + ## 0.17.0 (2026-09-27) - **A frame beside a disputed roll-height on the same roll is now a third witness, and it settles five roll-heights no logbook does** ([#74](https://github.com/NewGraphEnvironment/fly/issues/74)). The rule was fixed before it was run. A neighbour one frame away, on the same roll, lens and scale, must itself sit in the band, and the catalogue's height must stand in an **exact** named relation to it: ×10, ÷10, ÷100, ×10.764 or one leading digit. Exact means within what storing whole metres of a 0.3048 m/ft conversion can move a figure, and the catalogue both rounds and truncates. A figure converted at 3.28 ft/m can only be refused by it, never wrongly accepted. Every neighbour naming a relation must name the same one, and spacing must agree. A logbook vetoes it only by naming a different factor. The rule was run over all 521 roll-heights the logbook leaves excluded, in both tails. `bc5596` 204–211 (26,212 m, ten times frame 203) and `bcb98013` frame 52 (97,924 m, a leading 9 on its neighbours' 7,924) leave #54's ÷10.764, which drew them 10.3–11.2% narrow and 17.1% wide in linear size. Three lower-tail roll-heights beyond the issue come out of `"implausible"`: `bc7675` 609 → 6,096 m, `bc87070` 396 → 3,962 m and `bcc822` 701 → 7,010 m. The logbook corroborates each in rows its own rule could not accept diff --git a/R/fly_footprint.R b/R/fly_footprint.R index 9448898..5baf136 100644 --- a/R/fly_footprint.R +++ b/R/fly_footprint.R @@ -220,13 +220,17 @@ fly_height_slip_factor <- function() 3.28084^2 # whose reported height is at most twice what their scale implies sit inside this band # (98.7% of all 1.44 million, weighting in the strata above that), and the 1,589 slipped # frames land at 0.80-1.32 once repaired against 10.0-15.8 before. The UPPER edge sits in -# the trough between that mass and the next one out, centred on 2.0, which is a 305 mm lens -# catalogued as 153 — a frame the DEM route would draw at twice its true width, and the -# nominal route gets right because it never reads `focal_length`. For about half of that -# mass: fly#60's frame spacing shows 105 of its 209 sampled frames carry the right height -# and a wrong scale, which the nominal route draws at half width (fly#72). The lower edge is that -# factor inverted, because the error is a ratio either way; it is set by symmetry and not -# by a trough, since ordinary frames simply thin out below 0.8 with no second mass at 0.5. +# the trough between that mass and the next one out, centred on 2.0. That mass is two +# defects that read alike here (fly#72). On some rolls it is a 305 mm lens catalogued as +# 153, which the DEM route would draw at twice its true width and the nominal route gets +# right because it never reads `focal_length`: the logbooks write a 12" lens on 21 sampled +# roll-heights. On others the height and lens are right and `scale` is recorded at half its +# denominator, which the nominal route draws at half width: those roll-heights, settled by +# the logbooks and frame spacing, are carried in `flying_height_rolls.csv` at factor 1. +# Neither reading is safe to apply unwitnessed, so the band stays where it is and the +# table decides per roll. The lower edge is that factor inverted, because the error is a +# ratio either way; it is set by symmetry and not by a trough, since ordinary frames +# simply thin out below 0.8 with no second mass at 0.5. # See `inst/notes/terrain-correction.md`. fly_height_ratio_band <- function() c(1 / 1.6, 1.6) @@ -252,7 +256,9 @@ fly_flying_height_max <- function() 16000 # logbook "20.0", thousands of feet, catalogued as 2,000 ft), 1 where the logbook confirms # the height and the SCALE is the wrong field, and 0.1 where the height is ten times too # LARGE. That last case is fly#71: rolls flown before 2003 that #54 divides by 10.764. `tail` records -# which side of the band the roll-height came from. `height_m` is the height used — the logbook's, +# which side of the band the roll-height came from: `lower`, `upper` (#54's slipped frames), or +# `near_upper` (fly#72) — the r ~ 2 mass, where a factor of 1 is the same `scale_wrong` cause +# reached from above the band, and the only factor named there. `height_m` is the height used — the logbook's, # converted, since the catalogue's rounding survives multiplying by the factor. # # fly#74 added a third witness where no logbook settles a roll-height: a frame adjacent by @@ -264,9 +270,10 @@ fly_flying_height_max <- function() 16000 # ratio it implies rather than a named slip; only `factor != 1` is read from it here. # # Keyed on roll, height, lens and scale together, so the table reaches only the frames it -# was measured on. Every lower-tail roll-height it does not correct, and every #54-slipped one -# it does not reach, is listed with its reason in `flying_height_rolls_excluded.csv`: an -# unlisted roll is unmeasured, not clean. +# was measured on. Every lower-tail roll-height it does not correct, every #54-slipped one +# it does not reach, and every sampled near_upper one it leaves on nominal scale, is listed +# with its reason in `flying_height_rolls_excluded.csv`: an unlisted roll is unmeasured, not +# clean. The near_upper rows come from a 600-frame sample of that stratum, not a census. # Produced by `data-raw/height_calibrate-lower_tail_rolls.R`; see # `inst/notes/terrain-correction.md`. fly_height_roll_table <- function() { @@ -714,16 +721,20 @@ fly_is_square <- function(footprints) { #' the roll-heights in `inst/extdata/flying_height_rolls.csv`, which the #' province's flight logbooks, or failing them an adjacent frame on the same #' roll in an exact named relation, together with the spacing between adjacent -#' frames, settled (fly#60, fly#71, fly#74): a height with one or two digits dropped (a logbook "20.0", +#' frames, settled (fly#60, fly#71, fly#72, fly#74): a height with one or two digits dropped (a logbook "20.0", #' thousands of feet, catalogued as 2,000 ft), a height recorded ten times too -#' large, a leading digit added, or a correct height beside a wrong `scale`. Checked before the +#' large, a leading digit added, or a correct height beside a wrong `scale` — +#' on either side of the band, including frames around twice their nominal +#' scale whose `scale` is recorded at half its denominator. Checked before the #' 10.76 slip above. Sized from the measured height; matched on `film_roll`, #' `flying_height`, `focal_length` and `scale` together, so it needs a #' `film_roll` column. As with the slip, `flying_height` is not overwritten} #' \item{`"implausible"`}{they disagree some other way, or `flying_height` is #' above 16,000 m or below the terrain. Nothing can say whether the height, the #' scale or the focal length is the wrong one — a 305 mm lens catalogued as -#' 153 looks the same from here — so the height is not used: a film frame +#' 153 and a scale recorded at half its denominator look the same from here, +#' and the roll table separates them only where it was measured — so the +#' height is not used: a film frame #' falls back to nominal scale, and a digital frame with no other route has no #' footprint} #' \item{`NA`}{no height was judged: no `dem`, a frame the DEM route does not @@ -735,7 +746,8 @@ fly_is_square <- function(footprints) { #' #' Measured over the whole catalogue, the two corrections for a height that is too #' large apply to those 1,589 frames and to nothing else: 308 by the roll table, -#' the rest by 10.76. A digital frame's `scale` is a nominal figure, not +#' the rest by 10.76. (The table's factor-1 rows correct a scale, not a height, so +#' they are not among them.) A digital frame's `scale` is a nominal figure, not #' its image scale, so it is not compared: only the 16,000 m ceiling, or terrain at #' or above the aircraft, can refuse one. To list the frames worth a second look: #' `dplyr::filter(fp, height_source != "reported")`. diff --git a/data-raw/flying_height_logbooks.csv b/data-raw/flying_height_logbooks.csv index 99de287..c043875 100644 --- a/data-raw/flying_height_logbooks.csv +++ b/data-raw/flying_height_logbooks.csv @@ -213,3 +213,188 @@ "bcc00085__bcc00085_1.jpg",FALSE,"bcc00085","30BCC00085","203-218",203,218,"18500","True Height","18500","CFL 304.483","305","1: 15000","clear","line 3 dir 090" "bcc00085__bcc00085_1.jpg",FALSE,"bcc00085","30BCC00085","219-229",219,229,"19000","True Height","19000","CFL 304.483","305","1: 15000","clear","line 2 dir 270" "bcc00085__bcc00085_1.jpg",FALSE,"bcc00085","30BCC00085","230-236",230,236,"19000","True Height","19000","CFL 304.483","305","1: 15000","clear","line 1 dir 090" +"bc7223__bc7223_1.jpg",FALSE,"bc7223","Delta 56 / B.C. 7223","1-52",1,52,"3.0","TRUE HEIGHT (M'/M.S.L.)","3000","ZEISS camera no. 2, 12""","305","","clear","Sheet 3 of 3; project PWA Accident Scene Campbell River OP 119; field roll Delta 56; strip 1. Remarks: difficult to maintain levels, altimeter not set for project altitude" +"bc7223__bc7223_1.jpg",FALSE,"bc7223","Delta 56 / B.C. 7223","53",53,53,"4.1","TRUE HEIGHT (M'/M.S.L.)","4100","ZEISS camera no. 2, 12""","305","","clear","Sheet 3 of 3; strip 2 (final 53-108) START row reads 4.1, END row (108) reads 3.2; heights differ within the strip so emitted as two single-frame rows; altimeter not set for project altitude" +"bc7223__bc7223_1.jpg",FALSE,"bc7223","Delta 56 / B.C. 7223","108",108,108,"3.2","TRUE HEIGHT (M'/M.S.L.)","3200","ZEISS camera no. 2, 12""","305","","clear","Sheet 3 of 3; strip 2 END row; start row (53) reads 4.1; frames 54-107 between carry no written height" +"bc7223__bc7223_1.jpg",FALSE,"bc7223","Delta 56 / B.C. 7223","109-119",109,119,"2.2","TRUE HEIGHT (M'/M.S.L.)","2200","ZEISS camera no. 2, 12""","305","","clear","Sheet 3 of 3; strips 3 (109-114) and 4 (115-119) both 2.2" +"bc5138__bc5138_1.jpg",FALSE,"bc5138","DELTA 11 / B.C. 5138","1-31",1,31,"18.0","TRUE HEIGHT (M'/M.S.L.)","18000","RC8, camera no. 355, 6""","153","","clear","Sheet 1 of 2; project Quatsino OP 35; strip 5" +"bc5138__bc5138_2.jpg",FALSE,"bc5138","DELTA 11 / B.C. 5138","32-148",32,148,"18.0","TRUE HEIGHT (M'/M.S.L.)","18000","RC8, camera no. 355, 6""","153","","clear","Sheet 2 of 2; project Port Renfrew-Barkley Sound refly OP 71; strips 1-5 plus refly strip 3 (143-148); overlap changed to 70% from 60% at field frame 48" +"bc79058__bc79058_3.jpg",FALSE,"bc79058","B.C. 79058","112-257",112,257,"26.5","True Height (M'/M.S.L.)","26500","camera 124223, f 153 mm","153","","partial","Sheet 3 of 3 (sheet digits overwritten); Moyie-Waterton Lakes Op 120/79; field roll Nov 47; height written once on strip 3 and ditto'd through strips 2, 1 and 9 (refly); first final number overwritten, read as 112 (field 291-? end 152); frames 249-250 clearing; 397-400 cleared/punched" +"bc79029__bc79029_3.jpg",FALSE,"bc79029","B.C. 79029","9-61",9,61,"21.5","True Height (M'/M.S.L.)","21500","camera 124223, f 153 mm","153","","clear","Sheet 3 of 3; Sooke Lake (Greater Vic Watershed) Op 186/79; field roll Nov 27; strips 4,3,2,1 ditto'd; cleared 3 frames before and 4 after" +"bc79043__bc79043_3.jpg",FALSE,"bc79043","B.C. 79-043","140-193",140,193,"5.5","True Height (M'/M.S.L.)","5500","camera 110399, f 305 mm","305","","clear","Sheet 3 of 3 (overwritten); Ganges Op 39/79; field roll Nov 36; strips 4 and 5 ditto'd" +"bc79026__bc79026_3.jpg",FALSE,"bc79026","B.C. 79-026","170-233",170,233,"12.5","True Height (M'/M.S.L.)","12500","camera 21162, f 153 mm","153","","clear","Sheet 3 of 4 (sheet numbers overwritten); Similkameen River Refly Op 46/79; field roll Oscar 5; strips 4,3,2,1 ditto'd; 65-67 cleared" +"bc79026__bc79026_4.jpg",FALSE,"bc79026","B.C. 79026","234-244",234,244,"14.0","True Height (M'/M.S.L.)","14000","camera 21162, f 153 mm","153","","clear","Sheet 4 of 4 (overwritten); Shatford Creek Op 50/79; field roll Oscar 5; strip 1" +"bc79141__bc79141_3.jpg",FALSE,"bc79141","B.C. 79141","160-187",160,187,"28.0","True Height (M'/M.S.L.)","28000","camera 124227, f 153 mm","153","","clear","Sheet 3 of 5; Morley Lk - Swift River Op 55/79; field roll Nov 27; strips 1 and 2 ditto'd" +"bc79039__bc79039_7.jpg",FALSE,"bc79039","B.C. 79 039","169-179",169,179,"25.5","True Height (M'/M.S.L.)","25500","camera 124223, f 153 mm","153","","clear","Sheet 7 of 7; Dome Rock - Terrace Cr. Op 124/79; field roll Nov 29; strip 1; field 113-114 cleared" +"bc79058__bc79058_2.jpg",FALSE,"bc79058","B.C. 79058","42-111",42,111,"25.0","True Height (M'/M.S.L.)","25000","camera 110399, f 305 mm","305","","clear","Sheet 2 of 3 (overwritten); Three Valley Op 129/79; field roll Nov 46; strips 5,3,1,4,2 ditto'd; last two final numbers (98, 111) partly overwritten; clear 3 frames, punched 1" +"bc79058__bc79058_1.jpg",FALSE,"bc79058","B.C. 79058","1-41",1,41,"24.5","True Height (M'/M.S.L.)","24500","camera 110399, f 305 mm","305","","clear","Sheet 1 of 3 (overwritten); Bews Creek Op 130/79; field roll Nov 46; strips 1,3,2 ditto'd; cleared 4 frames" +"bc79026__bc79026_1.jpg",FALSE,"bc79026","B.C. 79-026","1-69",1,69,"25.0","True Height (M'/M.S.L.)","25000","camera 122520, f 305 mm","305","","clear","Sheet 1 of 4 (sheet numbers overwritten); Nicola-Barton Op 144/79; field roll Oscar 4; strips 7 and 8" +"bc79026__bc79026_1.jpg",FALSE,"bc79026","B.C. 79-026","70-140",70,140,"24.5","True Height (M'/M.S.L.)","24500","camera 122520, f 305 mm","305","","clear","Sheet 1 of 4; new height 24.5 written at final 70 (strip 9 start) and again at 87 (strip 9 end), ditto'd through strips 10 and 11 to 140; clear 3 frames (432-434), 435-436 clearing" +"bc79072__bc79072_1.jpg",FALSE,"bc79072","B.C. 79-072","1-184",1,184,"26.0","True Height (M'/M.S.L.)","26000","camera 110398, f 305 mm","305","","partial","Sheet 1 of 2; Kingcome P.S.Y.U. Op 155/79; field roll Oscar 35; strips 39,38,37,44,45 ditto'd; last row pair after 184 (strip 46, OFF) crossed out and marked DO NOT USE, skipped; splice noted; exposure varies for terrain" +"bc79043__bc79043_2.jpg",FALSE,"bc79043","B.C. 79-043","82-139",82,139,"24.0","True Height (M'/M.S.L.)","24000","camera 124223, f 153 mm","153","","clear","Sheet 2 of 3; Bonapart Hihium Op 156/79; field roll Nov 37; strips 1-4 ditto'd; camera u/s at 139" +"bc79127__bc79127_1.jpg",FALSE,"bc79127","79-127 (B.C. 79126 struck out)","1-135",1,135,"19.34","True Height (M'/M.S.L.)","19340","ZE 122520, f 305 mm","305","","clear","Sheet 1 of 2 (overwritten); Chilako-Cluculz Op 165/79; field roll Oscar 57; final roll written 79126 then struck and 79-127 written below; strips 1-4 ditto'd; height written with two decimals (19.34) - read under thousands convention as 19,340 ft, unusual precision; 404-407 cleared" +"bc79131__bc79131_2.jpg",FALSE,"bc79131","B.C. 79/131","129-250",129,250,"18.34","True Height (M'/M.S.L.)","18340","camera 122520, f 305 mm","305","","clear","Sheet 2 of 3 (overwritten); Fort Nelson P.S.Y.U. Op 174/79; field roll Oscar 55; strips 31 and 30 ditto'd; height written with two decimals (18.34) read as 18,340 ft, unusual precision; remark: note change in focal length used on this magazine, make necessary adjustment in annotation of roll; clear 6 frames middle roll 139-144 (field)" +"bc79072__bc79072_2.jpg",FALSE,"bc79072","B.C. 79-072","185-284",185,284,"5.3","True Height (M'/M.S.L.)","5300","camera 21162, f 153 mm (153 underlined)","153","","partial","Sheet 2 of 2; Puntledge-Tsolum River #1 Op 181/79; field roll Oscar 31; height 5.3 written on first strip 9 (field 1-35) which is marked DO NOT USE (no final numbers) - skipped, height ditto'd to refly strip 9 and strips 6, 5; last final number overwritten, 284 written below" +"bc79029__bc79029_2.jpg",FALSE,"bc79029","B.C. 79029","1-8",1,8,"14.0","True Height (M'/M.S.L.)","14000","camera 124223, f 153 mm","153","","partial","Sheet 2 of 3; Mt. Arrowsmith Op 316/79; field roll Nov 27; strip 1; start final number struck out with 1 written above (field 5-12 -> final 1-8); field 13-24 cleared" +"bc79039__bc79039_5.jpg",FALSE,"bc79039","B.C. 79039","97-129",97,129,"2.60","True Height (M'/M.S.L.)","2600","ZE. 124223, f 153 mm","153","","clear","Sheet 5 of 7 (overwritten); Lynnmour-Maplewood Op 253/79; field roll Nov. 29; strips 1-3 ditto'd" +"bc79039__bc79039_6.jpg",FALSE,"bc79039","B.C. 79039","130-151",130,151,"1.05","True Height (M'/M.S.L.)","1050","ZE. 124223, f 153 mm","153","","clear","Sheet 6 of 7 (overwritten); Scott Road Op 254/79; field roll Nov. 29; strip 1" +"bc79039__bc79039_6.jpg",FALSE,"bc79039","B.C. 79039","152-168",152,168,"2.55","True Height (M'/M.S.L.)","2550","ZE. 124223, f 153 mm","153","","clear","Sheet 6 of 7; new height at 152 ditto'd through strip 2 to 168; strip no. for 152-162 ditto'd from strip 1 (dir 360 struck, 180); field 99-101 cleared" +"bc79039__bc79039_2.jpg",FALSE,"bc79039","B.C. 79 039","7-23",7,23,"6.0","True Height (M'/M.S.L.)","6000","camera 124223, f 153 mm","153","","clear","Sheet 2 of 7 (overwritten); Johnson Creek Op 256/79; field roll Nov 29; strip 1; clear 3 frames (10,11,12)" +"bc79039__bc79039_2.jpg",FALSE,"bc79039","B.C. 79 039","24-44",24,44,"7.0","True Height (M'/M.S.L.)","7000","camera 124223, f 153 mm","153","","clear","Sheet 2 of 7; Christina Lake Op 258/79; strips 1 and 2 ditto'd; clear 3 (51,52,53)" +"bc79026__bc79026_2.jpg",FALSE,"bc79026","B.C. 79 026","141-169",141,169,"22.0","True Height (M'/M.S.L.)","22000","camera 122520, f 305 mm","305","","clear","Sheet 2 of 4 (overwritten); Slocan Lake Op 307/79; field roll Oscar 4; strips 1,2,4 ditto'd; 437-438 and 468-470 clearing frames; spliced; no clock/date instrument in camera" +"bc79029__bc79029_1.jpg",FALSE,"bc79029","B.C. 79-029","",,,"","True Height (M'/M.S.L.)","","camera 124223, f 153 mm","153","","partial","Sheet 1 of 3; Op 186/79 (project name faint); field roll Nov 27; only frame rows are strip 1 ST/OFF marked DO NOT USE (reflown June 1/79), no height written, final column only dashes; 1-3 and 12-15 cleared" +"bc79039__bc79039_3.jpg",FALSE,"bc79039","B.C. 79039","45-70",45,70,"11.0","True Height (M'/M.S.L.)","11000","camera 124223, f 153 mm","153","","clear","Sheet 3 of 7 (overwritten); Naka Creek / Adams River Op 325/79; field roll Nov 29; strip 1; cleared 4 frames before, 27-29 cleared after" +"bc79039__bc79039_4.jpg",FALSE,"bc79039","B.C. 79039","71-96",71,96,"6.50","True Height (M'/M.S.L.)","6500","ZE. 124223, f 153 mm","153","","clear","Sheet 4 of 7 (overwritten); Durieu - Allan Lake Op 327/79; field roll Nov. 29; strips 1 (80% fwd overlap) and 2 (60% fwd overlap) ditto'd; last final number 96 slightly malformed (field 18-26 -> 88-96)" +"bc79141__bc79141_1.jpg",FALSE,"bc79141","B.C. 79-141","1-12",1,12,"6.0","True Height (M'/M.S.L.)","6000","camera 124223, f 153 mm","153","","clear","Sheet 1 of 5 (overwritten); Hope China Bar #1 Op 328/79; field roll Nov 27; height 6.0 written on first strip 4 row pair, which is marked DO NOT USE (no final numbers) - skipped; height ditto'd to reflown strip 4 (final 1-12); clear 2 frames 296-297 and 320-321" +"bc79131__bc79131_1.jpg",FALSE,"bc79131","B.C. 79-131","1-8",1,8,"6.6","True Height (M'/M.S.L.)","6600","camera 21162, f 153 mm","153","","clear","Sheet 1 of 3; Beatton River #1 Op 349/79; field roll Oscar 55; strip 2" +"bc79131__bc79131_1.jpg",FALSE,"bc79131","B.C. 79-131","9-54",9,54,"6.7","True Height (M'/M.S.L.)","6700","camera 21162, f 153 mm","153","","clear","Sheet 1 of 3; new height at 9 ditto'd through strips 3, 4, 5" +"bc79131__bc79131_1.jpg",FALSE,"bc79131","B.C. 79-131","55-118",55,118,"6.9","True Height (M'/M.S.L.)","6900","camera 21162, f 153 mm","153","","partial","Sheet 1 of 3; new height at 55 (written loosely, read 6.9) ditto'd through strips 7, 6, 8, 9; clear 3 frames (119,120,121)" +"bc79195__bc79195_1.jpg",FALSE,"bc79195","B.C. 79-195","1-199",1,199,"23.0","True Height (M'/M.S.L.)","23000","camera 110799, f 305 mm","305","","clear","Sheet 1 of 3 (overwritten); Vancouver P.S.Y.U. Op 368/79; field roll Nov. 95; strips 14-19 ditto'd; camera number read as 110799 (uncertain)" +"bc79195__bc79195_2.jpg",FALSE,"bc79195","B.C. 79-195","200-208",200,208,"10.25","True Height (M'/M.S.L.)","10250","camera 124223, f 153 mm","153","","clear","Sheet 2 of 3 (overwritten); Garibaldi Op 372/79; field roll Nov. 95; strip 1; remark: 6"" job on end of 12"" roll, check annotation numbers" +"bc79195__bc79195_3.jpg",FALSE,"bc79195","B.C. 79-195","209-264",209,264,"6.X5","True Height (M'/M.S.L.)","","camera 124223, f 153 mm","153","","partial","Sheet 3 of 3 (overwritten); Cheakamus River Op 392/79; field roll Nov. 95; strips 4,3,2,1 ditto'd; height reads 6.?5 with the middle digit struck through/overwritten by an X - cannot tell which digit (6.25? 6.75?), so ft left empty; clear 3 frames after 255 and after 264" +"bc79141__bc79141_2.jpg",FALSE,"bc79141","B.C. 79141","13-159",13,159,"27.0","True Height (M'/M.S.L.)","27000","camera 124223, f 153 mm","153","","clear","Sheet 2 of 5 (overwritten); Dease Lk. Highway Op 13/79 (56 struck); field roll Nov. 27; strips 12,13,14,1,2,2(on),3,4,5,6,7 ditto'd; cleared 4 frames before, clear 2 after" +"bc79141__bc79141_4.jpg",FALSE,"bc79141","B.C. 79141","188-?",188,,"28.0","True Height (M'/M.S.L.)","28000","camera 124223, f 153 mm","153","","partial","Sheet 4 of 5 (sheet digits smudged); Atlin Lk. Op 54/79; field roll Nov. 27; strips 1 and 2 ditto'd; final numbers after 188 smudged (strip 1 end looks like 21?, strip 2 22?-22?), end frame not readable; remark: #180 cleared - no significance to strip #1 of Op 54/79 or strip #2 of Op 55/79" +"bc79075__bc79075_1.jpg",FALSE,"bc79075","B.C. 79-075","1-43",1,43,"16.50","True Height (M'/M.S.L.)","16500","ZE. 128316, f 85 mm","85","","clear","Sheet 1 of 2 (overwritten); Dease Lake Highway Op 56/79; field roll Oscar 37; strips 8-11 ditto'd; field 5 and 44-46 cleared" +"bc79043__bc79043_1.jpg",FALSE,"bc79043","B.C. 79-043","1-81",1,81,"8.5","True Height (M'/M.S.L.)","8500","camera 124223, f 153 mm","153","","clear","Sheet 1 of 3; Charles Cr. Op 311/79; field roll Nov. 37; strips 5,4,3,2,1 ditto'd; cleared 4 frames" +"bc78153__bc78153_5.jpg",FALSE,"bc78153","B.C. 78153","214-249",214,249,"2.9","True Height (M'/M.S.L.)","2900","camera 124223, f 153 mm","153","","clear","Sheet 5 of 8; Burnaby (Refly) Op 11/78; field roll Nov. 62; strips A and B ditto'd" +"bc78028__bc78028_3.jpg",FALSE,"bc78028","B.C. 78028","23-39",23,39,"12.5","True Height (M'/M.S.L.)","12500","camera 21162, f 153 mm","153","","clear","Sheet 3 of 5; Nesakwatch Creek Op 18/78; field roll Nov #6; strip 1; clear 1 frame (3) before, clear 3 frames (21,22,23) after" +"bc78153__bc78153_1.jpg",FALSE,"bc78153","B.C. 78153","1-103",1,103,"24.5","True Height (M'/M.S.L.)","24500","ZE. 110399, f 305 mm","305","","clear","Sheet 1 of 8; Little Moyie Op 25/78; field roll Nov. 62; strips 1-4 ditto'd; fog at start of strip 4 (southerly 2 or 3 photos covered by fog)" +"bc78110__bc78110_4.jpg",FALSE,"bc78110","B.C. 78-110","66-68",66,68,"10.15","True Height (M'/M.S.L.)","10150","camera 21162, f 153 mm","153","","clear","Sheet 4 of 6; Benson Lake Op 40/78; field roll Nov 38; strip 1, 3 preselected photo centres" +"bc78153__bc78153_8.jpg",FALSE,"bc78153","B.C. 78153","288-289",288,289,"20.25","True Height (M'/M.S.L.)","20250","ZE. 122530, f 305 mm","305","","clear","Sheet 8 of 8; Colwood (41) & Albert Head (42) Op 41/78 (42 written above); field roll Nov. 62; strip 1, pre-selected photo centres; remark: first 6 sheets were exposed on 6"" camera, pages 7 and 8 exp. on 12""; rebate change #78,79 clearing frames" +"bc78009__bc78009_2.jpg",FALSE,"bc78009","B.C. 78-009","67-83",67,83,"2.60","True Height (M'/M.S.L.)","2600","camera #124223, f 153 mm","153","","clear","Sheet 2 of 3; Nanaimo By Pass Op 52/78; field roll Delta 13; strip 1 (67-82) at 2.60 and strip 2 start (83) marked with ditto; remark on sheet: photos badly tilted, not suitable for mapping, may have some value for reconn." +"bc78009__bc78009_2.jpg",FALSE,"bc78009","B.C. 78-009","100",100,100,"2.70","True Height (M'/M.S.L.)","2700","camera #124223, f 153 mm","153","","clear","Sheet 2 of 3; strip 2 END row (final 100, overwritten) written 2.70 while its start (83) is ditto of 2.60; frames 84-99 between carry no separate height; photos badly tilted" +"bc78009__bc78009_2.jpg",FALSE,"bc78009","B.C. 78-009","101-131",101,131,"3.10","True Height (M'/M.S.L.)","3100","camera #124223, f 153 mm","153","","clear","Sheet 2 of 3; strip 3 (field 109-115, height 3.00) and strip 5 (field 132-147) marked DO NOT USE with no final numbers - skipped; 3.10 written on strip 4 (101-116) and ditto'd through DO-NOT-USE strip 5 to reflown strip (117-131); photos badly tilted" +"bc78009__bc78009_2.jpg",FALSE,"bc78009","B.C. 78-009","132-149",132,149,"3.00","True Height (M'/M.S.L.)","3000","camera #124223, f 153 mm","153","","partial","Sheet 2 of 3; strip 3 refly; final 132 has a stroke through it and end 149 overwritten (field 163-180 -> 132-149); field 181-183 cleared; photos badly tilted" +"bc78028__bc78028_1.jpg",FALSE,"bc78028","B.C. 78028","1-20",1,20,"3.1","True Height (M'/M.S.L.)","3100","camera 21162, f 153 mm","153","","clear","Sheet 1 of 5; Nanaimo Bypass Refly Op 52/78; field roll Nov 6; strip 5; cleared 5 frames before, clear 3 after" +"bc78110__bc78110_1.jpg",FALSE,"bc78110","B.C. 78-110","1-14",1,14,"5.1","True Height (M'/M.S.L.)","5100","camera 21162, f 153 mm","153","","clear","Sheet 1 of 6; Nanaimo Bypass Op 52/78; field roll Nov 38; strip A; cleared 5 frames; remark on thin negatives / processor slowed" +"bc78110__bc78110_1.jpg",FALSE,"bc78110","B.C. 78-110","15-39",15,39,"2.6","True Height (M'/M.S.L.)","2600","camera 21162, f 153 mm","153","","clear","Sheet 1 of 6; strip 6" +"bc78110__bc78110_2.jpg",FALSE,"bc78110","B.C. 78-110","40-47",40,47,"10.15","True Height (M'/M.S.L.)","10150","camera 21162, f 153 mm","153","","clear","Sheet 2 of 6; Stamp-Somass River #2 Op 56/78; field roll Nov 38; strip 4" +"bc78028__bc78028_4.jpg",FALSE,"bc78028","B.C. 78028","40-91",40,91,"7.150","True Height (M'/M.S.L.)","7150","camera 21162, f 153 mm","153","","clear","Sheet 4 of 5 (smudged); Kettle River #1 Op 84/78; field roll Nov. 6; strips 1,3(overwritten),2 ditto'd; height written with three decimals" +"bc78028__bc78028_4.jpg",FALSE,"bc78028","B.C. 78028","92-153",92,153,"7.350","True Height (M'/M.S.L.)","7350","camera 21162, f 153 mm","153","","clear","Sheet 4 of 5; 7.350 written on field 53 row (misfire first exposure, no final number - skipped), ditto'd from final 92 through strips 4,5,6,8 to 153" +"bc78028__bc78028_4.jpg",FALSE,"bc78028","B.C. 78028","154-204",154,204,"7,550","True Height (M'/M.S.L.)","7550","camera 21162, f 153 mm","153","","clear","Sheet 4 of 5; new height at 154 (written 7,550 or 7.550) ditto'd through strips 9 and 7; last final 204 overwritten; 167-175 cleared; increase in overlap due to high sidehills" +"bc78028__bc78028_5.jpg",FALSE,"bc78028","B.C. 78028","205-279",205,279,"22.7","True Height (M'/M.S.L.)","22700","ZEISS 122520, f 305 mm","305","","clear","Sheet 5 of 5; Columbia River - Radium Op 91/78; field roll written Oscar 10 on this sheet (earlier sheets Nov 6), 'part roll'; strips 1,2,1 ditto'd; 1-4 and 76 cleared; remark: two #1 strips on field copy, use start/end place names" +"bc78009__bc78009_1.jpg",FALSE,"bc78009","B.C. 78-009","1-66",1,66,"5.5","True Height (M'/M.S.L.)","5500","camera 124223, f 153 mm","153","","clear","Sheet 1 of 3 (overwritten); Haney Op 114/78; field roll Delta 13; strips 6 and 5 ditto'd; clear 16 frames before; 67-74 cleared" +"bc78101__bc78101_4.jpg",FALSE,"bc78101","B.C. 78-101","186-247",186,247,"24.0","True Height (M'/M.S.L.)","24000","camera 122520, f 305 mm","305","","clear","Sheet 4 of 4; Big Valley P.S.Y.U. Op 138/78; field roll Oscar 29; strip 3; extra exposure for cloud at 13" +"bc78009__bc78009_3.jpg",FALSE,"bc78009","78009","150-169",150,169,"5.5","TRUE HEIGHT (M'/M.S.L.)","5500","ZE 110398 f 305","305","","clear","sheet 3 of 3; Coquihalla River Op 178/78; field Nov#4; strips 3-4; PART ROLL" +"bc78009__bc78009_3.jpg",FALSE,"bc78009","78009","170-220",170,220,"6.0","TRUE HEIGHT (M'/M.S.L.)","6000","ZE 110398 f 305","305","","clear","sheet 3 of 3; strips 5-7; height ditto'd" +"bc78009__bc78009_3.jpg",FALSE,"bc78009","78009","221-236",221,236,"6.5","TRUE HEIGHT (M'/M.S.L.)","6500","ZE 110398 f 305","305","","clear","sheet 3 of 3; strips 8-9, 6.5 written on both strips" +"bc78009__bc78009_3.jpg",FALSE,"bc78009","78009","237-248",237,248,"7.0","TRUE HEIGHT (M'/M.S.L.)","7000","ZE 110398 f 305","305","","clear","sheet 3 of 3; strip 10" +"bc78009__bc78009_3.jpg",FALSE,"bc78009","78009","249-268",249,268,"7.5","TRUE HEIGHT (M'/M.S.L.)","7500","ZE 110398 f 305","305","","clear","sheet 3 of 3; strip 11" +"bc78051__bc78051_4.jpg",FALSE,"bc78051","78051","210-228",210,228,"8.0","TRUE HEIGHT (M'/M.S.L.)","8000","21162 f 153","153","","clear","sheet 4 of 5; Houser Ridge Op 289/78; field 15" +"bc78051__bc78051_2.jpg",FALSE,"bc78051","78051","132-195",132,195,"22.5","TRUE HEIGHT (M'/M.S.L.)","22500","110398 f 305","305","","clear","sheet 2 of 5; Mica Pondage Op 330/78; strips 10,13,12,11; 196 cleared" +"bc78051__bc78051_3.jpg",FALSE,"bc78051","78051","196-209",196,209,"8.0","TRUE HEIGHT (M'/M.S.L.)","8000","21162 f 153","153","","clear","sheet 3 of 5; Trout Lake Op 267/78" +"bc78101__bc78101_1.jpg",FALSE,"bc78101","78-101","1-83",1,83,"25.5","TRUE HEIGHT (M'/M.S.L.)","25500","124223 f 153","153","","clear","sheet 1 of 4; Harrison-Yale Op 309/78; field Oscar 30; strips 7,6,5,4; remark: Op 138 Big Valley #1-66 spliced onto end of this short roll, #1-66 is 12in photography" +"bc78101__bc78101_1.jpg",FALSE,"bc78101","78-101","84-143",84,143,"26.5","TRUE HEIGHT (M'/M.S.L.)","26500","124223 f 153","153","","clear","sheet 1 of 4; strips 3,2,1" +"bc78153__bc78153_2.jpg",FALSE,"bc78153","78-153","104-144",104,144,"27.5","TRUE HEIGHT (M'/M.S.L.)","27500","124223 f 153","153","","clear","sheet 2 of 8; Kootenay-White Rivers Op 312/78; 27.5 written on field 1-19 rows marked DO NOT USE (no final numbers), inherited by 104-144" +"bc78051__bc78051_1.jpg",FALSE,"bc78051","78051","1-131",1,131,"22.5","TRUE HEIGHT (M'/M.S.L.)","22500","110398 f 305","305","","clear","sheet 1 of 5; Mica Pondage Op 330/78; strips 1-9; remark camera may have rapid fired at 131" +"bc78153__bc78153_6.jpg",FALSE,"bc78153","78153","250-262",250,262,"6.8","TRUE HEIGHT (M'/M.S.L.)","6800","124223 f 153","153","","clear","sheet 6 of 8; Manning Park Op 338/78; 6.8 written on field 172-178 DO NOT USE row (no finals), inherited by 250-262" +"bc78153__bc78153_6.jpg",FALSE,"bc78153","78153","263-276",263,276,"6.4","TRUE HEIGHT (M'/M.S.L.)","6400","124223 f 153","153","","clear","sheet 6 of 8; strip 1" +"bc78153__bc78153_4.jpg",FALSE,"bc78153","78153","194-213",194,213,"9.5","TRUE HEIGHT (M'/M.S.L.)","9500","124223 f 153","153","","clear","sheet 4 of 8; Mt Seymour Park Op 339/78" +"bc78101__bc78101_3.jpg",FALSE,"bc78101","78101","175-185",175,185,"11.1","TRUE HEIGHT (M'/M.S.L.)","11100","124223 f 153","153","","clear","sheet 3 of 4; Okanagan Orthophoto Op 363/78" +"bc78101__bc78101_2.jpg",FALSE,"bc78101","78101","144-174",144,174,"2150","TRUE HEIGHT (M'/M.S.L.)","","124223 f 153","153","","clear","sheet 2 of 4; Kelowna #2 Op 376/78; height written 2150 does not fit thousands convention (possibly literal 2,150 ft); left empty" +"bc78153__bc78153_3.jpg",FALSE,"bc78153","78153","145-193",145,193,"26.","TRUE HEIGHT (M'/M.S.L.)","26000","124223 f 153","153","","clear","sheet 3 of 8; Kelowna Op 386/78; remark 80% fwd overlap" +"bc78028__bc78028_2.jpg",FALSE,"bc78028","78028","21",21,21,"7.0","TRUE HEIGHT (M'/M.S.L.)","7000","ZE 21162 f 153","153","","partial","sheet 2 of 5; Abbotsford Airport Op 402/78; preselected photo centre airport; final number overwritten (21 with (21) beside)" +"bc78028__bc78028_2.jpg",FALSE,"bc78028","78028","22",22,22,"3.5","TRUE HEIGHT (M'/M.S.L.)","3500","ZE 21162 f 153","153","","clear","sheet 2 of 5; preselected photo centre viewing area" +"bc78110__bc78110_5.jpg",FALSE,"bc78110","78-110","69-71",69,71,"1","TRUE HEIGHT (M'/M.S.L.)","1000","21162 f 153","153","","clear","sheet 5 of 6; Buttle Lake (Western Mines) Op 36/78; height written as single digit '1', read as 1 thousand ft; 3 preselected photo centres" +"bc78110__bc78110_3.jpg",FALSE,"bc78110","78-110","48-65",48,65,"3.75","TRUE HEIGHT (M'/M.S.L.)","3750","21162 f 153","153","","clear","sheet 3 of 6; Carnation Creek Op 354/78" +"bc77013__bc77013_3.jpg",FALSE,"bc77013","77-13","90-247",90,247,"10.5","TRUE HEIGHT (M'/M.S.L.)","10500","124223 f 153","153","","clear","sheet 3 of 4; Skeena River #2 Op 68/77; 10.5 written on field 1-17 DO NOT USE row, inherited; field 151-154 DO NOT USE (no finals) skipped" +"bc77115__bc77115_1.jpg",FALSE,"bc77115","77-115","1-66",1,66,"25.0","TRUE HEIGHT (M'/M.S.L.)","25000","110398 f 305","305","","clear","sheet 1 of 3; Dean PSYU Op 104/77; focal overwritten before 305" +"bc77013__bc77013_2.jpg",FALSE,"bc77013","77-13","57-89",57,89,"24.0","TRUE HEIGHT (M'/M.S.L.)","24000","110398 f 12""","305","","clear","sheet 2 of 4; Prince George Sales Area Op 112" +"bc77013__bc77013_1.jpg",FALSE,"bc77013","77-13","1-56",1,56,"24.0","TRUE HEIGHT (M'/M.S.L.)","24000","ZE 124223 f 6"" 153","153","","clear","sheet 1 of 4; 93 I&J Fort St James-McGregor Op 138/77; remark: 'This page may belong to another roll?'" +"bc77115__bc77115_3.jpg",FALSE,"bc77115","77-115","117-218",117,218,"7.5","TRUE HEIGHT (M'/M.S.L.)","7500","21162 6"" f 153","153","","clear","sheet 3 of 3; Salmo River Op 277/77" +"bc77115__bc77115_2.jpg",FALSE,"bc77115","77-115","67-116",67,116,"2.7","TRUE HEIGHT (M'/M.S.L.)","2700","ZE 21162 (6"") f 153","153","","clear","sheet 2 of 3; 16th Ave Extension Op 300/77; every second frame annotated; 30% overlap setting" +"bc5699__bc5699_1.jpg",FALSE,"bc5699","5699","1-254",1,254,"6.15","TRUE HEIGHT (M'/M.S.L.)","6150","ZE 124223 (6"")","153","","clear","sheet 1 of 1; Fraser Valley Flooding Op 7; field Delta#4; 255-260 wound to end; faint '5700' in first final box" +"bc5701__bc5701_5702_1.jpg",FALSE,"bc5701","5701","1-296",1,296,"6.15","TRUE HEIGHT (M'/M.S.L.)","6150","ZE 124223 (6"")","153","","clear","sheet 1 of 2 (sheet holds 5701 and 5702); Fraser Valley Flooding Op 7; field Delta#5; 297-300 cleared, cut" +"bc5701__bc5701_5702_1.jpg",FALSE,"bc5702","5702","1-178",1,178,"6.15","TRUE HEIGHT (M'/M.S.L.)","6150","ZE 124223 (6"")","153","","clear","sheet 1 of 2; after cut, field 301-478 = final 001-178 (5702); height rewritten 6.15" +"bc5702__bc5701_5702_2.jpg",FALSE,"bc5702","5702","179-260",179,260,"3.000","TRUE HEIGHT (M'/M.S.L.)","3000","ZE 124223 (6"")","153","","partial","sheet 2 of 2; Fraser Valley Flooding Op 7; field Delta 5, field 489-570; separator could be '.' or ','; either reading is 3000 ft; final 179 overwritten and 220 corrected to 260" +"bc5709__bc5709_1.jpg",FALSE,"bc5709","5709","1-73",1,73,"11.5","TRUE HEIGHT (M'/M.S.L.)","11500","ZE 124223 (6"")","153","","clear","sheet 1 of 3; Sonora-Stuart Is. Environment Op 97/76; field Oscar#2" +"bc5709__bc5709_3.jpg",FALSE,"bc5709","5709","217-273",217,273,"9.920","TRUE HEIGHT (M'/M.S.L.)","9920","ZE 124223 (6"")","153","","partial","sheet 3 of 3; Quesnel Dept Highways Op 111/76; height read as 9.920 (9,920 ft); a struck-out line above reads 9.2" +"bc5709__bc5709_2.jpg",FALSE,"bc5709","5709","74-154",74,154,"11.0","TRUE HEIGHT (M'/M.S.L.)","11000","ZE 124223 (6"")","153","","clear","sheet 2 of 3; White-Salmon Rv Rec & Con Op 187/76; strips 1-3" +"bc5709__bc5709_2.jpg",FALSE,"bc5709","5709","155-184",155,184,"12.0","TRUE HEIGHT (M'/M.S.L.)","12000","ZE 124223 (6"")","153","","clear","sheet 2 of 3; strip 4" +"bc5709__bc5709_2.jpg",FALSE,"bc5709","5709","185-216",185,216,"12.5","TRUE HEIGHT (M'/M.S.L.)","12500","ZE 124223 (6"")","153","","clear","sheet 2 of 3; strip 5" +"bc5642__bc5642_1.jpg",FALSE,"bc5642","5642","1-32",1,32,"11.5","TRUE HEIGHT (M'/M.S.L.)","11500","ZE 21162 (6"")","153","","clear","sheet 1 of 3; Cypress Park Op 1/75" +"bc5648__bc5648_2.jpg",FALSE,"bc5648","5648","46-109",46,109,"3.50","TRUE HEIGHT (M'/M.S.L.)","3500","RC8-502","","","clear","sheet 2 of 3 (sheet no. partly cut off); Barnett-Ioco Op 28/75; camera only, no focal written" +"bc7716__bc7715_7716_3.jpg",FALSE,"bc7716","7716","169-246",169,246,"12.5","TRUE HEIGHT (M'/M.S.L.)","12500","ZE 110398 ""12""","305","","clear","sheet 3 of 3; Tashis-Zeballos-Muchalat LD Insp Op 42/75; first Final B.C. box annotated 'wrong roll', second reads 7716" +"bc5665__bc5664_5665_3.jpg",FALSE,"bc5665","5665","77-113",77,113,"12.","TRUE HEIGHT (M'/M.S.L.)","12000","ZE 21162 ""6""","153","","partial","sheet 3? of 5; Lower Nass Valley LD Insp Op 46/75; height '12.' with possible faint trailing digit" +"bc5665__bc5664_5665_2.jpg",FALSE,"bc5664","5664","242-285",242,285,"7.25","TRUE HEIGHT (M'/M.S.L.)","7250","ZE 21162 ""6""","153","","clear","sheet 2 of 5 (sheet holds 5664 and 5665); Terrace Area LD Inspection Op 47/75; 286-290 cleared/punched" +"bc5665__bc5664_5665_2.jpg",FALSE,"bc5665","5665","1-56",1,56,"7.25","TRUE HEIGHT (M'/M.S.L.)","7250","ZE 21162 ""6""","153","","clear","sheet 2 of 5; field 291-346; 7.25 rewritten at start of 5665" +"bc5665__bc5664_5665_2.jpg",FALSE,"bc5665","5665","57-76",57,76,"13.5","TRUE HEIGHT (M'/M.S.L.)","13500","ZE 21162 ""6""","153","","partial","sheet 2 of 5; field 347-366, strips 7-8; 13.5 appears overwritten/bold" +"bc5667__bc5666_5667_6.jpg",FALSE,"bc5667","5667","117-122",117,122,"12.5","TRUE HEIGHT (M'/M.S.L.)","12500","ZE #21162 ""6""","153","","partial","sheet 4 of 5 (amended); Prince Rupert LD Inspection Div Op 48/75; final numbers faint" +"bc5650__bc5650_2.jpg",FALSE,"bc5650","5650","67-137",67,137,"6.75","TRUE HEIGHT (M'/M.S.L.)","6750","RC8-502","","","clear","sheet 2 of 4; Squamish Area Op 56 Land Inspection; camera only" +"bc5689__bc5689_2.jpg",FALSE,"bc5689","5689","95-179",95,179,"5.2","TRUE HEIGHT (M'/M.S.L.)","5200","RC8-502 (6"")","153","","clear","sheet 2 of 3; Shuswap R. Op 66 Water Resources; filename listed twice in batch, transcribed once" +"bc5689__bc5689_2.jpg",FALSE,"bc5689","5689","180-191",180,191,"5.5","TRUE HEIGHT (M'/M.S.L.)","5500","RC8-502 (6"")","153","","clear","sheet 2 of 3; Eagle River Op 283 refly" +"bc5689__bc5689_2.jpg",FALSE,"bc5689","5689","192-215",192,215,"5.2","TRUE HEIGHT (M'/M.S.L.)","5200","RC8-502 (6"")","153","","clear","sheet 2 of 3; Op 66" +"bc5645__bc5645_3.jpg",FALSE,"bc5645","5645","277-295",277,295,"2150","TRUE HEIGHT (M'/M.S.L.)","","RC8-502","","","partial","sheet 3 of 3; Tillicum-Thetis Interchange Op 83; height 2150 does not fit thousands convention (possibly literal 2,150 ft); left empty; finals faint" +"bc5644__bc5644_1.jpg",FALSE,"bc5644","5644","1-11",1,11,"6.1","TRUE HEIGHT (M'/M.S.L.)","6100","ZE 21162 ""6""","153","","clear","sheet 1 of 2; Coquitlam River Water Res Op 99/75; field 12-24 rejected (slashed, 'this flight 300 ft low')" +"bc5644__bc5644_1.jpg",FALSE,"bc5644","5644","12-22",12,22,"6.3","TRUE HEIGHT (M'/M.S.L.)","6300","ZE 21162 ""6""","153","","clear","sheet 1 of 2; refly at correct height (field 25-35)" +"bc5642__bc5642_2.jpg",FALSE,"bc5642","5642","33-210",33,210,"6.10","TRUE HEIGHT (M'/M.S.L.)","6100","ZE 21162 (6"")","153","","clear","sheet 2 of 3; Fraser River Low Water Op 121" +"bc5644__bc5644_2.jpg",FALSE,"bc5644","5644","23-96",23,96,"6.1","TRUE HEIGHT (M'/M.S.L.)","6100","ZE 21162 ""6""","153","","partial","sheet 2 of 2; Fraser River Low Water Op 121/75; last strip field 106-123 but final 93-96 (count mismatch)" +"bc5645__bc5645_2.jpg",FALSE,"bc5645","5645","186-276",186,276,"6.1","TRUE HEIGHT (M'/M.S.L.)","6100","RC8-502","","","clear","sheet 2 of 3; Fraser River Low Water Op 121" +"bc5648__bc5648_3.jpg",FALSE,"bc5648","5648","110-277",110,277,"6.10","TRUE HEIGHT (M'/M.S.L.)","6100","RC8-502","","","clear","sheet 3 of 3; Fraser River Low Water Op 121" +"bc5650__bc5650_1.jpg",FALSE,"bc5650","5650","1-66",1,66,"6.10","TRUE HEIGHT (M'/M.S.L.)","6100","RC8-502","","","clear","sheet 1 of 4; Fraser River Low Water Op 121" +"bc5689__bc5689_3.jpg",FALSE,"bc5689","5689","216-287",216,287,"6.1","TRUE HEIGHT (M'/M.S.L.)","6100","RC8-502 (6"")","153","","clear","sheet 3 of 3; Fraser R. Low Water Op 121" +"bc5645__bc5645_1.jpg",FALSE,"bc5645","5645","1-185",1,185,"2.4","TRUE HEIGHT (M'/M.S.L.)","2400","RC8-502","","","clear","sheet 1 of 3; Mud Bay Op 122 Map Production; field 58 row struck through; end of strip 7 final looks '67' but field 57" +"bc5667__bc5666_5667_5.jpg",FALSE,"bc5667","5667","36-116",36,116,"17.0","TRUE HEIGHT (M'/M.S.L.)","17000","ZE 21162 ""6""","153","","clear","sheet 3 of 5 (amended); Campbell River/Courtenay D.O.H. Op 296/75" +"bc5667__bc5666_5667_4.jpg",FALSE,"bc5667","5667","32-35",32,35,"10.5","TRUE HEIGHT (M'/M.S.L.)","10500","ZE 21162 ""6""","153","","clear","sheet 2 of 5 (amended); Cowichan Orthophoto Op 315/75" +"bc5665__bc5664_5665_4.jpg",FALSE,"bc5665","5665","114-216",114,216,"9.0","M'/M.S.L.","9000","ZE 21162 ""6""","153","","clear","Sheet 4 of 5; Highway 16 Dept Munic Affairs OP38/75; 9.0 written on first strip, ditto through final 216" +"bc5665__bc5664_5665_4.jpg",FALSE,"bc5665","5665","217-232",217,232,"9.5","M'/M.S.L.","9500","ZE 21162 ""6""","153","","clear","Sheet 4 of 5; new height written on last strip (field 104-119)" +"bc5689__bc5689_1.jpg",FALSE,"bc5689","5689","1-94",1,94,"5.0","M'/M.S.L.","5000","RC 8-502 (6"")","153","","clear","Sheet 1 of 3; Refly Kootenay Canal OP 298 BC Hydro; 3 strips ditto" +"bc5667__bc5666_5667_3.jpg",FALSE,"bc5667","5667","1-23",1,23,"6.2","M'/M.S.L.","6200","ZE 21162 ""6""","153","","clear","Sheet number overwritten (1 of 5?); Silver Lake Wat. Invest OP 318/75; remarks say photographer unsure whether started with field 208 or 207" +"bc5667__bc5666_5667_3.jpg",FALSE,"bc5667","5667","24-31",24,31,"9.2","M'/M.S.L.","9200","ZE 21162 ""6""","153","","clear","Same sheet; strip 3 new height" +"bc7454__bc7453_7454_2.jpg",FALSE,"bc7454","7454","1-123",1,123,"8.0","M'/M.S.L.","8000","ZE#1/UAG#1","","","clear","Sheet 2 of 4 or 5 (total overwritten); Kootenay Canal Area OP 111 BC Hydro; remarks: low level 80% forward overlap for mosaic; camera identifier only, no focal written" +"bc5507__bc5507_3.jpg",FALSE,"bc5507","5507","33-152",33,152,"22.0","M'/M.S.L.","22000","RC8 355 (50 struck, 355 written)","","","partial","Sheet 3 of 3; Peace PSYU Forest Inventory OP 10/72; field UYT#17; 355 read as camera serial not focal (RC8-502 elsewhere carries 6""); finals 153-158 (field 175-180) marked DO NOT USE, excluded" +"bc5507__bc5507_3.jpg",FALSE,"bc5507","5507","159-233",159,233,"22.0","M'/M.S.L.","22000","RC8 355 (50 struck, 355 written)","","","partial","Continuation after DNU strip (restart at field 181 final 159); some red finals overwritten" +"bc5510__bc5510_1.jpg",FALSE,"bc5510","5510","1-94",1,94,"22.0","M'/M.S.L.","22000","RC 8 355","","","clear","Sheet 1 of 2; Peace PSYU Forest Inventory OP 10/72; field UYT#18" +"bc5494__bc5494_1.jpg",FALSE,"bc5494","5494","1-267",1,267,"21.0","M'/M.S.L.","21000","RC-8-355","","","clear","Sheet 1 of 1; Finlay Klappan / Kechika-Liard Forest Inventory OP 9/72, 11/72; field UYT#8" +"bc5508__bc5508_1.jpg",FALSE,"bc5508","5508","1-299",1,299,"21.5","M'/M.S.L.","21500","RC8 355","","","clear","Sheet 1 of 1; Kechika-Liard Forest Inventory OP 12/72; field UYT#20" +"bc5509__bc5509_1.jpg",FALSE,"bc5509","5509","1-298",1,298,"21.5","M'/M.S.L.","21500","RC8 355","","","clear","Sheet 1 of 1; Kechika-Liard OP 12/72; field UYT#19; field 225 DO NOT USE (no final), skipped" +"bc5510__bc5510_2.jpg",FALSE,"bc5510","5510","95-244",95,244,"21.5","M'/M.S.L.","21500","RC8 355","","","clear","Sheet 2 of 2; Kechika-Liard Forest Inventory OP 12/72" +"bc7407__bc7407_7408_1.jpg",FALSE,"bc7407","7407","1-242",1,242,"16.5","M'/M.S.L.","16500","ZE#2 UAG#4 110398","","","clear","Sheet 1 of 2; E&N Lands Dept of Finance OP 65/72; one height covers both rolls on sheet" +"bc7407__bc7407_7408_1.jpg",FALSE,"bc7408","7408","1-241",1,241,"16.5","M'/M.S.L.","16500","ZE#2 UAG#4 110398","","","clear","Same sheet; roll 7408 starts at field 243 final 1; height ditto'd from top" +"bc5507__bc5507_2.jpg",FALSE,"bc5507","5507","11-36",11,36,"22.5","M'/M.S.L.","22500","RC8 355","","","partial","Sheet 2 of 3; Bell Irving Boundary Forest OP 14/72; final end overwritten (reads 32 or 36); field 1-26 implies 36; field 29-39 test exposures" +"bc5507__bc5507_1.jpg",FALSE,"bc5507","5507","1-10",1,10,"14.0","M'/M.S.L.","14000","RC8-502","","","clear","Sheet 1 of 3; Parsnip River OP 114/72 BC Hydro; field DELTA 33 (sheets 2-3 are field UYT#17)" +"bc5507__bc5507_1.jpg",FALSE,"bc5507","5507","",,,"12.5","M'/M.S.L.","12500","RC8-502","","","clear","TEST strip field 16-22 at 12.5, no final numbers assigned" +"bc5667__bc5666_5667_7.jpg",FALSE,"bc5667","5667","123-134",123,134,"13.0","M'/M.S.L.","13000","ZE 21162 ""6""","153","","clear","Sheet number overwritten (4?) of 5; Dept Hwy Terrace OP 292/75" +"bc5650__bc5650_3.jpg",FALSE,"bc5650","5650","138-218",138,218,"10.20","M'/M.S.L.","10200","RC8-502","","","clear","Sheet 3 of 4; Gulf Islands OP57/75; field 142-144 DO NOT USE (off line), skipped" +"bc5650__bc5650_4.jpg",FALSE,"bc5650","5650","219-270",219,270,"10.20","M'/M.S.L.","10200","RC8-502","","","clear","Sheet 4 of 4; Gulf Islands OP57" +"bc80006__bc80006_3.jpg",FALSE,"bc80006","80-006","134-217",134,217,"7.0","M'/M.S.L.","7000","124223 f 153 mm","153","","clear","Sheet 3 of 5 (4 overwritten to 5); Moore Lake Op 27/80" +"bc80122__bc80122_1.jpg",FALSE,"bc80122","80-122","1-55",1,55,"26.00","M'/M.S.L.","26000","ZE 110399 f 305 mm","305","","clear","Sheet 1 of 2; Ladybird-Norns Creek Op 83/80" +"bc80048__bc80048_1.jpg",FALSE,"bc80048","80-048","1-123",1,123,"23.5","M'/M.S.L.","23500","21162 f 153 mm","153","","clear","Sheet 1 of 3; Sayward #2 Op 108/80; remark: photographer not certain of numbering system assumed; one frame cleared between strips" +"bc80122__bc80122_2.jpg",FALSE,"bc80122","80-122","56-263",56,263,"13.50","M'/M.S.L.","13500","ZE 110399 f 305 mm","305","","clear","Sheet 2 of 2; Perow-Tachek Op 134/80; field 62-64 DO NOT USE, skipped" +"bc80018__bc80018_4.jpg",FALSE,"bc80018","80-018","64-161",64,161,"21.0","M'/M.S.L.","21000","122520 f 305 mm","305","","clear","Sheet 4 of 4; Nelson Forks Op 147/80; end of strip 16 final 114 (overwritten)" +"bc80018__bc80018_3.jpg",FALSE,"bc80018","80-018","38-63",38,63,"6.0","M'/M.S.L.","6000","RC 8 502 f 153 mm","153","","partial","Sheet 3 of 4; Westland Pit Quesnel Op 190/80; height 6.0 written on DNU strip (shutter off) and ditto'd; first DNU strip reads 96.0 (or 6.0 with stray stroke), both DNU skipped" +"bc80018__bc80018_2.jpg",FALSE,"bc80018","80-018","32-37",32,37,"9.9","M'/M.S.L.","9900","RC8 502 f 153 mm","153","","clear","Sheet 2 of 4; Graveyard Creek Op 197/80" +"bc80018__bc80018_1.jpg",FALSE,"bc80018","80-018","1-31",1,31,"9.4","M'/M.S.L.","9400","RC8 502 f 153 mm","153","","clear","Sheet 1 of 4; Pine River Op 198/80 (overwritten)" +"bc80006__bc80006_2.jpg",FALSE,"bc80006","80-006","104-133",104,133,"6.0","M'/M.S.L.","6000","124223 f 153 mm","153","","clear","Sheet 2 of 5; Felker Lake Op 204/80; one strip DO NOT USE (no finals), skipped" +"bc80006__bc80006_5.jpg",FALSE,"bc80006","80-006","253-332",253,332,"5.2","M'/M.S.L.","5200","110398 f 305 mm","305","","clear","Sheet 5 of 5; Port Alberni #2 Op 212/80; field roll Nov 16; date written 7 May 79" +"bc80006__bc80006_4.jpg",FALSE,"bc80006","80-006","218-252",218,252,"10.2","M'/M.S.L.","10200","124223 f 153 mm","153","","clear","Sheet 4 of 5; Denman-Hornby Is. Op 213/80; spliced" +"bc80048__bc80048_2.jpg",FALSE,"bc80048","80-048","124-302",124,302,"21.5","M'/M.S.L.","21500","21162 f 153 mm","153","","clear","Sheet 2 of 3; Pemberton-Lillooet Op 219/80" +"bc80048__bc80048_3.jpg",FALSE,"bc80048","80-048","303-353",303,353,"12.5","M'/M.S.L.","12500","21162 f 153 mm","153","","clear","Sheet 3 of 3; 303-334 Op 220/80 Birkenhead Lk-Paradise Valley, 335-353 Op 218/80; final strip DO NOT USE (no finals), skipped" +"bc80006__bc80006_1.jpg",FALSE,"bc80006","80-006","1-103",1,103,"11.5","M'/M.S.L.","11500","124223 f 153 mm","153","","clear","Sheet 1 of 5; Williams Lake Op 237/80; remarks 80% FO under clouds on some strips" +"bc80005__bc80005_2.jpg",FALSE,"bc80005","80-005","247-249",247,249,"5.2","M'/M.S.L.","5200","124223 f 153 mm","153","","clear","Sheet 2 of 3; Sidney-Victoria Op 294/80 (288 struck)" +"bc80005__bc80005_3.jpg",FALSE,"bc80005","80-005","250-277",250,277,"5.2","M'/M.S.L.","5200","124223 f 153 mm","153","","clear","Sheet 3 of 3; 250-259 Langford Shawnigan Cutoff Op 288-80, 260-277 Helmcken-Wilkinson Rd Op 292-80" +"bc80005__bc80005_1.jpg",FALSE,"bc80005","80-005","1-246",1,246,"5.2","M'/M.S.L.","5200","124223 f 153 mm","153","","clear","Sheet 1 of 3; Sidney-Victoria Op 294/80 (Langford struck); last strip DO NOT USE (no finals), skipped" +"bc81012__bc81012_1.jpg",FALSE,"bc81012","81-012","1-191",1,191,"12.00","M'/M.S.L.","12000","21162 f 153 mm","153","","partial","Sheet 1 of 4; Chehalis-Rose Creek Op 43/81; strip 7 DO NOT USE then restart final 86; several intermediate finals overwritten" +"bc81012__bc81012_3.jpg",FALSE,"bc81012","81-012","196-213",196,213,"12.0","M'/M.S.L.","12000","21162 f 153 mm","153","","clear","Sheet 3 of 4; West Redonda Island Op 48/81; one strip DO NOT USE (no finals), skipped" +"bc81012__bc81012_2.jpg",FALSE,"bc81012","81-012","192-195",192,195,"8.3","M'/M.S.L.","8300","21162 f 153 mm","153","","clear","Sheet 2 of 4; Mackenzie Airport Op 300/81 Transport Canada" +"bc81012__bc81012_4.jpg",FALSE,"bc81012","81-012","214-264",214,264,"8.1","M'/M.S.L.","8100","124223 f 153 mm","153","","clear","Sheet 4 of 4; 214-219 Vaseux Lake Op 324/81, 220-264 Skaha Lake Op 325/81 (nav training); attached to roll 81-012" +"bc84029__bc84029_2.jpg",FALSE,"bc84029","84-029","202-249",202,249,"16500","M'/M.S.L.","16500","RC 10 f 304 mm","304","","clear","Sheet 2 of 3; Sechelt-Gibsons 25F/84; height written as full figure 16500 (not thousands convention), repeated on each strip; left uninterpreted; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc84029__bc84029_3.jpg",FALSE,"bc84029","84-029","250-283",250,283,"10600","M'/M.S.L.","10600","RC 10 f 304 mm","304","","partial","Sheet 3 of 3; Cayuse River 103E/84; full figure 10600, not thousands convention; intermediate finals smeared, end 283 inferred from next start 284; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc84029__bc84029_3.jpg",FALSE,"bc84029","84-029","284-294",284,294,"10900","M'/M.S.L.","10900","RC 10 f 304 mm","304","","clear","Same sheet; full figure 10900; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc85079__bc85079_1.jpg",FALSE,"bc85079","85079","1-261",1,261,"21000","M'/M.S.L.","21000","RC10 f 305 mm","305","","clear","Sheet 1 of 1; Monkman-Longworth 50-F/85; full figure 21000 (not thousands convention); interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc85080__bc85080_1.jpg",FALSE,"bc85080","85080","1-84",1,84,"21000","M'/M.S.L.","21000","RC10 f 305 mm","305","","clear","Sheet 1 of 2; Monkman-Longworth 50-F/85; full figure; cut-out frames between strips have no finals; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc85080__bc85080_1.jpg",FALSE,"bc85080","85080","85-145",85,145,"20,000","M'/M.S.L.","20000","RC10 f 305 mm","305","","clear","Same sheet; full figure 20,000; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc85080__bc85080_1.jpg",FALSE,"bc85080","85080","146-192",146,192,"21000","M'/M.S.L.","21000","RC10 f 305 mm","305","","clear","Same sheet; full figure 21000; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc85080__bc85080_2.jpg",FALSE,"bc85080","85080","193-238",193,238,"21000","M'/M.S.L.","21000","RC10 f 305 mm","305","","clear","Sheet 2 of 2; full figure 21000; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc85081__bc85081_1.jpg",FALSE,"bc85081","85081","1-80",1,80,"21,000","M'/M.S.L.","21000","RC10 f 305 mm","305","","clear","Sheet 1 of 3; Monkman-Longworth 50-F/85; full figure; frame 6829 85% overlap; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc85081__bc85081_1.jpg",FALSE,"bc85081","85081","81-97",81,97,"20000","M'/M.S.L.","20000","RC10 f 305 mm","305","","clear","Same sheet; full figure 20000; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc85081__bc85081_2.jpg",FALSE,"bc85081","85081","98-137",98,137,"21,500","M'/M.S.L.","21500","RC10 f 305 mm","305","","clear","Sheet 2 of 3; Clearwater-North Thompson 39-F/85; full figure 21,500; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc85081__bc85081_3.jpg",FALSE,"bc85081","85081","138-231",138,231,"21,000","M'/M.S.L.","21000","RC10 f 305 mm","305","","clear","Sheet 3 of 3; Monkman-Longworth 50-F/85; full figure 21,000; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bcc544__bcc544_1.jpg",FALSE,"bcc544","(C)544","1-21",1,21,"5800","M'/M.S.L.","5800","RC10 f 305 mm","305","","clear","Sheet 1 of 2; Tsitika River ARC C-125-E/86; colour neg; full figure 5800 (not thousands convention); interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bcc544__bcc544_1.jpg",FALSE,"bcc544","(C)544","22-55",22,55,"5700","M'/M.S.L.","5700","RC10 f 305 mm","305","","clear","Same sheet; full figure 5700; blanks 843-845 field between strips; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bcc544__bcc544_1.jpg",FALSE,"bcc544","(C)544","56-70",56,70,"5500","M'/M.S.L.","5500","RC10 f 305 mm","305","","clear","Same sheet; full figure 5500; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bcc544__bcc544_1.jpg",FALSE,"bcc544","(C)544","71-85",71,85,"5300","M'/M.S.L.","5300","RC10 f 305 mm","305","","clear","Same sheet; full figure 5300; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bcc544__bcc544_2.jpg",FALSE,"bcc544","(C)544","86-102",86,102,"5300","M'/M.S.L.","5300","RC10 f 305 mm","305","","clear","Sheet 2 of 2; full figure 5300; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc84029__bc84029_1.jpg",FALSE,"bc84029","84-029","1-201",1,201,"21000","M'/M.S.L.","21000","Rc 10 f 304 mm","304","","clear","Sheet 1 of 3; E & N Lands 60AH/84; full figure 21000 repeated on each strip; interpreted as whole feet: this sheet writes full figures (fly#72 merge)" +"bc7223__bc7223_1.jpg",FALSE,"bc7223","Delta 56 / B.C. 7223","54-107",54,107,"","TRUE HEIGHT (M'/M.S.L.)","","ZEISS camera no. 2, 12""","305","","clear","Sheet 3 of 3; strip 2 interior: START row (53) reads 4.1 and END row (108) reads 3.2, so the height changes along the strip and no height is written for these frames; added at merge (fly#72 code-check) so they read as covered but unread, not as uncovered" +"bc78009__bc78009_2.jpg",FALSE,"bc78009","B.C. 78-009","84-99",84,99,"","True Height (M'/M.S.L.)","","camera #124223, f 153 mm","153","","clear","Sheet 2 of 3; strip 2 interior: START row (83) ditto of 2.60 and END row (100) reads 2.70, so no height is written for these frames; added at merge (fly#72 code-check) so they read as covered but unread, not as uncovered" diff --git a/data-raw/height_calibrate-lower_tail_rolls.R b/data-raw/height_calibrate-lower_tail_rolls.R index 0a3c82a..d44f42a 100644 --- a/data-raw/height_calibrate-lower_tail_rolls.R +++ b/data-raw/height_calibrate-lower_tail_rolls.R @@ -10,6 +10,11 @@ # and which a divisor of 10 lands in the band just as well. Both tails go through one # function, `settle()`, and into the same two tables, with a `tail` column saying which. # +# fly#72 adds a third set: the `near_upper` frames beyond the band, the r ~ 2 mass the note +# once called a 305 mm lens catalogued as 153. Spacing says that is true of about half; on +# the other half the catalogued height is the one flown and `scale` is the wrong field. The +# logbooks are read against it the same way, with 1 the only factor named. +# # Everything here is public. It reads what `height_calibrate-flying_height_slip.R` cached # (`data-raw/.cache/centroids/`, every frame's x/y/frame_number) and shipped # (`inst/extdata/flying_height_sweep.csv`, terrain under the sampled frames) — run that @@ -133,6 +138,12 @@ message(sprintf("frames: nominal fits %d, reported fits %d, neither %d", sum(mis_rolls$n[mis_rolls$fits == "nominal"]), sum(mis_rolls$n[mis_rolls$fits == "reported"]), sum(mis_rolls$n[mis_rolls$fits == "neither"]))) +# fly#72 was opened on this split, and the verdict below is read against it: the spacing must +# still divide these 209 frames the way the issue reports before any logbook is consulted. +split_n <- tapply(mis_rolls$n, mis_rolls$fits, sum) +if (!identical(as.integer(split_n[c("nominal", "reported", "neither")]), c(90L, 105L, 14L))) { + stop("near_upper spacing split no longer reproduces fly#72's 90 / 105 / 14") +} # --------------------------------------------------------------------------- # Stage 3 — the lower tail, per roll @@ -218,17 +229,37 @@ fetch_logbooks <- function(rolls) { length(rolls), sum(status %in% 200L), sum(!status %in% 200L))) invisible(u) } -# Fetched only when the cache is absent; the fly#71 pages were added by the same function -# for the slipped rolls. Delete the directory to refetch everything. -if (!dir.exists(LOG_DIR)) { - fetch_logbooks(unique(c(rolls$film_roll, s$film_roll[s$set == "upper_tail"]))) -} +# Fetched for every roll with no page in the cache: the lower tail (fly#60), #54's slipped +# frames (fly#71) and the near_upper frames beyond the band (fly#72). A roll the catalogue +# links no page for is queried again on each run, which costs one request and changes +# nothing. Delete the directory to refetch everything. +want <- unique(c(rolls$film_roll, s$film_roll[s$set == "upper_tail"], + s$film_roll[s$set == "near_upper" & is.finite(s$r) & s$r > band[2]])) +cached <- if (dir.exists(LOG_DIR)) unique(sub("__.*", "", list.files(LOG_DIR))) else character() +if (length(setdiff(want, cached))) fetch_logbooks(setdiff(want, cached)) logs <- read.csv("data-raw/flying_height_logbooks.csv", colClasses = "character") logs$frame_from <- as.integer(logs$frame_from) logs$frame_to <- as.integer(logs$frame_to) logs$log_ft <- as.numeric(logs$height_ft_interpreted) logs$log_focal <- as.numeric(logs$focal_mm) +# A page's photo scale, read only where the field STARTS with it ("1:15,000 (project line)", +# "SCALE: 1:52,000 (title)", "(1:15,000) in project line"): a scale quoted inside a remark +# ("not requested at 1:40,000") is not the one flown, and would veto a row by accident. +# One thousands style per figure — commas, spaces, or none, in groups of three — and no digit +# may follow it, however spaced: "1:15,000 123" or "1:15840 12 frames" is read as no scale +# at all rather than swallowed into 15,000,123 or cut short. A missed scale only withholds a +# veto; a misread one could fire it. +scale_pat <- paste0( + "^\\s*\\(?\\s*(?:scale\\s*:\\s*)?1\\s*:\\s*", + "([0-9]{1,3}(?:,[0-9]{3})+|[0-9]{1,3}(?: [0-9]{3})+|[0-9]+)", + "(?![0-9]|,[0-9]|\\s+[0-9])" +) +scale_hit <- regmatches(logs$scale_as_written, + regexec(scale_pat, logs$scale_as_written, perl = TRUE, ignore.case = TRUE)) +logs$log_scale <- vapply(scale_hit, function(m) { + if (length(m) < 2) NA_real_ else as.numeric(gsub("[, ]", "", m[2])) +}, numeric(1)) # --- Controls: rolls whose answer is known, read blind ------------------------- # The two clean rolls must read back exactly as catalogued. bc78065 is a #54 slipped roll. @@ -269,6 +300,7 @@ settle <- function(set, named, tail) { stopifnot(nrow(lt) == nrow(set)) lt$log_ft <- NA_real_ lt$log_focal <- NA_real_ + lt$log_scale <- NA_real_ # What the logbook says about each frame, as one of four states — never folded into "no # height", which is how an earlier version reported frames under a conflict or an unread # row as frames no logbook page covered (code-check round 3): @@ -288,6 +320,10 @@ settle <- function(set, named, tail) { lt$log_ft[i] <- logs$log_ft[hit[1]] foc <- unique(logs$log_focal[hit][is.finite(logs$log_focal[hit])]) if (length(foc) == 1) lt$log_focal[i] <- foc + # The photo scale where the page writes one ("1:15,840", "SCALE: 1:31,680"): the one + # field that states the value fly#72 disputes. Read as the digits after "1:". + sc <- unique(logs$log_scale[hit][is.finite(logs$log_scale[hit])]) + if (length(sc) == 1) lt$log_scale[i] <- sc } else if (length(hit)) { lt$log_state[i] <- "conflict" } else if (length(cover) || lt$film_roll[i] %in% unparsed_roll) { @@ -336,6 +372,11 @@ settle <- function(set, named, tail) { # Only a LEGIBLE focal length that differs counts against the row; a blank or unread # one says nothing either way. n_focal_conflict = sum(is.finite(d$log_focal) & !d$focal_agrees), + # A legible logbook scale, against the catalogue's and against twice it (fly#72). + n_scale_read = sum(is.finite(d$log_scale)), + n_scale_same = sum(is.finite(d$log_scale) & abs(d$log_scale / d$scale_n - 1) <= 0.02), + n_scale_double = sum(is.finite(d$log_scale) & abs(d$log_scale / (2 * d$scale_n) - 1) <= 0.02), + p_nominal = median(d$p_nominal, na.rm = TRUE), n_base = sum(is.finite(d$base[agree])), p_corrected = if (any(agree)) median(1 - d$base[agree] / (FORMAT_M * (h_true - d$elev[agree]) / d$f_m[agree]), na.rm = TRUE) else NA_real_, @@ -349,11 +390,24 @@ settle <- function(set, named, tail) { list(verdict = verdict, frames = lt) } +# The near_upper frames beyond the band (fly#72): the r ~ 2 mass the note called a 305 mm lens +# catalogued as 153, which spacing splits in two. Only a factor of 1 is named: the question is +# whether the crew flew the catalogued height, in which case the SCALE is the wrong field and +# the nominal fallback draws the frame at half width. A logbook naming a 12" lens is the other +# half, and condition 2 below already excludes it as a different defect. The sweep holds a +# 600-frame SAMPLE of this stratum (2 < r above sea level <= 3), not a census, so a roll-height +# no sampled frame sits on is unmeasured here. +near <- s[s$set == "near_upper" & is.finite(s$r) & s$r > band[2], ] +message(sprintf("\n== near_upper beyond the band: %d frames on %d rolls ==", nrow(near), + length(unique(near$film_roll)))) + lower_v <- settle(lower, named = c(1, 10, 100), tail = "lower") upper_v <- settle(upper, named = c(1 / 10, 1 / K), tail = "upper") +near_v <- settle(near, named = 1, tail = "near_upper") saveRDS(list(verdict = lower_v$verdict, frames = lower_v$frames), "data-raw/.cache/lower_tail_verdict.rds") saveRDS(upper_v, "data-raw/.cache/upper_tail_verdict.rds") +saveRDS(near_v, "data-raw/.cache/near_upper_verdict.rds") # The control fly#60 read blind must now settle as a slipped roll, at x0.1. ctl78 <- upper_v$verdict[upper_v$verdict$film_roll == "bc78065", ] @@ -377,18 +431,34 @@ if (!(nrow(ctl78) == 1 && isTRUE(all.equal(ctl78$factor, 0.1)))) { # SCALE is the wrong field, and the fallback to nominal scale is what draws those frames # wrong. Among the slipped frames, spacing cannot tell 1/10 from 1/10.764 (7.6% apart), so # there condition 3 is a sanity check and the logbook alone decides the factor. +# +# The near_upper frames (fly#72) go through the same rule unchanged, with 1 the only factor +# named. There condition 3 is the discriminating witness rather than a sanity check: the two +# readings on offer, the catalogued height and nominal scale, are a factor of ~2 apart, which +# spacing separates. A factor-1 row there is the upper-side mirror of the lower tail's +# `scale_wrong`: the same cause, from the other side of the band. -v <- rbind(lower_v$verdict, upper_v$verdict) +v <- rbind(lower_v$verdict, upper_v$verdict, near_v$verdict) v$covered <- v$n_logbook / v$n v$agreeing <- ifelse(v$n_logbook > 0, v$n_agree / v$n_logbook, 0) v$focal_conflict <- v$n_focal_conflict > 0 v$spacing_ok <- fits(v$p_corrected) +# near_upper (fly#72): the logbook height cannot tell the two readings apart — a 305 mm lens +# catalogued as 153 ALSO flew the catalogued height — so factor 1 agrees on lens rolls too. +# What separates them is the spacing, and there it must do both halves: fit the reported +# height AND reject nominal scale, the same test the issue's split was made with. Fitting +# alone is not enough: on a lens roll the reported height implies ~0.80 overlap, and the +# window's top is 0.78. And a legible logbook scale equal to the catalogue's vetoes the row, +# since `scale` is the field the row says is wrong. +nu_row <- v$tail == "near_upper" +v$spacing_ok[nu_row] <- v$spacing_ok[nu_row] & !fits(v$p_nominal[nu_row]) +v$scale_veto <- nu_row & v$n_scale_same > 0 is_f <- function(f) is.finite(v$factor) & abs(v$factor - f) < 1e-9 # A slipped roll-height whose logbook names 1/10.764 is not tabled: #54's repair already # sizes it from the same height, and tabling it would relabel frames without moving them. v$confirms_54 <- v$tail == "upper" & is_f(1 / K) v$accept <- is.finite(v$factor) & v$covered >= 0.5 & v$agreeing >= 0.9 & - !v$focal_conflict & v$spacing_ok & !v$confirms_54 + !v$focal_conflict & v$spacing_ok & !v$confirms_54 & !v$scale_veto # Where the logbook read under half the frames, the reason names the state most of the # unread frames are in — the predicate that actually fired, not the one next to it. unread_state <- ifelse(v$n_conflict >= v$n_uninterpreted & v$n_conflict > 0, "conflict", @@ -401,7 +471,12 @@ v$reason <- dplyr::case_when( v$n_logbook == 0 ~ "no logbook page covers these frames", v$covered < 0.5 ~ "logbook covers under half the frames", !is.finite(v$factor) | v$agreeing < 0.9 ~ "logbook height is not a named multiple of the catalogue's", + v$focal_conflict & nu_row & fits(v$p_corrected) & !fits(v$p_nominal) ~ + "logbook names a different lens and spacing fits the reported height; the two disagree", v$focal_conflict ~ "logbook names a different lens; nominal scale already sizes it", + v$scale_veto ~ "logbook writes the catalogue's scale", + nu_row & fits(v$p_corrected) & fits(v$p_nominal) ~ + "spacing fits both the reported height and nominal scale", v$n_base == 0 ~ "no adjacent frames to measure spacing on", TRUE ~ "spacing rejects the logbook's height" ) @@ -527,6 +602,12 @@ v$sibling_frame <- NA_real_ v$sibling_reason <- NA_character_ v$sibling_rel <- NA_character_ for (i in which(!v$accept)) { + # A near_upper roll-height's height is not what is in dispute — its scale is — so there is + # no relation between heights for a neighbour to name. + if (v$tail[i] == "near_upper") { + v$sibling_reason[i] <- "not applied: the scale is disputed, not the height" + next + } sb <- sibling(i) v$sibling_reason[i] <- sb$reason if (!sb$ok) next @@ -573,6 +654,9 @@ if (!c2_ok) { # the table is not read as a list of frames drawn at nominal scale. up <- v$tail == "upper" & !v$accept & v$reason != "logbook confirms #54's 10.764" v$reason[up] <- paste0(v$reason[up], "; #54's 10.764 still applies") +# Likewise a near_upper roll-height left out is drawn at nominal scale, as it was before. +nu <- v$tail == "near_upper" & !v$accept & !grepl("nominal scale", v$reason, fixed = TRUE) +v$reason[nu] <- paste0(v$reason[nu], "; nominal scale still applies") v$cause <- dplyr::case_when( !v$accept ~ NA_character_, is_f(1) ~ "scale_wrong", @@ -603,7 +687,7 @@ excluded_out <- excluded_out[order(excluded_out$tail, excluded_out$film_roll, excluded_out$flying_height), ] stopifnot(nrow(rolls_out) + nrow(excluded_out) == nrow(v), sum(rolls_out$frames_measured) + sum(excluded_out$frames_measured) == - nrow(lower) + nrow(upper)) + nrow(lower) + nrow(upper) + nrow(near)) message(sprintf("\n== verdict: %d roll-heights corrected (%d frames), %d excluded (%d frames) ==", nrow(rolls_out), sum(rolls_out$frames_measured), @@ -625,7 +709,8 @@ reach <- vapply(seq_len(nrow(rolls_out)), function(i) { stopifnot(all(reach >= rolls_out$frames_measured)) message(sprintf("frames the corrections reach in the whole catalogue: %d (measured: %d)", sum(reach), sum(rolls_out$frames_measured))) -# The two tails cannot share a key: a height cannot be both under and over the band. +# The tails cannot share a key: a height cannot be both under and over the band, and a +# near_upper roll-height (2 < r above sea level <= 3) cannot be a slipped one (> 3). stopifnot(!anyDuplicated(key4(v$film_roll, v$flying_height, v$focal_length, v$scale_n))) write.csv(rolls_out, "inst/extdata/flying_height_rolls.csv", row.names = FALSE, na = "") diff --git a/inst/extdata/flying_height_rolls.csv b/inst/extdata/flying_height_rolls.csv index 21dfa4e..67f4ece 100644 --- a/inst/extdata/flying_height_rolls.csv +++ b/inst/extdata/flying_height_rolls.csv @@ -24,6 +24,30 @@ "lower","bc87070",396,153,23000,10,"height_digit_dropped","sibling",,203,3962,34,,0.597,0.795 "lower","bc88054",655,305,15000,10,"height_digit_dropped","logbook",21500,,6553.2,17,17,0.602,1.072 "lower","bcc822",701,305,15000,10,"height_digit_dropped","sibling",,119,7010,11,,0.613,1.098 +"near_upper","bc5138",5486,153,16000,1,"scale_wrong","logbook",18000,,5486.4,7,7,0.723,2.219 +"near_upper","bc5494",6400,153,16000,1,"scale_wrong","logbook",21000,,6400.8,1,1,0.595,2.05 +"near_upper","bc5507",4267,153,12000,1,"scale_wrong","logbook",14000,,4267.2,1,1,0.618,1.907 +"near_upper","bc5508",6553,153,16000,1,"scale_wrong","logbook",21500,,6553.2,13,13,0.58,2.063 +"near_upper","bc5509",6553,153,16000,1,"scale_wrong","logbook",21500,,6553.2,17,17,0.576,2.153 +"near_upper","bc5510",6553,153,16000,1,"scale_wrong","logbook",21500,,6553.2,3,3,0.589,2.095 +"near_upper","bc5642",1859,153,6000,1,"scale_wrong","logbook",6100,,1859.3,6,6,0.627,2.001 +"near_upper","bc5644",1859,153,6000,1,"scale_wrong","logbook",6100,,1859.3,6,6,0.6,1.912 +"near_upper","bc5645",1859,153,6000,1,"scale_wrong","logbook",6100,,1859.3,2,2,0.613,1.929 +"near_upper","bc5648",1859,153,6000,1,"scale_wrong","logbook",6100,,1859.3,6,6,0.625,2.021 +"near_upper","bc5650",1859,153,6000,1,"scale_wrong","logbook",6100,,1859.3,1,1,0.593,2.025 +"near_upper","bc5665",4115,153,12000,1,"scale_wrong","logbook",13500,,4114.8,1,1,0.668,2.143 +"near_upper","bc5689",1859,153,6000,1,"scale_wrong","logbook",6100,,1859.3,3,3,0.614,1.95 +"near_upper","bc5699",1875,153,6000,1,"scale_wrong","logbook",6150,,1874.5,8,8,0.61,2.025 +"near_upper","bc5701",1875,153,6000,1,"scale_wrong","logbook",6150,,1874.5,15,15,0.602,1.979 +"near_upper","bc5702",1875,153,6000,1,"scale_wrong","logbook",6150,,1874.5,8,8,0.598,2.027 +"near_upper","bc5709",3024,153,7920,1,"scale_wrong","logbook",9920,,3023.6,3,3,0.597,2.043 +"near_upper","bc7692",4450,305,7200,1,"scale_wrong","logbook",14600,,4450.1,1,1,0.628,1.651 +"near_upper","bc78110",1554,153,5000,1,"scale_wrong","logbook",5100,,1554.5,1,1,0.632,1.984 +"near_upper","bc79039",777,153,2000,1,"scale_wrong","logbook",2550,,777.2,1,1,0.688,2.527 +"near_upper","bc79141",8230,153,25000,1,"scale_wrong","logbook",27000,,8229.6,4,4,0.682,1.877 +"near_upper","bc85079",6401,305,10000,1,"scale_wrong","logbook",21000,,6400.8,2,2,0.632,1.686 +"near_upper","bc85080",6401,305,10000,1,"scale_wrong","logbook",21000,,6400.8,7,7,0.618,1.815 +"near_upper","bc85081",6401,305,10000,1,"scale_wrong","logbook",21000,,6400.8,3,3,0.581,1.68 "upper","bc5596",26212,153,12000,0.1,"height_decimal_dropped","sibling",,203,2621,8,,0.619,0.957 "upper","bc78065",4115,153,2000,0.1,"height_decimal_dropped","logbook",1350,,411.5,24,24,0.594,0.975 "upper","bc78078",9449,153,6000,0.1,"height_decimal_dropped","logbook",3100,,944.9,15,15,0.622,0.957 diff --git a/inst/extdata/flying_height_rolls_excluded.csv b/inst/extdata/flying_height_rolls_excluded.csv index 43de361..c75d109 100644 --- a/inst/extdata/flying_height_rolls_excluded.csv +++ b/inst/extdata/flying_height_rolls_excluded.csv @@ -51,6 +51,40 @@ "lower","bcc162",2438,305,16000,86,"logbook names a different lens; nominal scale already sizes it","no adjacent frame at another height" "lower","bcc401",1219,305,10000,12,"logbook height is not a named multiple of the catalogue's","no adjacent frame in an exact named relation" "lower","bcc94147",5511,305,52000,1,"logbook height is not a named multiple of the catalogue's","no adjacent frame at another height" +"near_upper","bc5667",1890,153,6000,2,"spacing rejects the logbook's height; nominal scale still applies","not applied: the scale is disputed, not the height" +"near_upper","bc7223",1249,305,2000,5,"logbook height or frame range not read; nominal scale still applies","not applied: the scale is disputed, not the height" +"near_upper","bc7407",5029,153,15000,6,"spacing rejects the logbook's height; nominal scale still applies","not applied: the scale is disputed, not the height" +"near_upper","bc7454",2438,153,6000,3,"spacing rejects the logbook's height; nominal scale still applies","not applied: the scale is disputed, not the height" +"near_upper","bc77013",7315,153,20000,2,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc77115",7620,153,20000,2,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc7716",3810,305,5000,5,"spacing rejects the logbook's height; nominal scale still applies","not applied: the scale is disputed, not the height" +"near_upper","bc78009",1829,153,5000,1,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc78009",2134,153,5000,2,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc78009",2286,153,5000,5,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc78028",6919,153,20000,1,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc78051",6858,153,20000,13,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc78101",7315,153,20000,2,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc78153",7468,153,20000,5,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc79026",7468,153,20000,2,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc79026",7620,153,20000,3,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc79029",1676,153,5000,4,"no logbook page covers these frames; nominal scale still applies","not applied: the scale is disputed, not the height" +"near_upper","bc79043",1676,153,5000,2,"logbook names a different lens and spacing fits the reported height; the two disagree; nominal scale still applies","not applied: the scale is disputed, not the height" +"near_upper","bc79058",7468,153,20000,2,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc79058",7620,153,20000,2,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc79072",7925,153,20000,9,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc79075",5029,88,25000,3,"spacing rejects the logbook's height; nominal scale still applies","not applied: the scale is disputed, not the height" +"near_upper","bc79127",5895,153,15840,5,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc79131",5590,153,16000,5,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc79195",7010,153,20000,6,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc80005",1585,153,5000,1,"spacing rejects the logbook's height; nominal scale still applies","not applied: the scale is disputed, not the height" +"near_upper","bc80006",1585,153,5000,2,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc80018",6401,153,20000,3,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc80048",6553,153,20000,11,"spacing rejects the logbook's height; nominal scale still applies","not applied: the scale is disputed, not the height" +"near_upper","bc80122",4115,153,10000,8,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc80122",7925,153,20000,2,"logbook names a different lens; nominal scale already sizes it","not applied: the scale is disputed, not the height" +"near_upper","bc81012",7925,153,20000,6,"logbook height is not a named multiple of the catalogue's; nominal scale still applies","not applied: the scale is disputed, not the height" +"near_upper","bc84029",6401,305,10000,1,"logbook height is not a named multiple of the catalogue's; nominal scale still applies","not applied: the scale is disputed, not the height" +"near_upper","bcc544",6096,305,8000,1,"logbook height is not a named multiple of the catalogue's; nominal scale still applies","not applied: the scale is disputed, not the height" "upper","bc79027",19995,153,13000,10,"spacing rejects the logbook's height; #54's 10.764 still applies","no adjacent frame in an exact named relation" "upper","bcc03004",71640,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies","no adjacent frame in band" "upper","bcc03004",71641,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies","no adjacent frame in band" diff --git a/inst/notes/terrain-correction.md b/inst/notes/terrain-correction.md index 0583afc..37ac5da 100644 --- a/inst/notes/terrain-correction.md +++ b/inst/notes/terrain-correction.md @@ -231,13 +231,13 @@ Four things here were measured and each is load-bearing: inside the band have a median of 1.05 and run 0.83–1.51 (5–95%). The next mass out is centred on **r = 2, and of the 223 frames sampled beyond r 1.8 in that stratum, 209 are catalogued - at 153 mm**: a 305 mm lens recorded as a 153. The DEM route draws those at twice their - true width; the nominal route gets them right, because it never reads `focal_length`. - Falling back is therefore the correct answer there and not merely the cautious one. - **Only for about half of them** — fly#60 measured the spacing between adjacent frames, and - 105 of the 209 frames in that mass fit their *reported* height, with a scale recorded at - half its denominator, so the fallback draws those at half width (fly#72). 91 of the 105 are - on 1972–76 rolls, but the split is per roll, not by year. The + at 153 mm**. This was first read as a 305 mm lens recorded as a 153 — which the DEM route + draws at twice its true width and the nominal route gets right — and so falling back was + called the correct answer there rather than merely the cautious one. **That is true of + about half of it.** Spacing and the logbooks split the mass into two defects that read + alike from `r` (fly#72, below): a wrong lens, which the fallback sizes correctly, and a + right height beside a `scale` recorded at half its denominator, which the fallback drew at + half width and the roll table now corrects. The lower edge is the same factor inverted, since the error is a ratio either way — set by symmetry, not by a trough: ordinary frames thin out steadily below 0.8 and there is no second mass at 0.5. @@ -486,6 +486,78 @@ not exact to the metre, nothing here chooses between them. Reproduce with `data-raw/height_calibrate-lower_tail_rolls.R` (Stage 5b), which prints every figure in this section and stops unless both controls return their neighbours' heights. +## The r ≈ 2 mass is two defects, settled per roll (fly#72) + +**Verified:** 2026-09-27 · **Issues:** fly#72 (from fly#54, fly#60) · **Produced by:** +`data-raw/height_calibrate-lower_tail_rolls.R`, set `near_upper` + +fly#54 set the band's upper edge in a trough before a mass at r = 2 and called the mass a +305 mm lens catalogued as 153. fly#60's spacing split it instead: of the 209 sampled frames +beyond r 1.8 at 153 mm, 90 give the designed ~60% forward overlap under nominal scale (the +lens reading), 105 give it under the **reported** height with nominal implying ~20%, and 14 +fit neither. The generator stops if its spacing no longer reproduces that 90 / 105 / 14. + +The same two witnesses fly#60 used then settle it per roll-height, over every sampled frame +beyond the band (252 frames, 53 rolls, 58 roll-heights, all lenses). New logbook pages were +transcribed blind — readers given only the images, one control sheet each, all three read +back as catalogued — into `data-raw/flying_height_logbooks.csv`. One thing changed in how: +`focal_mm` is filled only where a focal length is **written**, never inferred from a camera +model, because on this set the lens is the question. + +**The logbook height cannot separate the two readings.** A lens catalogued wrong and a scale +catalogued wrong both predict the crew flew the catalogued height, so factor 1 "agrees" on +both. What separates them is a legible lens and the spacing. So for `near_upper` the rule, +fixed before the run, adds one condition to fly#60's: spacing must fit the reported height +**and reject nominal scale** — fitting alone would not do, since a lens roll's reported +height implies ~0.80 overlap and the window's top is 0.78. A legible logbook scale equal to +the catalogue's would veto a row; no sampled page writes a scale, so it never fired. + +The result: + +| outcome | roll-heights | frames | +|---|---|---| +| tabled, factor 1, `scale_wrong` | 24 | 120 | +| excluded: logbook writes a 12" lens | 21 | 82 | +| excluded: spacing rejects the reported height | 7 | 31 | +| excluded: logbook lens and spacing disagree | 1 | 2 | +| excluded: logbook height not the catalogue's | 3 | 8 | +| excluded: logbook height not read | 1 | 5 | +| excluded: no logbook page | 1 | 4 | + +- The 24 are 20 roll-heights at 153 mm (107 frames, sixteen of them 1972–76 `bc54xx`–`bc57xx`, + plus `bc5138`, `bc78110`, `bc79039`, `bc79141`) and 4 at 305 mm (13 frames: `bc7692`, + `bc85079`, `bc85080`, `bc85081`). Three of the 305 mm ones sit at r 1.65–1.69, not 2: the + scale is the wrong field there too, but not by a factor of two, so `scale_wrong` names the + field and not the ratio. +- Their keys reach **3,227** catalogue frames. That is an upper bound on what moves: + terrain *lowers* r below its above-sea-level value, and the table is consulted only + outside the band, so a frame on a tabled key over high enough ground (287–2,636 m, + depending on the roll) is inside it and was already sized from its reported height — + the same height the table gives it, so nothing changes there. In the sample, 120 of the + 132 frames on the tabled keys are beyond the band; the 12 inside are all on `bc85079`, + `bc85080` and `bc85081`. The frames beyond it were drawn by the fallback at 1/r of their + width — half, at r = 2; they are now drawn from the height the crew flew. +- The 82 frames whose logbooks write a 12" lens are the lens reading with a witness spacing + never saw — the issue's lens rolls `bc78051`, `bc79072` and `bc80122` among them. Nominal + scale remains right for them. +- `bc80048` fits neither story: its logbook writes 6", and spacing fits neither reading — + 0.79 under nominal and 0.89 under the reported height, both above the 0.78 top of the + window. It stays on nominal because nothing here justifies moving it, not because the + spacing supports nominal. + `bc79043` is the reverse — 12" written, spacing fitting the reported height — and is + excluded with a reason saying the witnesses disagree. + +**Bound.** `near_upper` is a 600-frame sample of 2 < r above sea level ≤ 3, not a census. +Not measured: roll-heights no sampled frame sits on; the 505 `upper_tail` census frames that +#54 does not repair; and frames just under the stratum's floor. The largest known group is +there: **1,534 frames on nine 1985 rolls** (`bc85063`–`bc85091`) at 6,096 m, 305 mm and +1:10000, whose ratio above sea level is 1.999 against the sample's lowest 2.025. Two of them +turned up among the random draws beyond the band (`bc85083` r 1.73, `bc85090` r 1.63), their +neighbours `bc85080` and `bc85081` log 20,000 ft in the pages read here, and the tabled +`bc85079`–`bc85081` are the same camera, lens and scale — so they are likely the same +`scale_wrong`, and unmeasured. A third random frame, `bc5703` (1:6000, 153 mm, r 1.99), looks +like the 1972–76 group. An unlisted roll-height stays on nominal scale. + ## What a partially covered footprint costs (fly#58) A footprint hanging off the edge of the DEM is sized from the mean of the part that is diff --git a/man/fly_footprint.Rd b/man/fly_footprint.Rd index 54fb1e7..67d8a56 100644 --- a/man/fly_footprint.Rd +++ b/man/fly_footprint.Rd @@ -227,16 +227,20 @@ elevation} the roll-heights in \code{inst/extdata/flying_height_rolls.csv}, which the province's flight logbooks, or failing them an adjacent frame on the same roll in an exact named relation, together with the spacing between adjacent -frames, settled (fly#60, fly#71, fly#74): a height with one or two digits dropped (a logbook "20.0", +frames, settled (fly#60, fly#71, fly#72, fly#74): a height with one or two digits dropped (a logbook "20.0", thousands of feet, catalogued as 2,000 ft), a height recorded ten times too -large, a leading digit added, or a correct height beside a wrong \code{scale}. Checked before the +large, a leading digit added, or a correct height beside a wrong \code{scale} — +on either side of the band, including frames around twice their nominal +scale whose \code{scale} is recorded at half its denominator. Checked before the 10.76 slip above. Sized from the measured height; matched on \code{film_roll}, \code{flying_height}, \code{focal_length} and \code{scale} together, so it needs a \code{film_roll} column. As with the slip, \code{flying_height} is not overwritten} \item{\code{"implausible"}}{they disagree some other way, or \code{flying_height} is above 16,000 m or below the terrain. Nothing can say whether the height, the scale or the focal length is the wrong one — a 305 mm lens catalogued as -153 looks the same from here — so the height is not used: a film frame +153 and a scale recorded at half its denominator look the same from here, +and the roll table separates them only where it was measured — so the +height is not used: a film frame falls back to nominal scale, and a digital frame with no other route has no footprint} \item{\code{NA}}{no height was judged: no \code{dem}, a frame the DEM route does not @@ -248,7 +252,8 @@ all for another reason), one the DEM does not cover, or \code{flying_height} or Measured over the whole catalogue, the two corrections for a height that is too large apply to those 1,589 frames and to nothing else: 308 by the roll table, -the rest by 10.76. A digital frame's \code{scale} is a nominal figure, not +the rest by 10.76. (The table's factor-1 rows correct a scale, not a height, so +they are not among them.) A digital frame's \code{scale} is a nominal figure, not its image scale, so it is not compared: only the 16,000 m ceiling, or terrain at or above the aircraft, can refuse one. To list the frames worth a second look: \code{dplyr::filter(fp, height_source != "reported")}. diff --git a/planning/archive/2026-09-issue-72-near-upper-scale-wrong/README.md b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/README.md new file mode 100644 index 0000000..6d435be --- /dev/null +++ b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/README.md @@ -0,0 +1,42 @@ +## Outcome + +fly#54 had called the film mass at r ≈ 2 a 305 mm lens catalogued as 153, and so treated its +nominal-scale fallback as correct. fly#60's spacing split that mass in half, and this issue +settled it per roll-height. 144 logbook pages were fetched for the 53 rolls beyond the band in +the sweep's `near_upper` sample, and three blind readers transcribed them, one control sheet +each; all three controls read back as catalogued. The generator then ran fly#60's two +witnesses under a new `tail = "near_upper"`. The main lesson came from the plan review: **the +logbook height cannot tell the two defects apart**, because a wrong lens and a wrong scale both +mean the crew flew the catalogued height. So for this tail, spacing must *reject* nominal +scale, not just fit the reported height. That rule was fixed before the run. Code-check took +three rounds. Every finding was the same mechanism: a claim written from a mental picture of +the data, not recomputed from it. The loop ended when round 3 enumerated all 43 claims in the +new prose. + +## Measurement + +- 252 sampled frames beyond the band, on 53 rolls and 58 roll-heights. The spacing split + reproduces the issue's 90 / 105 / 14. +- **24 roll-heights (120 frames) tabled at factor 1, `scale_wrong`**: 20 at 153 mm, mostly on + 1972–76 rolls, and 4 at 305 mm. Their keys reach at most 3,227 catalogue frames. These frames + were drawn at 1/r of their width, and are now drawn from their height. +- **21 roll-heights (82 frames) have a logbook that writes a 12" lens.** That confirms the lens + reading, with a witness spacing never saw, and those frames stay on nominal scale. +- Wrong turns, kept: + - I claimed the reach was out of band "whatever the terrain". It is not: terrain lowers r, + and 12 of the 132 sampled frames are in band. + - The note said bc80048's spacing "supports nominal". It fits neither reading. + - bc7223 first shipped as "no page", when its page covers it with a height that changes + along the strip. + - The scale parser read remark scales, then swallowed trailing digits. +- Bound: this was a sample, not a census. 1,534 frames on nine 1985 rolls at 6,096 m, 305 mm, + 1:10000 fall just under the stratum floor (1.999 against 2.025) and are unmeasured. + +## Evidence + +`data-raw/height_calibrate-lower_tail_rolls.R` (set `near_upper`); +`data-raw/flying_height_logbooks.csv` (rows after line 215); the note section +"The r ≈ 2 mass is two defects" in `inst/notes/terrain-correction.md`; the plan review and the +code-check rounds are `review-*.md` in this directory. + +Closed by: PR (fly#72) diff --git a/planning/archive/2026-09-issue-72-near-upper-scale-wrong/findings.md b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/findings.md new file mode 100644 index 0000000..531bf50 --- /dev/null +++ b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/findings.md @@ -0,0 +1,118 @@ +# Findings — Half the r≈2 mass is a wrong scale (#72) + +## Issue context + +## Problem + +`inst/notes/terrain-correction.md` and the comment on `fly_height_ratio_band()` say the mass of +film frames beyond r ≈ 2 is a 305 mm lens catalogued as 153 mm, so falling back to nominal +scale is "the correct answer there and not merely the cautious one". Measured while settling +fly#60 by the spacing between frames adjacent by frame number, **that is true for about half +of them**. + +For the 209 `near_upper` frames beyond r 1.8 catalogued at 153 mm, split per roll-height: + +- **90 frames** (mostly 1978–81: bc78051, bc79072, bc80048, bc80122, …) give the designed ~60% + forward overlap under nominal scale and ~80% under the reported height. 14 fit neither. That is the lens + reading, and the fallback is right. +- **105 of the 209 frames** give ~60% under the **reported** height and ~20% under nominal. + 91 of those are on 1972–76 bc5xxx rolls (bc5701, bc5509, bc5508, bc5699, bc5702, …); 14 are + not (bc5138 1965, bc79141, bc79043, bc79039, bc78110), and two 1972–76 rolls do not fit this + way (bc7407 fits nominal, bc7454 neither). Key it per roll, not by year. The height and lens are right and + `scale` is the wrong field, recorded at half its true denominator. The fallback draws these + frames at **half their true width**, and nothing on the row says so. + +The instrument was checked on known answers first: in-band random frames read 0.63 +[0.59–0.69], and #54's slipped frames read 0.62 after repair against 0.97 before. + +This is the upper-side mirror of fly#60's `scale_wrong` class, which fly#60 settles for the +lower tail with logbooks as the second witness. + +## What would settle it + +The same two instruments fly#60 used, per roll-height: the logbook height (it should equal the +catalogue's where the scale is the wrong field) and spacing. Rows that pass both go into +`flying_height_rolls.csv` with factor 1. That needs the table lookup to reach frames above the +band as well as below it; it already keys on the band edge, not on a direction. + +## Evidence + +`data-raw/height_calibrate-lower_tail_rolls.R` prints the per-roll split ("near_upper r > 1.8 +at 153 mm"). + + +What exploration found that shapes the work: +- `fly_footprint()` needs **no logic change**: `out_of_band` is two-sided and the factor-1 gate is + `tab_factor == 1 & r_reported > 0` (R/fly_footprint.R ~1166–1193). A near_upper frame is + `disputed`, not `slipped` (r/10.764 ≈ 0.19), so today it is `implausible` → nominal; tabled, it + becomes `corrected_roll_table` at the reported height. Only comments/roxygen change. +- **0 of the 44 rolls have logbook rows** in `data-raw/flying_height_logbooks.csv`, and + `fetch_logbooks()` runs only when the cache dir is absent. Transcription is the heavy phase. +- `settle()` already takes `(set, named, tail)`; spacing, the focal-conflict check (which is + exactly the lens reading: logbook says 305 → excluded "names a different lens; nominal scale + already sizes it") and the key-uniqueness guard carry over. Sibling witness (Stage 5b) relations + are height relations only; near_upper rows skip it with a stated sibling_reason. +- Test `the table holds against the sweep…` hard-codes `tail ∈ {lower, upper}` and "factor-1 row + ⇒ r < band[1]" — both widen. + + +## Logbooks for the near_upper rolls (Phase 1, 2026-09-27) + +- 53 rolls sit beyond the band in the near_upper sample (252 frames, 58 roll-heights: 224 at + 153 mm on 44 rolls, 25 at 305 mm, 3 at 88 mm). Every one links logbook pages; 144 fetched, + `bc80048_4.jpg` is a 404 upstream (retried once, same). +- Transcribed blind by three unnamed general-purpose readers given only image paths and + `scratchpad/transcribe_instructions.md`; one control sheet each. All three controls read + back as catalogued: bc7349 1-217 at 20.0, bcc228 1-166 at 12.650, bc78065 62-270 at 13.0. + Spot-checked bc85080_1 and bc5701_5702_1 against the images myself: exact. +- 183 rows appended (2 identical-content duplicates from a sheet cached under two rolls + dropped), existing 214 rows byte-untouched; 2 interior rows added after code-check round 2, + so 185 new rows, 142 distinct page files, 54 roll labels. 1984-86 sheets write whole feet ("21000"), not + thousands; the readers left those blank per instruction and the merge reads them as feet (18 + rows, noted in `note`). "2150" on bc78101_2 and bc5645_3 left uninterpreted, as the fly#60 + rows left "1,000" and "600". +- Instruction change against fly#60's rows: `focal_mm` filled only where a focal length is + WRITTEN, never inferred from a camera model (fly#60 rows put 153 beside "RC8 502"). On this + set the lens is the question, so a camera-inferred 153 would have been the answer smuggled in. +- No near_upper page writes a photo scale, so the A2 scale veto never fired (0 read). + +## Verdict (Phase 2) + +Rule fixed before the run, with the Plan review's A1 folded in: near_upper additionally needs +spacing to REJECT nominal, because the logbook height cannot separate a lens roll from a scale +roll. Controls all passed, and the spacing split still reproduces the issue's 90 / 105 / 14. + +- **24 roll-heights, 120 frames tabled at factor 1, `scale_wrong`** — 20 at 153 mm (107 frames, + mostly 1972-76 bc55xx-bc57xx) and 4 at 305 mm (13 frames: bc7692, bc85079/80/81). + r_corrected 1.65-2.53; the r ~ 2 mass is 21 of them (114 frames); 3 roll-heights (6 frames, + bc7692/bc85079/bc85081, 305 mm, r 1.65-1.69) are the 1.6-1.8 group the review flagged (A4) — + the scale is wrong there too, but not by 2. +- **Catalogue reach: 3,227 frames — an upper bound.** First written as "every one out of band + whatever the terrain"; wrong way round (code-check round 1): terrain LOWERS r, and 12 of the + 132 sampled frames on the tabled keys (bc85079/80/81) are in band, already `reported` at the + same height. Sizing unaffected; the claim was not. +- **34 roll-heights (132 frames) excluded, nominal scale still applies:** + - 21 (82 frames): the logbook writes a 12" lens — the lens reading, now with a witness the + spacing never saw. The issue's lens rolls bc78051, bc79072, bc80122 are all here. + - 7 (31 frames): spacing rejects the reported height (bc80048, bc7407, bc7454, bc7716, + bc79075, bc5667, bc80005). **bc80048's logbook says 6"** and spacing fits neither reading + (0.79 nominal, 0.89 reported, window top 0.78); left on nominal for want of a witness. + - 1 (2 frames): bc79043 — logbook writes 305 but spacing fits the reported height. The + witnesses disagree; excluded with that reason. + - 3 (8 frames): logbook height not factor 1 (bc81012, bcc544, bc84029); 1 (5 frames): height + not read (bc7223 — its strip's height changes from 4.1 to 3.2 along it; first shipped as "no + page" because only the two endpoint frames were transcribed, code-check round 2; an interior + row with no height was added for it and for bc78009 84-99); 1 (4 frames): no page (bc79029). +- lower/upper rows of both tables are byte-identical to main (diff adds lines only). + +## Bound + +The near_upper set is a 600-frame SAMPLE of 2 < r_asl <= 3. Not measured here: roll-heights no +sampled frame sits on; 505 upper_tail census frames that are not #54-slipped; 3 random-set frames +beyond the band (one, bc5703 1:6000 at 153 mm, r 1.99, looks like this class). + +## Errors Encountered + +| Error | Resolution | +|-------|------------| +| Rewriting the logbook CSV with `write.csv()` re-quoted all 214 existing rows | Reverted; append with `write.table(append = TRUE)` and typed logical/integer columns | diff --git a/planning/archive/2026-09-issue-72-near-upper-scale-wrong/progress.md b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/progress.md new file mode 100644 index 0000000..9e16737 --- /dev/null +++ b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/progress.md @@ -0,0 +1,15 @@ +# Progress — Half the r≈2 mass is a wrong scale (#72) + +## Session 2026-09-27 + +- Plan-mode exploration — phases approved by user (tail = `near_upper`; scope = sampled roll-heights) +- Created branch `72-near-upper-scale-wrong` off main +- Scaffolded PWF baseline from issue #72 with approved phases +- Next: start Phase 1 +- Plan review (Plan agent) → `review-plan.md`; A1 (spacing must reject nominal), A2 (scale veto), A3 folded into the rule before the run +- Phase 1: fetched 144 pages for 53 rolls; three blind readers transcribed 185 rows; merged 183 into `data-raw/flying_height_logbooks.csv` +- Phase 2: generator settles `near_upper`; 24 roll-heights / 120 frames tabled `scale_wrong`, 34 / 132 excluded; tables regenerated, lower/upper rows unchanged +- Phase 3: tests widened to three tails; near_upper frame sized from its height (bc5509), lens roll (bc78051) stays nominal, restore-the-bug via mocked table; r_corrected held to one unit in the third decimal (published height_m rounding) +- Phase 4: fly_footprint comments/roxygen, terrain-correction.md new fly#72 section, NEWS, CLAUDE.md Key Decisions +- /code-check: 3 rounds (review-round1..3.md). R1: reach claim backwards (terrain lowers r), scale parser read remark scales, test comment. R2: parser fix still swallowed trailing digits (inside a fix), bc80048 wording, bc7223 "no page" vs unread (interior rows added). R3: mechanism = claims written from a picture of the data, not recomputed; enumerated 43 claims, 2 false (NEWS row counts stale after R2 fix; Bound paragraph missed the 1,534-frame bc850xx family at r_asl 1.999). Fixed and verified directly; loop ended by enumeration +- Full suite before the round fixes: FAIL 0 | SKIP 0 | PASS 2537; roll tests after: 391 PASS diff --git a/planning/archive/2026-09-issue-72-near-upper-scale-wrong/review-plan.md b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/review-plan.md new file mode 100644 index 0000000..5bdf2b9 --- /dev/null +++ b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/review-plan.md @@ -0,0 +1,22 @@ +# Plan review — fly#72 (Plan agent, 2026-09-27) + +Verdict: no blocker. `fly_footprint()` already reaches a factor-1 row above the band +(`out_of_band` two-sided; gate `tab_factor == 1 & r_reported > 0`; table before #54's slip). + +| # | Finding | Disposition | +|---|---|---| +| G1 | Sibling witness would misfire on near_upper (falls into lower-tail branch, empty `d`) | Fixed: explicit skip, `sibling_reason` states why | +| G2 | Census stopifnot, suffix, key comment, rds saves hard-coded to two sets | Fixed | +| G3 | Doubled "nominal scale" in suffixed reason | Fixed: suffix only where the reason does not already say it | +| G4 | Tests assume two tails in several places | Phase 3 | +| O1 | Fetch already done | Noted | +| O2 | Spacing-split control is circular | Kept as a regression stop on the instrument; the independent check is the logbook lens on the issue's named rolls, reported in findings | +| A1 | Logbook height cannot separate lens from scale (both predict factor 1); spacing window top 0.78 vs lens-roll ~0.80 | Fixed in the rule BEFORE running: near_upper also requires spacing to reject nominal | +| A2 | `scale_as_written` is the only field stating the disputed value | Transcribed on every page; a legible scale equal to the catalogue's vetoes; twice it reported | +| A3 | Lens conflict where spacing fits reported gets a misleading reason | Separate reason | +| A4 | r 1.6-1.9 frames are not the half-scale mechanism | Report r≈2 and 1.6-1.9 separately | +| S1 | Numbers: 252 frames, 53 rolls, 58 roll-heights (all lenses) | Corrected | +| S2 | Unsampled bound misses 505 non-slipped upper_tail census frames and 3 random frames | Stated as a bound in the note | +| S3 | Catalogue reach overstates (most in band, never consulted) | Report as an upper bound | +| AC1 | Zero-accepted outcome | Tests assert the near_upper census reconciles whether or not rows are accepted | +| AC2-4 | Diff CSVs by tail; docs list; restore-the-bug test | Phases 2-4 | diff --git a/planning/archive/2026-09-issue-72-near-upper-scale-wrong/review-round1.md b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/review-round1.md new file mode 100644 index 0000000..5276739 --- /dev/null +++ b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/review-round1.md @@ -0,0 +1,59 @@ +# Review round 1 — fly#72 staged diff + +## Findings + +- **[bug — false claim in shipped note]** inst/notes/terrain-correction.md, new section, bullet 2 + ("Their keys reach **3,227** catalogue frames, all in the 2 < r above sea level ≤ 3 stratum, + so every one is outside the band whatever the terrain and the table fires on all of them. + The fallback drew them at 1/r of their width"). + The inference runs the wrong way: terrain *lowers* r (r_agl = (fh − elev)/nominal ≤ r_asl), so + r_asl > 2 does not keep a frame above 1.6. `fly_footprint()` only consults the table for + `out_of_band` frames (R/fly_footprint.R ~1204). The key reaches the band at a terrain of + fh − 1.6·nominal: 390 m for bc5642/44/45/48/50/89, 406 m for bc5699/5701/5702, 330 m for + bc78110, 287 m for bc79039, 936 m for bc7692, 1,521 m for bc85079–81. The shipped sweep already + disproves it: of the 132 sampled frames on the 24 tabled keys, **12 sit inside the band** + (r 1.49–1.57, all on bc85079/80/81, which is half of those three rolls' 24 sampled frames). + Those frames are `"reported"`, the table does not fire on them, and the fallback never drew + them narrow. Their sizing is unaffected, because factor 1 means the same height. But the + sentence is wrong, and so is the NEWS implication that all 3,227 frames the keys reach move + from a 1/r-wide fallback to `"corrected_roll_table"`. CLAUDE.md's "reaching 3,227 in the + catalogue" is accurate as a key count; only the note's "every one / fires on all of them" + is false. Suggest: "reach 3,227 catalogue frames; the table fires on those that terrain leaves + beyond the band (in the sample, 120 of 132), and the rest were already sized from their + height". + +- **[fragile]** data-raw/height_calibrate-lower_tail_rolls.R:187–189, the logbook scale parser. + It reads any "1:" anywhere in `scale_as_written` as the page's scale. Row 117 (bc78105, + `remark: "not requested at 1:40,000"`) parses to 40000, a scale the remark says was *not* + flown. The capture `[0-9][0-9, ]*` also swallows trailing space-separated digits (for example + "1:15,000 12 photos" becomes 1500012). Today this is inert: no near_upper frame's covering row + carries a scale (`log_scale` is NA on all 252, so the veto never fires, as the note says). But + the veto is wired to `n_scale_same > 0`, so it would drop a row if a future page's remark + names the catalogue's scale. + +- **[minor — wrong premise in test comment]** tests/testthat/test-fly_footprint_height_rolls.R:597. + The comment says bc5509's logbook "reads 21,500 ft (6,553.2 m) on a 6\" lens". The transcribed + row (bc5509__bc5509_1.jpg) has `focal_as_written` "RC8 355" and a **blank** `focal_mm`, and the + shipped row has `log_focal` NA. So the logbook states no lens there, and the acceptance rests + on spacing alone. That matches the note's own point that the height cannot discriminate, so + the comment overstates the witness. + +## Checked and clean + +- Every count in the note's outcome table (24/120, 21/82, 7/31, 1/2, 3/8, 2/9 → 58/252) against + the shipped CSVs. The 20 × 153 mm / 107 frames and 4 × 305 mm / 13 frames split. All sixteen + bc54xx–bc57xx rows are 1972–76. r_corrected 1.651/1.686/1.680 on bc7692/85079/85081. Reach + 3,227 (lower 1,130, upper 308). 505 = 2,094 − 1,589. The 3 random-set frames beyond the band, + bc5703 among them. All 21 focal-conflict rows are 305 against 153 (bc79075's 85 against 88 + is not a conflict). No near_upper page writes a scale. +- The logbook CSV has 183 added rows and no removed or altered ones. 142 new files; roll labels + reach 54, since bc5664 and bc7408 ride on neighbours' pages. NEWS says "144 pages on 53 rolls", + which matches the fetch count in progress.md. That is defensible if 2 fetched pages yielded no + rows, and not flagged. +- Rule: `spacing_ok & !fits(p_nominal)` cannot produce NA (`fits()` is `is.finite() & ...`). The + reason ordering gives each excluded near_upper row the predicate that fired, and the + "; nominal scale still applies" suffix is skipped only where the text already says so. The + sibling skip is correct, the census stopifnot includes `near`, and the 90/105/14 guard is exact. +- Tests: the whole file passes in a temp copy (NOT_CRAN=true, load_all). The factor-1 + near_upper row is reached (r = 2.55 > 1.6, `r_reported > 0`), and the mocked-table test + restores the defect and would fail without the row. diff --git a/planning/archive/2026-09-issue-72-near-upper-scale-wrong/review-round2.md b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/review-round2.md new file mode 100644 index 0000000..dbfccf4 --- /dev/null +++ b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/review-round2.md @@ -0,0 +1,58 @@ +# Code-check round 2 — fly#72 (branch 72-near-upper-scale-wrong) + +Scope: staged diff, with the round-1 fixes read closely. Verified against the shipped CSVs, +`inst/extdata/flying_height_sweep.csv`, `data-raw/.cache/near_upper_verdict.rds`, the +overlap window recomputed from the centroid cache (p_window = 0.5573 to 0.7797), and two +logbook images. `test-fly_footprint_height_rolls.R` passes (`NOT_CRAN=true`, load_all). + +Round-1 fixes confirmed: +- Fix 1: 120 of 132 sampled frames on tabled keys are beyond the band, the 12 inside all on + bc85079/80/81, threshold elevation 287.4–2,636.2 m. All reproduce. +- Fix 2: `scale_pat` now rejects the remark `not requested at 1:40,000`, and reads every + scale string in the logbook CSV correctly. No near_upper page has a scale (n_scale_read = 0), + so the veto is inert, as the note says. +- Fix 3: bc5509's camera line is "RC8 355" with focal_mm blank. The comment is now correct. + +Other figures that reproduce: 24/120, 21/82, 7/31, 1/2, 3/8, 2/9 (sum 58/252). All 22 +focal-conflict rows are logbook 305 against catalogue 153. 20 tabled keys are at 153 mm +(107 frames) and 4 at 305 mm (13 frames), r_corrected 1.651/1.686/1.680/1.815. Every +accepted row has p_corrected in 0.58–0.72 and p_nominal outside the window, so none sits at +the window's top edge. bc79039 sheet 6 matches its transcription (2.55 at 152-168, f 153 mm). + +## Findings + +- **[severity: bug]** inst/notes/terrain-correction.md:542 (the `bc80048` bullet): the note + says bc80048's "spacing fits nominal and rejects the reported height. It stays on nominal, + which the spacing supports." Spacing does not fit nominal. The verdict gives + p_nominal = 0.789 and p_corrected = 0.890, and the window's top is 0.7797, so + `fits(p_nominal)` is FALSE along with `fits(p_corrected)`. The excluded row's reason + ("spacing rejects the logbook's height") is correct. The note's claim that spacing + *supports* nominal for this roll is not: spacing fits neither reading, so nothing here + measures that nominal is right for bc80048's 11 frames. This is a shipped doc stating a + wrong measurement. + +- **[severity: bug]** inst/extdata/flying_height_rolls_excluded.csv (bc7223, 1249 m, 305 mm, + 1:2000, 5 frames) and the note's table row "excluded: no logbook page | 2 | 9": the reason + is "no logbook page covers these frames", but a page does. `bc7223__bc7223_1.jpg` (sheet 3 + of 3) lists strip 2 as final 53–108. Its START row reads 4.1 (4,100 ft = 1,249.7 m, the + catalogued height) and its END row reads 3.2. The transcription emits only frames 53 and + 108 for that strip, so the sampled frames 56/74/82/84/92 get `log_state = "none"`, the + state defined as "no row on any page reaches this frame". The actual state is a covered + strip whose height varies within it. That is the "conflict/unread vs no page" confusion + fly#60's round 3 fixed, arriving through the transcription instead of through the code. + The outcome is conservative (the roll stays on nominal), but the shipped reason and the + note's "no logbook page" count (2 roll-heights) are wrong for this roll, which has 12" + written and spacing fitting reported (p_reported 0.66–0.77, p_nominal ~0.46). Either + transcribe 54–107 as a covered row with an unread or conflicting height, or reword the + reason and count. + +- **[severity: fragile]** data-raw/height_calibrate-lower_tail_rolls.R:247-251: the comment + says "a number written after the scale is never swallowed into it", but + `(?:[, ][0-9]{3})+` still swallows a space-separated three-digit number after a + comma-grouped scale: `"1:15,000 123"` parses as 15000123 (measured). No current row + triggers it and the veto is inert, so nothing moves today. The claim in the comment is + false, though, and a future page reading like "1:15,000 250 ft AGL" would feed a bogus + scale into `n_scale_same` / `n_scale_double`. + +No other defects found in the R/fly_footprint.R gate, the test file, NEWS or the CLAUDE.md +entry. diff --git a/planning/archive/2026-09-issue-72-near-upper-scale-wrong/review-round3.md b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/review-round3.md new file mode 100644 index 0000000..a149c91 --- /dev/null +++ b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/review-round3.md @@ -0,0 +1,136 @@ +# Code-check round 3 — fly#72 (branch 72-near-upper-scale-wrong) + +Scope: staged diff, read against the shipped CSVs, `inst/extdata/flying_height_sweep.csv`, +`data-raw/.cache/near_upper_verdict.rds`, the centroid cache and `data-raw/flying_height_logbooks.csv`. +The generator was re-run in a `cp -R` copy: exit 0, and both `inst/extdata` tables came out +byte-identical to the staged ones. The overlap window is 0.557 to 0.780, and the 90 / 105 / 14 guard +passes. `test-fly_footprint_height_rolls.R` passes in the copy (`NOT_CRAN=true`, load_all: +PASS 391, FAIL 0). `devtools::document()` in the copy leaves `man/` unchanged. + +## Mechanism + +Each earlier finding was a claim written from the author's model of the data, or from a +snapshot of it, and not re-derived from the shipped artifact: + +- in its final state (after a later fix moved the data), or +- for the cases the author did not look at directly. The claim held for the instances that + were examined and was extended to the ones that were not. + +The earlier instances were: terrain's direction (reasoned), the regex's behaviour (asserted in +a comment, never run), bc5509's lens and bc80048's spacing (a category's property attributed to +a member), and bc7223's "no page" (the transcription's shape read as the page's content). + +This round, the same mechanism reaches: + +1. NEWS counts that the round-2 fix made stale. +2. A NEWS count that counts one sheet pair twice. +3. A note bound that characterises only the one frame the author looked at. + +## Findings + +- **[severity: bug]** NEWS.md:5 — "`data-raw/flying_height_logbooks.csv` gains **183** rows from + **144** newly read pages on **53** rolls, **transcribed blind**." Recomputed against the staged + CSV (`git diff --cached`: 0 removed lines, only additions): + - **185** rows are added, not 183. The round-2 fix for bc7223 added the two interior rows + (bc7223 54-107, bc78009 84-99) after this line was written, and nobody re-derived it. + - Those two rows' own `note` says "added at merge (fly#72 code-check)". The same goes for the + 18 whole-feet rows (1984-86 sheets), which say "interpreted as whole feet ... (fly#72 + merge)". So "transcribed blind" is no longer true of all the added rows. The readers left + those 18 heights blank, and the merge filled them. The three tabled 305 mm rows (bc85079, + bc85080, bc85081) rest on exactly those merge-filled heights. + - The CSV has **142** distinct new page files. 144 is the fetch count, and it includes one + sheet pair (`bc5701_5702_1/2`) cached under both `bc5701` and `bc5702`. findings.md:68 + says the duplicates were dropped, so 144 counts two images twice. + - The new rows cover **52** of the 53 near_upper rolls. bc7692's pages were already in the + CSV from fly#60. Counted by `film_roll` label they cover 54, because `bc5664` and `bc7408` + ride on neighbours' sheets. No count gives 53. + + This is a shipped changelog stating wrong numbers about a shipped input. Suggested wording: + "gains 185 rows from 142 newly read pages on 52 rolls (54 roll labels). 165 were transcribed + blind; 18 whole-feet heights were interpreted at merge, and 2 interior strip rows with no + written height were added at merge." Re-derive the numbers from the CSV rather than using + these. + +- **[severity: bug]** inst/notes/terrain-correction.md, section "The r ≈ 2 mass…", **Bound** + paragraph — "3 random-set frames beyond the band (one, `bc5703` at 1:6000 and 153 mm, r 1.99, + looks like this class)." The three frames are bc5703 (153 mm, 1:6000, 1829 m, r 1.99), + **bc85083** (305 mm, 1:10000, 6096 m, r 1.73) and **bc85090** (305 mm, 1:10000, 6096 m, r 1.63). + The two it leaves out look *more* like the tabled class than bc5703 does: + - They are 1985, 305 mm, 1:10000, Monkman-area rolls numbered next to the three 305 mm rows + this diff tables (bc85079, bc85080, bc85081 at 6401 m). + - They sit at 20,000 ft, and the logbook already in this diff writes 20,000 ft for bc85080 + 85-145 and bc85081 81-97, on the same camera and scale. + + They were never sampled because their r above sea level is 6096 / 3050 = **1.999**, just + under the stratum's floor of 2.025. The whole 20,000 ft / 305 mm / 1:10000 family in 1985 + falls in that gap: + + | roll | frames | + |---|---| + | bc85080 | 61 | + | bc85081 | 17 | + | bc85082 | 180 | + | bc85083 | 251 | + | bc85089 | 255 | + | bc85090 | 252 | + | bc85091 | 223 | + | **total** | **1,239** | + + So the sentence names one frame as the unmeasured instance of the class and implies the other + two are not instances. That understates the unmeasured reach, in the paragraph a reader uses to + judge what the table does not cover. The generic "roll-heights no sampled frame sits on" covers + these frames, but the named example points away from the largest concentration. Suggested fix: + say all three look like the class. Name the 1985 305 mm 1:10000 rolls at 20,000 ft, which sit + at r above sea level 1.999, below the stratum, with a logbook page already read for two of + them. + +No other defects. The R gate, the generator rule and the test assertions are clean, and so is +every other prose claim (table below). + +## Enumeration: every claim in the diff's new prose, recomputed + +| # | Claim | Where | Result | +|---|---|---|---| +| 1 | 209 sampled beyond r 1.8 at 153 mm: 90 nominal / 105 reported / 14 neither | note §fly#72; generator guard | verified: generator prints 90/105/14; "neither" = bc80048 (9), bc7454 (3), bc77013 (2), none of them both-fit | +| 2 | Nominal implies ~20% on the 105 | note §fly#72 | verified: median roll p_nominal 0.22 | +| 3 | Generator stops unless it reproduces 90/105/14 | note; generator | verified (code) | +| 4 | 252 frames, 53 rolls, 58 roll-heights, all lenses | note; NEWS; CLAUDE | verified: 224 at 153 mm, 25 at 305 mm, 3 at 88 mm | +| 5 | Readers had images only, one control sheet each, all three read back as catalogued | note | not verifiable from shipped artifacts: new rows carry no `control=TRUE`, and the evidence is only in findings.md | +| 6 | `focal_mm` filled only where a focal length is written | note; NEWS; CLAUDE | verified: 0 of the new rows have `focal_mm` without a focal length in `focal_as_written`, and 22 with only a camera id are blank | +| 7 | Logbook height cannot separate the readings; factor 1 agrees on both | note; CLAUDE; NEWS; generator | verified: all 21 lens rows have factor 1 and n_agree = n | +| 8 | Lens roll's reported height implies ~0.80 overlap; window top 0.78 | note; generator comment | verified: lens-row p_corrected 0.779–0.838; window top 0.7797 | +| 9 | No sampled page writes a scale, so the veto never fired | note | verified: n_scale_read = 0 on all 58; `scale_as_written` empty on every new row | +| 10 | Outcome table 24/120, 21/82, 7/31, 1/2, 3/8, 1/5, 1/4 | note | verified against excluded.csv (sum 58/252) | +| 11 | 20 × 153 mm (107 frames), 4 × 305 mm (13 frames) | note | verified | +| 12 | Sixteen 1972–76 bc54xx–bc57xx, plus bc5138, bc78110, bc79039, bc79141 | note | verified: photo_year 1972–76 on all 16 | +| 13 | "mostly 1972–76 rolls" | NEWS | verified: 17 of 24, bc7692 (1974) included | +| 14 | Three 305 mm rows at r 1.65–1.69 | note | verified: 1.651 / 1.686 / 1.680 (bc85080 1.815) | +| 15 | Keys reach 3,227 catalogue frames | note; NEWS; CLAUDE | verified by the same `key4` count over 1,670,471 cached frames | +| 16 | Terrain lowers r; table consulted only outside the band | note; CLAUDE; NEWS | verified: `r_reported = (fh - elev) / nominal_agl`; `hit[!out_of_band] <- NA` | +| 17 | Threshold ground 287–2,636 m depending on roll | note | verified: 287.4 (bc79039) to 2,636.2 (bc5508/09/10) | +| 18 | 120 of 132 sampled frames on tabled keys beyond the band; 12 inside, all bc85079/80/81 | note | verified: 5 / 3 / 4 | +| 19 | In-band frames already sized from the same height, so nothing changes | note; NEWS | verified: height_m differs from the catalogue by at most 0.5 m | +| 20 | Fallback drew them at 1/r, half at r = 2 | note; NEWS | verified: disputed, not slipped (r / 10.764 < 0.625), hence implausible and nominal | +| 21 | bc78051, bc79072, bc80122 among the 82 lens frames | note | verified | +| 22 | bc80048: logbook writes 6"; 0.79 nominal, 0.89 reported, both above 0.78 | note | verified in substance: page reads "21162 f 153 mm"; 0.789 / 0.890 > 0.7797 | +| 23 | bc79043: 12" written; spacing fits reported; excluded as disagreeing | note | verified: "f 305 mm"; p_corrected 0.772 inside, p_nominal 0.507 below 0.557 | +| 24 | near_upper = 600-frame sample of 2 < r above sea level ≤ 3 | note; R comment; generator comment; NEWS | verified: 600 rows, r above sea level 2.025–2.999 | +| 25 | 505 upper_tail census frames #54 does not repair | note | verified: 2,094 − 1,589 | +| 26 | 3 random frames beyond the band; one, bc5703, looks like this class | note | **false**: see finding 2 | +| 27 | 223 beyond r 1.8, 209 at 153 mm | note, r = 2 bullet | verified | +| 28 | "about half of it" is the wrong lens | note bullet; CLAUDE #54 sentence; R comment; generator comment | verified as approximate: 90 vs 105 in the split; 82 vs 120 by logbook | +| 29 | "the roll table now corrects" the wrong-scale half | note bullet | overstated scope (corrects only sampled keys); bounded in the section below it, not flagged | +| 30 | Logbooks write a 12" lens on 21 sampled roll-heights | R comment; CLAUDE | verified: all 21 are logbook 305 against catalogue 153 | +| 31 | Factor-1 rows correct a scale, not a height, so are not among the 308 | roxygen / Rd | verified: 308 is the upper-tail reach and near_upper is separate | +| 32 | Factor 1 is the only factor named for near_upper | R comment; generator | verified: `named = 1`; tabled factors {1}; witness all "logbook" | +| 33 | Every sampled near_upper roll-height left on nominal is listed with its reason | R comment | verified: 34 excluded, all carrying "nominal scale" | +| 34 | A near_upper key (2 < r ≤ 3) cannot be a slipped one (> 3) | generator comment | verified: upper_tail r above sea level min 3.054; stopifnot passes | +| 35 | Scale regex: no digit may follow; "1:15,000 123" and "1:15840 12 frames" read as no scale | generator comment | verified by tracing the lookahead: both give NA | +| 36 | Roll with no cached page is refetched; costs one request | generator comment | verified: re-run logged "7 rolls: 0 fetched or cached, 0 failed" | +| 37 | 183 rows / 144 pages / 53 rolls / transcribed blind | NEWS | **false**: see finding 1 | +| 38 | bc5509 logbook 21,500 ft (6,553.2 m); camera line names no focal | test comment | verified: "RC8 355", focal blank | +| 39 | bc78051's logbook writes a 12" lens | test comment | verified in substance: "110398 f 305" | +| 40 | Drawn about 2.6 times what the fallback drew | test comment | verified: (6553.2 − 300) / 2448 = 2.55 | +| 41 | Rounding cases bc5648, bc79039 need ±0.001 | test comment | accepted per brief; the test passes | +| 42 | A scale-wrong near_upper row stays above the band | test | verified: r_corrected min 1.651 | +| 43 | bc7223 START 4.1 / END 3.2; bc78009 83 ditto 2.60 / 100 2.70 | logbook CSV notes | consistent with the round-2 read of the image; the rows land as uninterpreted (bc7223 log_state all "uninterpreted") | diff --git a/planning/archive/2026-09-issue-72-near-upper-scale-wrong/task_plan.md b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/task_plan.md new file mode 100644 index 0000000..42a7fde --- /dev/null +++ b/planning/archive/2026-09-issue-72-near-upper-scale-wrong/task_plan.md @@ -0,0 +1,60 @@ +# Task: Half the r≈2 mass is a wrong scale, not a mislabelled lens, and the fallback draws it at half width (#72) + +## Problem + +`inst/notes/terrain-correction.md` and the comment on `fly_height_ratio_band()` say the mass of +film frames beyond r ≈ 2 is a 305 mm lens catalogued as 153 mm, so falling back to nominal +scale is "the correct answer there and not merely the cautious one". Measured while settling +fly#60 by the spacing between frames adjacent by frame number, **that is true for about half +of them**. + +For the 209 `near_upper` frames beyond r 1.8 catalogued at 153 mm, split per roll-height: + +- **90 frames** (mostly 1978–81: bc78051, bc79072, bc80048, bc80122, …) give the designed ~60% + forward overlap under nominal scale and ~80% under the reported height. 14 fit neither. That is the lens + reading, and the fallback is right. +- **105 of the 209 frames** give ~60% under the **reported** height and ~20% under nominal. + 91 of those are on 1972–76 bc5xxx rolls (bc5701, bc5509, bc5508, bc5699, bc5702, …); 14 are + not (bc5138 1965, bc79141, bc79043, bc79039, bc78110), and two 1972–76 rolls do not fit this + way (bc7407 fits nominal, bc7454 neither). Key it per roll, not by year. The height and lens are right and + `scale` is the wrong field, recorded at half its true denominator. The fallback draws these + frames at **half their true width**, and nothing on the row says so. + +The instrument was checked on known answers first: in-band random frames read 0.63 +[0.59–0.69], and #54's slipped frames read 0.62 after repair against 0.97 before. + +This is the upper-side mirror of fly#60's `scale_wrong` class, which fly#60 settles for the +lower tail with logbooks as the second witness. + +Decisions taken at the gate: `tail = "near_upper"` (third value; `upper` stays #54's slipped +frames); scope = every out-of-band roll-height in the sweep's 600-frame `near_upper` **sample** +(all lenses), with unsampled roll-heights stated as a bound. + +## Phase 1: Fetch and transcribe the near_upper logbooks +- [x] Make `fetch_logbooks()` fetch rolls missing from the cache (not only when the dir is absent), and call it for the out-of-band near_upper rolls +- [x] Record which of the 44 rolls have no `flight_log_url` / failed fetch +- [x] Transcribe every fetched page into `data-raw/flying_height_logbooks.csv`, same schema, **blind**: readers get only the image paths, never catalogue height/scale/lens (2–3 unnamed general-purpose subagents, each writing rows to a scratch file; merged and spot-checked by me) +- [x] Re-read the fly#60 control rolls (`bcc228`, `bc7349`) in the same batch as a reader check; note `scale_as_written` where legible (reported, not gated) + +## Phase 2: Generator — settle near_upper per roll-height +- [x] In `height_calibrate-lower_tail_rolls.R`, define the set: `near_upper` frames with r > band[2]; run `settle(near, named = 1, tail = "near_upper")`, rule fixed before running (fly#60's: ≥50% covered, ≥90% agreeing, no legible focal conflict, spacing in window) +- [x] Controls before the verdict: spacing on the issue's split (lens rolls fit nominal, scale rolls fit reported), plus the existing controls unchanged +- [x] Sibling witness not applied to near_upper (no height relation); `sibling_reason` states why; excluded reason suffixed "nominal scale still applies" +- [x] Extend `cause`, the census `stopifnot`s and the key-uniqueness check to three tails; regenerate both CSVs; confirm lower/upper rows are byte-identical to before (diff) +- [x] Record printed numbers (frames/roll-heights settled, lens vs scale split, catalogue reach) in findings.md + +## Phase 3: Tests +- [x] Widen tail setequal to three values; census reconciliation loop over `near_upper` set; factor-1 row ⇒ r beyond the band on its own tail's side +- [x] A near_upper tabled frame (real table row, r ≈ 2) is `corrected_roll_table`, sized from the reported height at ~2× the nominal width; the same frame off the table stays `implausible`/nominal — restore-the-bug check that the test reddens without the row +- [x] An excluded near_upper roll-height naming 305 mm in its logbook stays nominal + +## Phase 4: Docs +- [x] Rewrite the r≈2 claims: `fly_height_ratio_band()` comment (R/fly_footprint.R ~223), roll-table comment (~254), roxygen (~700–737 incl. frame counts), `inst/notes/terrain-correction.md` (~233 and roll-table section) +- [x] NEWS entry derived from the regenerated tables; CLAUDE.md Key Decisions entry; `devtools::document()` + +## Validation + +- [x] Tests pass +- [x] `/code-check` clean on each commit +- [x] PWF checkboxes match landed work +- [ ] `/planning-archive` on completion diff --git a/tests/testthat/test-fly_footprint_height_rolls.R b/tests/testthat/test-fly_footprint_height_rolls.R index 4403252..5137568 100644 --- a/tests/testthat/test-fly_footprint_height_rolls.R +++ b/tests/testthat/test-fly_footprint_height_rolls.R @@ -190,20 +190,24 @@ test_that("the table holds against the sweep and the logbooks it was measured fr s <- utils::read.csv(system.file("extdata/flying_height_sweep.csv", package = "fly")) band <- fly_height_ratio_band() in_band <- function(r) r >= band[1] & r <= band[2] - # Two sets, one per tail: fly#60's lower tail, and fly#71's #54-slipped frames — the upper - # tail that dividing by 10.764 brings into the band. + # Three sets, one per tail: fly#60's lower tail, fly#71's #54-slipped frames — the upper + # tail that dividing by 10.764 brings into the band — and fly#72's near_upper frames beyond + # the band, the r ~ 2 mass. up <- s[s$set == "upper_tail", ] up <- up[in_band((up$flying_height / fly_height_slip_factor() - up$elev) / (up$scale_n * up$focal_length / 1000)), ] - sets <- list(lower = s[s$set == "lower_tail", ], upper = up) + nu <- s[s$set == "near_upper", ] + nu <- nu[(nu$flying_height - nu$elev) / (nu$scale_n * nu$focal_length / 1000) > band[2], ] + sets <- list(lower = s[s$set == "lower_tail", ], upper = up, near_upper = nu) expect_identical(nrow(sets$upper), 1589L) # premise: #54's census + expect_identical(nrow(sets$near_upper), 252L) # Contract: the named slips, one cause each, and nothing else. The upper tail names only # 1/10 by factor: a logbook confirming 10.764 is excluded, since #54's repair already sizes # it. The one relation that is not a factor is a leading digit, which only a same-roll # sibling can name (fly#74), so its row carries the ratio it implies and a cause saying so. - expect_setequal(unique(tab$tail), c("lower", "upper")) - expect_setequal(unique(excl$tail), c("lower", "upper")) + expect_setequal(unique(tab$tail), c("lower", "upper", "near_upper")) + expect_setequal(unique(excl$tail), c("lower", "upper", "near_upper")) expect_true(all(tab$witness %in% c("logbook", "sibling"))) # An excluded row says why each witness passed it over: the logbook in `reason`, the # same-roll sibling in `sibling_reason`. @@ -212,6 +216,9 @@ test_that("the table holds against the sweep and the logbooks it was measured fr expect_true(all(tab$witness[digit] == "sibling")) expect_setequal(unique(tab$factor[tab$tail == "lower" & !digit]), c(1, 10, 100)) expect_setequal(unique(tab$factor[tab$tail == "upper" & !digit]), 0.1) + # near_upper names only 1: the height was flown and the scale is the wrong field (fly#72). + expect_setequal(unique(tab$factor[tab$tail == "near_upper"]), 1) + expect_true(all(tab$witness[tab$tail == "near_upper"] == "logbook")) expect_identical(tab$cause[!digit], c(`1` = "scale_wrong", `10` = "height_digit_dropped", `100` = "height_two_digits_dropped", @@ -230,7 +237,9 @@ test_that("the table holds against the sweep and the logbooks it was measured fr expect_false(anyNA(excl$reason) || any(!nzchar(excl$reason))) # An excluded slipped roll-height is still repaired by #54, and its reason says so. expect_true(all(grepl("10.764", excl$reason[excl$tail == "upper"], fixed = TRUE))) - expect_false(any(grepl("10.764", excl$reason[excl$tail == "lower"], fixed = TRUE))) + expect_false(any(grepl("10.764", excl$reason[excl$tail != "upper"], fixed = TRUE))) + # An excluded near_upper roll-height stays on nominal scale, and its reason says so. + expect_true(all(grepl("nominal scale", excl$reason[excl$tail == "near_upper"], fixed = TRUE))) key <- function(d) paste(d$film_roll, d$flying_height, d$focal_length, d$scale_n) expect_length(intersect(key(tab), key(excl)), 0) @@ -247,13 +256,19 @@ test_that("the table holds against the sweep and the logbooks it was measured fr expect_identical(sum(tb$frames_measured) + sum(ex$frames_measured), nrow(set), info = tl) # Each row's corrected ratio recomputed from the sweep, at the precision it is published. + # The generator takes it from the logbook height unrounded and `height_m` is published to + # 0.1 m, so a ratio on a half-way third decimal can round either side (bc5648, bc79039): + # held to within one unit in the third decimal, not to the digit. for (i in seq_len(nrow(tb))) { d <- set[key(set) == key(tb)[i], ] expect_gt(nrow(d), 0) r <- median((tb$height_m[i] - d$elev) / (d$scale_n * d$focal_length / 1000)) - expect_equal(round(r, 3), tb$r_corrected[i], tolerance = 1e-9, info = key(tb)[i]) - # A height-slip row must land in the band; a scale-wrong row by definition does not. - if (tb$factor[i] == 1) { + expect_true(abs(r - tb$r_corrected[i]) <= 0.001, info = key(tb)[i]) + # A height-slip row must land in the band; a scale-wrong row by definition does not, + # and stays on the side of the band its tail came from. + if (tb$factor[i] == 1 && tl == "near_upper") { + expect_true(r > band[2], info = key(tb)[i]) + } else if (tb$factor[i] == 1) { expect_true(r < band[1], info = key(tb)[i]) } else { expect_true(in_band(r), info = key(tb)[i]) @@ -401,3 +416,62 @@ test_that("the key matches however round the numbers are", { expect_identical(fp$height_source, "corrected_roll_table") expect_equal(fp$height_agl, 15240 - 300) }) + + +# fly#72: the r ~ 2 mass. bc5509 is catalogued at 6,553 m, 153 mm, 1:16000, and its logbook +# reads 21,500 ft (6,553.2 m) — its camera line names no focal length, so the spacing is what +# accepted it: the height is right and the scale is half its true denominator. bc78051 sits in the same mass and its logbook writes a 12" lens, so it is the +# other half — a lens catalogued wrong, which nominal scale already sizes. +near_fixture <- function() { + rf <- roll_fixture()[c(1, 1), ] + rf$film_roll <- c("bc5509", "bc78051") + rf$scale <- c("1:16000", "1:20000") + rf$focal_length <- 153 + rf$flying_height <- c(6553, 6858) + rf +} + +test_that("a near_upper frame with a wrong scale is drawn from its height (fly#72)", { + skip_if_no_terra() + tab <- fly_height_roll_table() + excl <- utils::read.csv(system.file("extdata/flying_height_rolls_excluded.csv", + package = "fly")) + key <- function(roll, h, f, s) paste(roll, h, f, s) + k <- key(c("bc5509", "bc78051"), c(6553, 6858), 153, c(16000, 20000)) + # Premise: one real table row and one real exclusion for a different lens, both ABOVE the + # band here, where #54's repair cannot reach them (r / 10.764 is far under it). + expect_identical(k %in% key(tab$film_roll, tab$flying_height, tab$focal_length, tab$scale_n), + c(TRUE, FALSE)) + ex <- excl[key(excl$film_roll, excl$flying_height, excl$focal_length, excl$scale_n) == k[2], ] + expect_match(ex$reason, "different lens", fixed = TRUE) + nominal <- c(16000, 20000) * 0.153 + r <- (c(6553, 6858) - 300) / nominal + band <- fly_height_ratio_band() + expect_true(all(r > band[2])) + expect_true(all(r / fly_height_slip_factor() < band[1])) + + fp <- suppressWarnings(fly_footprint(near_fixture(), dem = roll_dem())) + expect_identical(fp$height_source, c("corrected_roll_table", "implausible")) + expect_equal(fp$height_agl[1], 21500 * 0.3048 - 300) + # Drawn at the width its height implies — about 2.6 times what the fallback drew. + w <- roll_width(fp) + expect_equal(w[1], 9 * 0.0254 * (21500 * 0.3048 - 300) / 0.153, tolerance = 1e-3) + flat <- fly_footprint(near_fixture()) + expect_gt(w[1] / roll_width(flat)[1], 2.5) + # The lens roll keeps the nominal-scale footprint exactly. + expect_equal(w[2], roll_width(flat)[2]) + expect_identical(fp$flying_height, near_fixture()$flying_height) +}) + + +test_that("without its table row a near_upper frame falls back to nominal scale", { + skip_if_no_terra() + # The defect fly#72 repairs, restored: the same frame, with the table emptied of it. + tab <- fly_height_roll_table() + testthat::local_mocked_bindings( + fly_height_roll_table = function() tab[tab$tail != "near_upper", ] + ) + fp <- suppressWarnings(fly_footprint(near_fixture()[1, ], dem = roll_dem())) + expect_identical(fp$height_source, "implausible") + expect_equal(roll_width(fp), roll_width(fly_footprint(near_fixture()[1, ]))) +})