diff --git a/CLAUDE.md b/CLAUDE.md index 9115e88..782903d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -295,9 +295,22 @@ height missing two digits arrives, so it is gated on the band and not on `disput every excluded roll-height carries the reason that actually fired — no page, rows that disagree, a row not read are three states, and an earlier draft folded them into one. Spacing separates readings ~1.6× apart and no closer, so it cannot tell ×10 from ×10.764; -the logbook can. The control roll `bc78065` suggests #54's own factor is ×10 on pre-2000 -rolls (fly#71), and half the r ≈ 2 mass is a wrong scale, not a mislabelled lens (fly#72). -Do not change `fly_height_slip_factor()` from this; read `inst/notes/terrain-correction.md` +the logbook can. Half the r ≈ 2 mass is a wrong scale, not a mislabelled lens (fly#72). Read +`inst/notes/terrain-correction.md` + +- **#54's slipped frames are ÷10 wherever a logbook reads them, tabled where spacing +agrees, and ÷10.764 stays for the rest** (fly#71) — the same logbooks, read blind against the 1,589, put the crew's height +at the catalogue's divided by 10 on every slipped roll a page covers bar a one-frame typo +(`bc78065`, `bc78078`, `bc79027`, `bc79103`, `bcc00085`), 6.7-7.6% above ÷10.764 on each. So 6 +roll-heights (299 frames) ride `flying_height_rolls.csv` at factor **0.1**, and both tables carry a +`tail` column. **`fly_height_slip_factor()` was deliberately not changed**: the six 2003/2005 +rolls have no logbook link (the 2003 ones carry measured per-frame heights and fit 10.764 better; +`bcc05001` lands on round feet under ÷10 and is undecided), so they and +three stragglers (1,290 frames) stay on it, each listed in `_excluded.csv` with a reason saying +so. The roll-table gate in `fly_footprint()` is `tab_factor != 1`, never `> 1`: `> 1` silently +handed a 0.1 row back to #54's repair. `bc79027` reads ÷10 but spacing rejects it, because its page +logs 80% overlap against the ~60% window. `bcb98013` frame 52 is a leading-digit typo that no +factor recovers. Neither case moved the rule ## Gotchas diff --git a/R/fly_footprint.R b/R/fly_footprint.R index ce4d19a..c43c0c5 100644 --- a/R/fly_footprint.R +++ b/R/fly_footprint.R @@ -199,6 +199,17 @@ fly_dem_coverage_min <- function() 0.95 # Dividing by it puts all 1,589 frames between 0.80 and 1.32 of the height their own scale # implies. Reading the value as plain feet instead leaves them at 3.0 to 4.7 times it, and # not one of the 1,589 inside the band. +# +# That the band accepts it does not make it the defect on every roll (fly#71). x10 and +# x10.764 are 7.6% apart and both land all 1,589 inside the band, so the terrain cannot +# choose between them. The logbooks can: on five of the six rolls before 2003 whose +# slipped frames a page covers, the crew's height is the catalogue's divided by 10, to 0.9% +# (the sixth is a one-frame typo no factor fits), and the roll-heights that also pass the +# spacing check are carried in `flying_height_rolls.csv`, which is consulted first. What +# reaches this factor is the 2003-2005 rolls, which have no logbook (the 2003 ones carry +# measured per-frame heights and fit 10.764 better; `bcc05001` is undecided), plus 19 +# older frames the table does not reach, each listed with its reason in +# `flying_height_rolls_excluded.csv`. fly_height_slip_factor <- function() 3.28084^2 # How far a frame's height above ground may sit from `scale x focal_length` before the DEM @@ -238,12 +249,15 @@ fly_flying_height_max <- function() 16000 # spacing between frames adjacent by number, which must imply the ~60% forward overlap the # flight was designed to. `factor` names the defect: 10 or 100 where digits were dropped (a # 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. `height_m` is the height used — the logbook's, +# 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, # converted, since the catalogue's rounding survives multiplying by the factor. # # 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 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, 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. # Produced by `data-raw/height_calibrate-lower_tail_rolls.R`; see # `inst/notes/terrain-correction.md`. fly_height_roll_table <- function() { @@ -251,7 +265,8 @@ fly_height_roll_table <- function() { if (!nzchar(path)) { stop("`flying_height_rolls.csv` is missing from the installed package.", call. = FALSE) } - utils::read.csv(path, colClasses = c(film_roll = "character", cause = "character")) + utils::read.csv(path, colClasses = c(film_roll = "character", cause = "character", + tail = "character")) } # The DEM-aligned grid a single footprint is counted against. @@ -671,7 +686,9 @@ fly_is_square <- function(footprints) { #' inheriting whatever is wrong with it, and the catalogue's is about 10.76 times #' (3.28084 squared) too large on 1,589 film frames from 13 rolls flown between #' 1974 and 2005 — a feet-to-metres conversion applied the wrong way round, which -#' draws a 1:35000 frame 110 km across. A film frame states its height above +#' draws a 1:35000 frame 110 km across. On six roll-heights flown 1978-2000 the flight +#' logbooks show the factor is exactly 10 instead (fly#71), and those frames are +#' carried in the roll table below. A film frame states its height above #' ground twice, as `flying_height` minus terrain and as scale times focal length, #' so the two are compared, and `height_source` records the outcome: #' @@ -686,9 +703,10 @@ fly_is_square <- function(footprints) { #' \item{`"corrected_roll_table"`}{they disagree, and the frame sits on one of #' the roll-heights in `inst/extdata/flying_height_rolls.csv`, which the #' province's flight logbooks and the spacing between adjacent frames settled -#' (fly#60): a height with one or two digits dropped (a logbook "20.0", -#' thousands of feet, catalogued as 2,000 ft), or a correct height beside a -#' wrong `scale`. Sized from the measured height; matched on `film_roll`, +#' (fly#60, fly#71): 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, or a correct height beside a wrong `scale`. 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 @@ -704,8 +722,9 @@ fly_is_square <- function(footprints) { #' `"implausible"` even where `focal_length` is missing} #' } #' -#' Measured over the whole catalogue, the correction applies to those 1,589 -#' frames and to nothing else. A digital frame's `scale` is a nominal figure, not +#' 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: 299 by the roll table, +#' the rest by 10.76. 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")`. @@ -1089,9 +1108,10 @@ fly_footprint <- function(centroids_sf, negative_size = 9, format_size = NULL, # two are compared, and a frame outside `fly_height_ratio_band()` is not sized from # its `flying_height` as it stands. Where dividing by `fly_height_slip_factor()` brings # it back inside, that is the slip and the corrected height is used; measured over the - # whole catalogue the repair fires on the 1,589 and on nothing else. Anywhere else the - # two disagree and nothing here can say which is wrong, so the frame falls back to - # nominal scale, exactly as one with unusable metadata does. + # whole catalogue the repair fires on the 1,589 and on nothing else, less the 299 on + # 1978-2000 rolls whose logbooks say x10, which the roll table takes first (fly#71). + # Anywhere else the two disagree and nothing here can say which is wrong, so the frame + # falls back to nominal scale, exactly as one with unusable metadata does. # # Judged after the FIRST pass and before the second, because the second pass samples # the rectangle this height implies and that is the expensive half of the mistake. @@ -1123,6 +1143,10 @@ fly_footprint <- function(centroids_sf, negative_size = 9, format_size = NULL, # inferring one. A height slip must still reconcile THIS frame's ratio, as #54's does; # a factor of 1 says the scale is the wrong field, so the ratio against that scale is # exactly what cannot be asked of it — but that height must still clear the ground. + # A factor below 1 is a height recorded too LARGE (fly#71: the 1978-2000 rolls #54 + # divides by 10.764, whose logbooks read ten times less) and is held to the band like + # any other slip. The test is `!= 1`, not `> 1`: the gate once read `> 1`, which handed + # those frames silently to #54's repair. # # Not only `disputed` frames: a height with two digits dropped sits BELOW the terrain # (bc7280 is catalogued at 60 m), which `disputed` hands to the terrain-above-aircraft @@ -1154,7 +1178,7 @@ fly_footprint <- function(centroids_sf, negative_size = 9, format_size = NULL, # is 96 m short. r_tabled <- (tab_height - first$elev) / nominal_agl tabled <- out_of_band & is.finite(tab_factor) & - ((tab_factor == 1 & r_reported > 0) | (tab_factor > 1 & in_band(r_tabled))) + ((tab_factor == 1 & r_reported > 0) | (tab_factor != 1 & in_band(r_tabled))) slipped <- disputed & !tabled & in_band(r_repaired) fh_used <- fh @@ -1293,7 +1317,8 @@ fly_footprint <- function(centroids_sf, negative_size = 9, format_size = NULL, warning( sum(roll_tabled), " of ", sum(dem_eligible), " frames sit on a roll whose ", "`flying_height` disagreement was settled by the flight logbooks and the spacing ", - "between frames (`flying_height_rolls.csv`): a height with dropped digits, or a ", + "between frames (`flying_height_rolls.csv`): a height with dropped digits or ", + "recorded ten times too large, or a ", "correct height beside a wrong `scale`. Sized from the measured height; ", "`flying_height` is left as supplied and `height_agl` is the height used. ", "See `height_source`.", diff --git a/data-raw/flying_height_logbooks.csv b/data-raw/flying_height_logbooks.csv index 17770c7..99de287 100644 --- a/data-raw/flying_height_logbooks.csv +++ b/data-raw/flying_height_logbooks.csv @@ -192,3 +192,24 @@ "CONTROL_bc78065__bc78065_3.jpg",TRUE,"bc78065","78065","62-270",62,270,"13.0","True Height (M'/M.S.L.)","13000","f 153 mm","153","","partial","sheet 3 of 3; Princeton Area Rivers OP 212/78; finals overwritten in red, read as 62...270 (uncertain); 80% fwd overlap" "CONTROL_bcc228__bcc228_1.jpg",TRUE,"bcc228","bcc228","1-166",1,166,"12.650","M'/M.S.L.","12650","f 305","305","","clear","Remark: ""Too much turbulence for this job at 7,650' with 6"" cone""" "CONTROL_bcc228__bcc228_2.jpg",TRUE,"bcc228","bcc228","177-218",177,218,"14.0","M'/M.S.L.","14000","f 305","305","","clear","First final 177 (possibly overwritten 187)" +"bc78078__bc78078_6.jpg",FALSE,"bc78078","B.C. 78078","55-63",55,63,"3.1","True Height (M'/M.S.L.)","3100","f 153 mm","153","","clear","handwritten form; sheet 6 of 16; field roll NOV. 25; camera 21162; strip 1 dir 315; op 358/78, June 27/78" +"bc78078__bc78078_6.jpg",FALSE,"bc78078","B.C. 78078","64-76",64,76,"-","True Height (M'/M.S.L.)","3100","f 153 mm","153","","clear","height column is a ditto dash under 3.1; strip 2 dir 160; final 64 written in red slightly smudged ('CA'-like) but field 64 is clear" +"bc78078__bc78078_6.jpg",FALSE,"bc78078","B.C. 78078","77-91",77,91,"-","True Height (M'/M.S.L.)","3100","f 153 mm","153","","clear","height column is a ditto dash under 3.1; strip 3 dir 340" +"bc79027__bc79027_4.jpg",FALSE,"bc79027","B.C. 79 027","190-204",190,204,"6.5","True Height (M'/M.S.L.)","6500","f 153 mm","153","","clear","sheet 4 of 4 (both numbers overwritten from 3 in blue); field roll OSCAR 8; camera ZE 21162; strip 1 dir 360; field numbers partly cut off at page edge; page also notes 173-175 cleared and negs 152-162 destroyed (processor breakdown); 80% fwd overlap" +"bc79027__bc79027_4.jpg",FALSE,"bc79027","B.C. 79 027","205-214",205,214,"-","True Height (M'/M.S.L.)","6500","f 153 mm","153","","clear","height column is a ditto dash under 6.5; strip 2 dir 180" +"bc79103__bc79103_5.jpg",FALSE,"bc79103","B.C. 79/03","294-317",294,317,"7.2","True Height (M'/M.S.L.)","7200","f 153 mm","153","","partial","sheet 5 of 5; field roll OSCAR 43; roll written '79/03' (read as 79103); height 7.2 clear; end final read 317 (red) - 3rd digit could be 7 or 1 unlikely other; field end number crossed out and overwritten (appears 124, consistent with field 101 -> final 294 offset); strip 2 dir 005, off at Miller Creek; film ran to end, spliced onto OSCAR 42" +"bcb98013__bcb98013_1.jpg",FALSE,"bcb98013","15BCB99013","001-123",1,123,"24000","True Height","24000","CFL 152.861","153","1: 35000","clear","typed form, height in plain feet (no unit stated; 24000 m impossible); page says final roll 15BCB99013, flight B-010-E-99, date 1999-AUG-02 - differs from file prefix bcb98013 (98 vs 99); line 197 dir 090; sheet 1 of 1; camera RC20 5085" +"bcb98013__bcb98013_1.jpg",FALSE,"bcb98013","15BCB99013","124-194",124,194,"24000","True Height","24000","CFL 152.861","153","1: 35000","clear","line 198 dir 270; off at 194 (126 deg 15 min); same roll-number discrepancy as above" +"bcb98013__bcb98013_1.jpg",FALSE,"bcb98013","15BCB99013","195-236",195,236,"24000","True Height","24000","CFL 152.861","153","1: 35000","clear","line 198 dir 270 resumed at 195; end 236; same roll-number discrepancy as above" +"bcc00085__bcc00085_1.jpg",FALSE,"bcc00085","30BCC00085","001-021",1,21,"18500","True Height","18500","CFL 304.483","305","1: 15000","clear","typed form, height in plain feet; flight C-057-FS-00, 2000-SEP-23; camera RC 10 3539; line 12 dir 270; sheet 1 of 1" +"bcc00085__bcc00085_1.jpg",FALSE,"bcc00085","30BCC00085","022-045",22,45,"18000","True Height","18000","CFL 304.483","305","1: 15000","clear","line 11 dir 090" +"bcc00085__bcc00085_1.jpg",FALSE,"bcc00085","30BCC00085","046-068",46,68,"18000","True Height","18000","CFL 304.483","305","1: 15000","clear","line 10 dir 270" +"bcc00085__bcc00085_1.jpg",FALSE,"bcc00085","30BCC00085","069-089",69,89,"18000","True Height","18000","CFL 304.483","305","1: 15000","clear","line 9 dir 090" +"bcc00085__bcc00085_1.jpg",FALSE,"bcc00085","30BCC00085","090-111",90,111,"18500","True Height","18500","CFL 304.483","305","1: 15000","clear","line 8 dir 270" +"bcc00085__bcc00085_1.jpg",FALSE,"bcc00085","30BCC00085","112-135",112,135,"18500","True Height","18500","CFL 304.483","305","1: 15000","clear","line 7 dir 090" +"bcc00085__bcc00085_1.jpg",FALSE,"bcc00085","30BCC00085","136-159",136,159,"18500","True Height","18500","CFL 304.483","305","1: 15000","clear","line 6 dir 270" +"bcc00085__bcc00085_1.jpg",FALSE,"bcc00085","30BCC00085","160-182",160,182,"19000","True Height","19000","CFL 304.483","305","1: 15000","clear","line 5 dir 090" +"bcc00085__bcc00085_1.jpg",FALSE,"bcc00085","30BCC00085","183-202",183,202,"18500","True Height","18500","CFL 304.483","305","1: 15000","clear","line 4 dir 270" +"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" diff --git a/data-raw/height_calibrate-lower_tail_rolls.R b/data-raw/height_calibrate-lower_tail_rolls.R index 858fd1f..13b3d10 100644 --- a/data-raw/height_calibrate-lower_tail_rolls.R +++ b/data-raw/height_calibrate-lower_tail_rolls.R @@ -6,6 +6,10 @@ # that is undecidable. Per roll it is not: the 1,962 sit on 42 rolls, and nearly every roll # carries ONE `flying_height`, so each roll is one measurement with many witnesses. # +# fly#71 reads the same logbooks against #54's slipped frames, which #54 divides by 10.764 +# 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. +# # 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 @@ -214,7 +218,11 @@ fetch_logbooks <- function(rolls) { length(rolls), sum(status %in% 200L), sum(!status %in% 200L))) invisible(u) } -if (!dir.exists(LOG_DIR)) fetch_logbooks(unique(rolls$film_roll)) +# 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"]))) +} logs <- read.csv("data-raw/flying_height_logbooks.csv", colClasses = "character") logs$frame_from <- as.integer(logs$frame_from) @@ -235,85 +243,123 @@ for (roll in names(clean)) { } } -# --- Each lower-tail frame, joined to the logbook row covering its frame number ----- +# --- The two sets the logbooks are read against -------------------------------- +# The lower tail (fly#60), and #54's slipped frames (fly#71): the upper tail brought into the +# band by dividing by 10.764. #54 could not tell x10 from x10.764 there, because both land +# every frame in the band and spacing cannot separate readings 7.6% apart. The logbook can. +upper <- s[s$set == "upper_tail", ] +upper <- upper[in_band((upper$flying_height / K - upper$elev) / upper$nominal_agl), ] +message(sprintf("\n== #54 slipped: %d frames on %d rolls ==", nrow(upper), + length(unique(upper$film_roll)))) +# Round feet as a witness that gates nothing: planned altitudes are round numbers, so the +# right reading of a planned-altitude roll lands on one. The 2003 rolls carry measured +# per-frame heights and cannot speak here. +rf <- aggregate(airp_id ~ film_roll + photo_year + flying_height, upper, length) +rf$off_x10 <- round(off_round(rf$flying_height / 10)) +rf$off_xK <- round(off_round(rf$flying_height / K)) +print(rf[rf$photo_year < 2003, ], row.names = FALSE) + fn <- f1[, c("airp_id", "frame_number")] -lt <- merge(lower, fn, by = "airp_id") -stopifnot(nrow(lt) == nrow(lower)) -lt$log_ft <- NA_real_ -lt$log_focal <- 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): -# read one covering row with a height, or several that agree -# conflict covering rows with heights that disagree; left unread, not guessed -# uninterpreted covered only by rows whose height was not read, or on a roll with a -# row whose frame range could not be parsed ("169-20?") -# none no row on any page reaches this frame -lt$log_state <- "none" -unparsed_roll <- unique(logs$film_roll[is.na(logs$frame_from) & nzchar(logs$frames_final)]) -for (i in seq_len(nrow(lt))) { - cover <- which(logs$film_roll == lt$film_roll[i] & !is.na(logs$frame_from) & - lt$frame_number[i] >= logs$frame_from & lt$frame_number[i] <= logs$frame_to) - hit <- cover[is.finite(logs$log_ft[cover])] - if (length(hit) && length(unique(logs$log_ft[hit])) == 1) { - lt$log_state[i] <- "read" - 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 - } else if (length(hit)) { - lt$log_state[i] <- "conflict" - } else if (length(cover) || lt$film_roll[i] %in% unparsed_roll) { - lt$log_state[i] <- "uninterpreted" + +# Every frame of `set`, joined to the logbook row covering its frame number, and judged +# per roll-height against the factors `named` — the catalogue's height times the factor is +# the crew's. The rule is fly#60's, fixed before any roll was classified by it; see Stage 5. +settle <- function(set, named, tail) { + lt <- merge(set, fn, by = "airp_id") + stopifnot(nrow(lt) == nrow(set)) + lt$log_ft <- NA_real_ + lt$log_focal <- 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): + # read one covering row with a height, or several that agree + # conflict covering rows with heights that disagree; left unread, not guessed + # uninterpreted covered only by rows whose height was not read, or on a roll with a + # row whose frame range could not be parsed ("169-20?") + # none no row on any page reaches this frame + lt$log_state <- "none" + unparsed_roll <- unique(logs$film_roll[is.na(logs$frame_from) & nzchar(logs$frames_final)]) + for (i in seq_len(nrow(lt))) { + cover <- which(logs$film_roll == lt$film_roll[i] & !is.na(logs$frame_from) & + lt$frame_number[i] >= logs$frame_from & lt$frame_number[i] <= logs$frame_to) + hit <- cover[is.finite(logs$log_ft[cover])] + if (length(hit) && length(unique(logs$log_ft[hit])) == 1) { + lt$log_state[i] <- "read" + 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 + } else if (length(hit)) { + lt$log_state[i] <- "conflict" + } else if (length(cover) || lt$film_roll[i] %in% unparsed_roll) { + lt$log_state[i] <- "uninterpreted" + } } + stopifnot(identical(lt$log_state == "read", is.finite(lt$log_ft))) + # The factor the logbook implies, accepted only where it is one of the named slips to + # within 2%. The catalogue stores whole metres, so a round figure of feet comes back a + # fraction off. Held as an INDEX into `named`, so 1/10.764 is never compared as a + # decimal string. The named factors of a set are at least 7.6% apart, so 2% cannot + # reach two of them. + ratio <- lt$log_ft * FT / lt$flying_height + lt$log_factor_i <- vapply(ratio, function(q) { + hit <- which(is.finite(q) & abs(q / named - 1) <= 0.02) + if (length(hit) == 1) hit else NA_integer_ + }, integer(1)) + # A 6" lens is written as 152-153 mm and a 12" one anywhere from 304 to 305. + lt$focal_agrees <- is.finite(lt$log_focal) & abs(lt$log_focal - lt$focal_length) <= 3 + + # Grouped on the same four fields the shipped key carries, so a row can never describe + # frames its key does not reach, or reach frames it does not describe. + verdict <- do.call(rbind, lapply(split(lt, list(lt$film_roll, lt$flying_height, + lt$focal_length, lt$scale_n), drop = TRUE), + function(d) { + read <- !is.na(d$log_factor_i) + fi <- if (any(read)) as.integer(names(which.max(table(d$log_factor_i[read])))) else NA_integer_ + agree <- read & d$log_factor_i %in% fi + # The height the table ships is the LOGBOOK's, converted — never the catalogue's times + # the factor. The catalogue stores whole metres of a converted figure, so bc7280's + # 20,000 ft is catalogued as 60 m, and 60 x 100 is 6,000 m against the 6,096 the crew + # wrote down. + h_true <- if (any(agree)) median(d$log_ft[agree]) * FT else NA_real_ + data.frame( + tail = tail, + film_roll = d$film_roll[1], photo_year = d$photo_year[1], + flying_height = d$flying_height[1], + focal_length = d$focal_length[1], scale_n = d$scale_n[1], n = nrow(d), + n_logbook = sum(is.finite(d$log_ft)), n_named = sum(read), + n_conflict = sum(d$log_state == "conflict"), + n_uninterpreted = sum(d$log_state == "uninterpreted"), + factor = named[fi], n_agree = sum(agree), + log_ft = if (any(agree)) median(d$log_ft[agree]) else NA_real_, + height_m = round(h_true, 1), + focal_logbook = paste(sort(unique(d$log_focal[is.finite(d$log_focal)])), collapse = "/"), + # 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), + 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_, + r_corrected = if (any(agree)) median((h_true - d$elev[agree]) / d$nominal_agl[agree]) else NA_real_ + ) + })) + verdict <- verdict[order(-verdict$n), ] + stopifnot(sum(verdict$n) == nrow(set)) + message(sprintf("\n== %s tail against the logbooks, per roll-height ==", tail)) + print(verdict[verdict$n_logbook > 0 | verdict$photo_year < 2003, ], row.names = FALSE) + list(verdict = verdict, frames = lt) } -stopifnot(identical(lt$log_state == "read", is.finite(lt$log_ft))) -# The factor the logbook implies, accepted only where it is one of the named slips to within -# 2%. The catalogue stores whole metres, so a round figure of feet comes back a fraction off. -ratio <- lt$log_ft * FT / lt$flying_height -named <- c(1, 10, 100) -lt$log_factor <- vapply(ratio, function(q) { - hit <- named[is.finite(q) & abs(q / named - 1) <= 0.02] - if (length(hit)) hit else NA_real_ -}, numeric(1)) -# A 6" lens is written as 152-153 mm and a 12" one anywhere from 304 to 305. -lt$focal_agrees <- is.finite(lt$log_focal) & abs(lt$log_focal - lt$focal_length) <= 3 -# Grouped on the same four fields the shipped key carries, so a row can never describe -# frames its key does not reach, or reach frames it does not describe. -verdict <- do.call(rbind, lapply(split(lt, list(lt$film_roll, lt$flying_height, - lt$focal_length, lt$scale_n), drop = TRUE), - function(d) { - read <- is.finite(d$log_factor) - fac <- if (any(read)) as.numeric(names(which.max(table(d$log_factor[read])))) else NA_real_ - agree <- read & d$log_factor %in% fac - # The height the table ships is the LOGBOOK's, converted — never the catalogue's times the - # factor. The catalogue stores whole metres of a converted figure, so bc7280's 20,000 ft - # is catalogued as 60 m, and 60 x 100 is 6,000 m against the 6,096 the crew wrote down. - h_true <- if (any(agree)) median(d$log_ft[agree]) * FT else NA_real_ - data.frame( - film_roll = d$film_roll[1], photo_year = d$photo_year[1], flying_height = d$flying_height[1], - focal_length = d$focal_length[1], scale_n = d$scale_n[1], n = nrow(d), - n_logbook = sum(is.finite(d$log_ft)), n_named = sum(read), - n_conflict = sum(d$log_state == "conflict"), - n_uninterpreted = sum(d$log_state == "uninterpreted"), - factor = fac, n_agree = sum(agree), - log_ft = if (any(agree)) median(d$log_ft[agree]) else NA_real_, - height_m = round(h_true, 1), - focal_logbook = paste(sort(unique(d$log_focal[is.finite(d$log_focal)])), collapse = "/"), - # 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), - 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_, - r_corrected = if (any(agree)) median((h_true - d$elev[agree]) / d$nominal_agl[agree]) else NA_real_ - ) -})) -verdict <- verdict[order(-verdict$n), ] -stopifnot(sum(verdict$n) == nrow(lower)) -message("\n== lower tail against the logbooks, per roll-height ==") -print(verdict, row.names = FALSE) -saveRDS(list(verdict = verdict, frames = lt), "data-raw/.cache/lower_tail_verdict.rds") +lower_v <- settle(lower, named = c(1, 10, 100), tail = "lower") +upper_v <- settle(upper, named = c(1 / 10, 1 / K), tail = "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") + +# 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", ] +if (!(nrow(ctl78) == 1 && isTRUE(all.equal(ctl78$factor, 0.1)))) { + stop("logbook control bc78065 does not settle at 1/10") +} # --------------------------------------------------------------------------- # Stage 5 — the verdict, and the two shipped tables @@ -321,28 +367,35 @@ saveRDS(list(verdict = verdict, frames = lt), "data-raw/.cache/lower_tail_verdic # # The rule, fixed before any roll was classified by it. A roll-height is corrected only where # 1. the logbook covers at least half its frames, and at least 90% of the covered frames -# name the same factor (1, 10 or 100); +# name the same factor (1, 10 or 100 in the lower tail; 1/10 or 1/10.764 among #54's +# slipped frames); # 2. no legible logbook focal length contradicts the catalogue's — a different lens is a # different defect, and nominal scale is already right for it; # 3. spacing under the corrected height is inside the window the random frames set. # Two instruments, independent of each other and of the three fields in dispute. A factor # of 1 means the logbook confirms the catalogued height and the spacing accepts it: the # SCALE is the wrong field, and the fallback to nominal scale is what draws those frames -# wrong. +# 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. -v <- verdict +v <- rbind(lower_v$verdict, upper_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) +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$focal_conflict & v$spacing_ok & !v$confirms_54 # 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", ifelse(v$n_uninterpreted > 0, "uninterpreted", "none")) v$reason <- dplyr::case_when( v$accept ~ NA_character_, + v$confirms_54 & v$covered >= 0.5 & v$agreeing >= 0.9 ~ "logbook confirms #54's 10.764", v$covered < 0.5 & unread_state == "conflict" ~ "logbook rows covering these frames disagree", v$covered < 0.5 & unread_state == "uninterpreted" ~ "logbook height or frame range not read", v$n_logbook == 0 ~ "no logbook page covers these frames", @@ -352,35 +405,46 @@ v$reason <- dplyr::case_when( v$n_base == 0 ~ "no adjacent frames to measure spacing on", TRUE ~ "spacing rejects the logbook's height" ) +# An excluded slipped roll-height is not refused: #54's repair still sizes it. Say so, so +# 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") v$cause <- dplyr::case_when( !v$accept ~ NA_character_, - v$factor == 1 ~ "scale_wrong", - v$factor == 10 ~ "height_digit_dropped", - v$factor == 100 ~ "height_two_digits_dropped" + is_f(1) ~ "scale_wrong", + is_f(10) ~ "height_digit_dropped", + is_f(100) ~ "height_two_digits_dropped", + is_f(1 / 10) ~ "height_decimal_dropped" ) +stopifnot(!anyNA(v$cause[v$accept])) rolls_out <- data.frame( + tail = v$tail, film_roll = v$film_roll, flying_height = v$flying_height, focal_length = v$focal_length, scale_n = v$scale_n, factor = v$factor, cause = v$cause, logbook_ft = v$log_ft, height_m = v$height_m, frames_measured = v$n, frames_logbook = v$n_agree, overlap_corrected = round(v$p_corrected, 3), r_corrected = round(v$r_corrected, 3) )[v$accept, ] -rolls_out <- rolls_out[order(rolls_out$film_roll, rolls_out$flying_height), ] +rolls_out <- rolls_out[order(rolls_out$tail, rolls_out$film_roll, rolls_out$flying_height), ] excluded_out <- data.frame( + tail = v$tail, film_roll = v$film_roll, flying_height = v$flying_height, focal_length = v$focal_length, scale_n = v$scale_n, frames_measured = v$n, reason = v$reason )[!v$accept, ] -excluded_out <- excluded_out[order(excluded_out$film_roll, excluded_out$flying_height), ] +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)) + sum(rolls_out$frames_measured) + sum(excluded_out$frames_measured) == + nrow(lower) + nrow(upper)) message(sprintf("\n== verdict: %d roll-heights corrected (%d frames), %d excluded (%d frames) ==", nrow(rolls_out), sum(rolls_out$frames_measured), nrow(excluded_out), sum(excluded_out$frames_measured))) -print(table(rolls_out$cause)) -print(tapply(rolls_out$frames_measured, rolls_out$cause, sum)) -print(table(excluded_out$reason)) +print(table(rolls_out$tail, rolls_out$cause)) +print(tapply(rolls_out$frames_measured, list(rolls_out$tail, rolls_out$cause), sum)) +print(table(excluded_out$tail, excluded_out$reason)) +print(tapply(excluded_out$frames_measured, list(excluded_out$tail, excluded_out$reason), sum)) print(rolls_out, row.names = FALSE) # The key reaches only the frames it was measured on: count what each row would touch @@ -398,6 +462,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. +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 = "") write.csv(excluded_out, "inst/extdata/flying_height_rolls_excluded.csv", row.names = FALSE, diff --git a/inst/extdata/flying_height_rolls.csv b/inst/extdata/flying_height_rolls.csv index 8f80c2c..8ebcbb8 100644 --- a/inst/extdata/flying_height_rolls.csv +++ b/inst/extdata/flying_height_rolls.csv @@ -1,23 +1,29 @@ -"film_roll","flying_height","focal_length","scale_n","factor","cause","logbook_ft","height_m","frames_measured","frames_logbook","overlap_corrected","r_corrected" -"bc5224",1082,153,15840,1,"scale_wrong",3550,1082,46,46,0.614,0.333 -"bc5449",609,153,15000,1,"scale_wrong",2000,609.6,3,3,0.585,0.261 -"bc5536",731,153,12000,1,"scale_wrong",2400,731.5,9,9,0.604,0.384 -"bc5651",701,153,31680,10,"height_digit_dropped",23000,7010.4,41,41,0.64,1.121 -"bc5655",1158,153,75062,1,"scale_wrong",3800,1158.2,9,9,0.586,0.096 -"bc5697",457,153,6000,1,"scale_wrong",1500,457.2,102,102,0.619,0.487 -"bc7280",60,305,16000,100,"height_two_digits_dropped",20000,6096,101,101,0.652,1.073 -"bc7584",609,305,20000,10,"height_digit_dropped",20000,6096,124,124,0.647,0.805 -"bc7692",4511,305,63360,1,"scale_wrong",14800,4511,31,31,0.639,0.197 -"bc77014",853,153,50000,10,"height_digit_dropped",28000,8534.4,7,7,0.614,0.887 -"bc7703",1561,305,20000,1,"scale_wrong",5120,1560.6,10,10,0.618,0.19 -"bc77032",701,610,10000,10,"height_digit_dropped",23000,7010.4,54,54,0.608,0.905 -"bc7717",1676,305,12500,1,"scale_wrong",5500,1676.4,84,84,0.628,0.409 -"bc7717",1829,305,12500,1,"scale_wrong",6000,1828.8,43,43,0.656,0.405 -"bc78105",1295,153,20000,1,"scale_wrong",4250,1295.4,17,17,0.637,0.216 -"bc78151",1295,153,20000,1,"scale_wrong",4250,1295.4,35,35,0.668,0.22 -"bc7835",640,305,20000,10,"height_digit_dropped",21000,6400.8,8,8,0.629,0.995 -"bc79122",640,305,20000,10,"height_digit_dropped",21000,6400.8,149,149,0.618,1.039 -"bc81013",823,153,40000,10,"height_digit_dropped",27000,8229.6,59,59,0.643,1.176 -"bc81027",732,153,40000,10,"height_digit_dropped",24000,7315.2,18,18,0.631,1.026 -"bc85036",686,305,15000,10,"height_digit_dropped",22500,6858,34,34,0.606,1.176 -"bc88054",655,305,15000,10,"height_digit_dropped",21500,6553.2,17,17,0.602,1.072 +"tail","film_roll","flying_height","focal_length","scale_n","factor","cause","logbook_ft","height_m","frames_measured","frames_logbook","overlap_corrected","r_corrected" +"lower","bc5224",1082,153,15840,1,"scale_wrong",3550,1082,46,46,0.614,0.333 +"lower","bc5449",609,153,15000,1,"scale_wrong",2000,609.6,3,3,0.585,0.261 +"lower","bc5536",731,153,12000,1,"scale_wrong",2400,731.5,9,9,0.604,0.384 +"lower","bc5651",701,153,31680,10,"height_digit_dropped",23000,7010.4,41,41,0.64,1.121 +"lower","bc5655",1158,153,75062,1,"scale_wrong",3800,1158.2,9,9,0.586,0.096 +"lower","bc5697",457,153,6000,1,"scale_wrong",1500,457.2,102,102,0.619,0.487 +"lower","bc7280",60,305,16000,100,"height_two_digits_dropped",20000,6096,101,101,0.652,1.073 +"lower","bc7584",609,305,20000,10,"height_digit_dropped",20000,6096,124,124,0.647,0.805 +"lower","bc7692",4511,305,63360,1,"scale_wrong",14800,4511,31,31,0.639,0.197 +"lower","bc77014",853,153,50000,10,"height_digit_dropped",28000,8534.4,7,7,0.614,0.887 +"lower","bc7703",1561,305,20000,1,"scale_wrong",5120,1560.6,10,10,0.618,0.19 +"lower","bc77032",701,610,10000,10,"height_digit_dropped",23000,7010.4,54,54,0.608,0.905 +"lower","bc7717",1676,305,12500,1,"scale_wrong",5500,1676.4,84,84,0.628,0.409 +"lower","bc7717",1829,305,12500,1,"scale_wrong",6000,1828.8,43,43,0.656,0.405 +"lower","bc78105",1295,153,20000,1,"scale_wrong",4250,1295.4,17,17,0.637,0.216 +"lower","bc78151",1295,153,20000,1,"scale_wrong",4250,1295.4,35,35,0.668,0.22 +"lower","bc7835",640,305,20000,10,"height_digit_dropped",21000,6400.8,8,8,0.629,0.995 +"lower","bc79122",640,305,20000,10,"height_digit_dropped",21000,6400.8,149,149,0.618,1.039 +"lower","bc81013",823,153,40000,10,"height_digit_dropped",27000,8229.6,59,59,0.643,1.176 +"lower","bc81027",732,153,40000,10,"height_digit_dropped",24000,7315.2,18,18,0.631,1.026 +"lower","bc85036",686,305,15000,10,"height_digit_dropped",22500,6858,34,34,0.606,1.176 +"lower","bc88054",655,305,15000,10,"height_digit_dropped",21500,6553.2,17,17,0.602,1.072 +"upper","bc78065",4115,153,2000,0.1,"height_decimal_dropped",1350,411.5,24,24,0.594,0.975 +"upper","bc78078",9449,153,6000,0.1,"height_decimal_dropped",3100,944.9,15,15,0.622,0.957 +"upper","bc79103",21946,153,10000,0.1,"height_decimal_dropped",7200,2194.6,24,24,0.644,1.031 +"upper","bcc00085",54860,305,15000,0.1,"height_decimal_dropped",18000,5486.4,68,68,0.617,1.026 +"upper","bcc00085",56390,305,15000,0.1,"height_decimal_dropped",18500,5638.8,127,127,0.617,1.026 +"upper","bcc00085",57910,305,15000,0.1,"height_decimal_dropped",19000,5791.2,41,41,0.631,1.052 diff --git a/inst/extdata/flying_height_rolls_excluded.csv b/inst/extdata/flying_height_rolls_excluded.csv index 0549964..9dd1339 100644 --- a/inst/extdata/flying_height_rolls_excluded.csv +++ b/inst/extdata/flying_height_rolls_excluded.csv @@ -1,56 +1,522 @@ -"film_roll","flying_height","focal_length","scale_n","frames_measured","reason" -"bc5097",2012,153,31680,15,"logbook covers under half the frames" -"bc5097",2073,153,31680,1,"logbook height is not a named multiple of the catalogue's" -"bc5280",457,153,6000,3,"spacing rejects the logbook's height" -"bc5524",243,153,12000,30,"logbook height or frame range not read" -"bc5524",274,153,12000,18,"logbook height or frame range not read" -"bc5524",304,153,12000,21,"logbook height or frame range not read" -"bc5593",1066,153,15000,8,"logbook height is not a named multiple of the catalogue's" -"bc5597",777,153,12000,18,"spacing rejects the logbook's height" -"bc5598",2,153,16000,29,"logbook rows covering these frames disagree" -"bc5628",1371,153,80000,35,"spacing rejects the logbook's height" -"bc7211",4877,305,32000,18,"spacing rejects the logbook's height" -"bc7675",609,305,16000,84,"logbook covers under half the frames" -"bc78104",1295,153,20000,52,"no logbook page covers these frames" -"bc79067",4770,305,50000,47,"spacing rejects the logbook's height" -"bc79067",4968,305,50000,60,"spacing rejects the logbook's height" -"bc79067",5067,305,50000,8,"spacing rejects the logbook's height" -"bc79067",5624,305,50000,10,"spacing rejects the logbook's height" -"bc79086",6325,305,60000,107,"logbook names a different lens; nominal scale already sizes it" -"bc79209",7620,305,50000,85,"logbook rows covering these frames disagree" -"bc80001",1524,305,10000,106,"logbook names a different lens; nominal scale already sizes it" -"bc81026",762,305,5000,11,"logbook names a different lens; nominal scale already sizes it" -"bc87070",396,153,23000,34,"logbook height or frame range not read" -"bcb05001",1051,305,20000,2,"no logbook page covers these frames" -"bcb05001",1053,305,20000,3,"no logbook page covers these frames" -"bcb05001",1054,305,20000,2,"no logbook page covers these frames" -"bcb05001",1055,305,20000,2,"no logbook page covers these frames" -"bcb05001",1056,305,20000,3,"no logbook page covers these frames" -"bcb05001",1057,305,20000,2,"no logbook page covers these frames" -"bcb05001",1058,305,20000,4,"no logbook page covers these frames" -"bcb05001",1059,305,20000,4,"no logbook page covers these frames" -"bcb05001",1060,305,20000,2,"no logbook page covers these frames" -"bcb05001",1061,305,20000,1,"no logbook page covers these frames" -"bcb05001",1062,305,20000,3,"no logbook page covers these frames" -"bcb05001",1063,305,20000,1,"no logbook page covers these frames" -"bcb05001",1064,305,20000,1,"no logbook page covers these frames" -"bcb05001",1065,305,20000,2,"no logbook page covers these frames" -"bcb05001",1066,305,20000,1,"no logbook page covers these frames" -"bcb05001",1069,305,20000,1,"no logbook page covers these frames" -"bcb05001",1215,305,20000,1,"no logbook page covers these frames" -"bcb05001",1216,305,20000,1,"no logbook page covers these frames" -"bcb05001",1217,305,20000,1,"no logbook page covers these frames" -"bcb05001",1218,305,20000,1,"no logbook page covers these frames" -"bcb05001",1220,305,20000,1,"no logbook page covers these frames" -"bcb05001",1222,305,20000,1,"no logbook page covers these frames" -"bcb05001",1225,305,20000,2,"no logbook page covers these frames" -"bcb05001",1226,305,20000,2,"no logbook page covers these frames" -"bcb05001",1227,305,20000,1,"no logbook page covers these frames" -"bcb05001",1228,305,20000,2,"no logbook page covers these frames" -"bcb05001",1229,305,20000,1,"no logbook page covers these frames" -"bcb05001",1230,305,20000,2,"no logbook page covers these frames" -"bcb05001",1231,305,20000,1,"no logbook page covers these frames" -"bcc162",2438,305,16000,86,"logbook names a different lens; nominal scale already sizes it" -"bcc401",1219,305,10000,12,"logbook height is not a named multiple of the catalogue's" -"bcc822",701,305,15000,11,"logbook height or frame range not read" -"bcc94147",5511,305,52000,1,"logbook height is not a named multiple of the catalogue's" +"tail","film_roll","flying_height","focal_length","scale_n","frames_measured","reason" +"lower","bc5097",2012,153,31680,15,"logbook covers under half the frames" +"lower","bc5097",2073,153,31680,1,"logbook height is not a named multiple of the catalogue's" +"lower","bc5280",457,153,6000,3,"spacing rejects the logbook's height" +"lower","bc5524",243,153,12000,30,"logbook height or frame range not read" +"lower","bc5524",274,153,12000,18,"logbook height or frame range not read" +"lower","bc5524",304,153,12000,21,"logbook height or frame range not read" +"lower","bc5593",1066,153,15000,8,"logbook height is not a named multiple of the catalogue's" +"lower","bc5597",777,153,12000,18,"spacing rejects the logbook's height" +"lower","bc5598",2,153,16000,29,"logbook rows covering these frames disagree" +"lower","bc5628",1371,153,80000,35,"spacing rejects the logbook's height" +"lower","bc7211",4877,305,32000,18,"spacing rejects the logbook's height" +"lower","bc7675",609,305,16000,84,"logbook covers under half the frames" +"lower","bc78104",1295,153,20000,52,"no logbook page covers these frames" +"lower","bc79067",4770,305,50000,47,"spacing rejects the logbook's height" +"lower","bc79067",4968,305,50000,60,"spacing rejects the logbook's height" +"lower","bc79067",5067,305,50000,8,"spacing rejects the logbook's height" +"lower","bc79067",5624,305,50000,10,"spacing rejects the logbook's height" +"lower","bc79086",6325,305,60000,107,"logbook names a different lens; nominal scale already sizes it" +"lower","bc79209",7620,305,50000,85,"logbook rows covering these frames disagree" +"lower","bc80001",1524,305,10000,106,"logbook names a different lens; nominal scale already sizes it" +"lower","bc81026",762,305,5000,11,"logbook names a different lens; nominal scale already sizes it" +"lower","bc87070",396,153,23000,34,"logbook height or frame range not read" +"lower","bcb05001",1051,305,20000,2,"no logbook page covers these frames" +"lower","bcb05001",1053,305,20000,3,"no logbook page covers these frames" +"lower","bcb05001",1054,305,20000,2,"no logbook page covers these frames" +"lower","bcb05001",1055,305,20000,2,"no logbook page covers these frames" +"lower","bcb05001",1056,305,20000,3,"no logbook page covers these frames" +"lower","bcb05001",1057,305,20000,2,"no logbook page covers these frames" +"lower","bcb05001",1058,305,20000,4,"no logbook page covers these frames" +"lower","bcb05001",1059,305,20000,4,"no logbook page covers these frames" +"lower","bcb05001",1060,305,20000,2,"no logbook page covers these frames" +"lower","bcb05001",1061,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1062,305,20000,3,"no logbook page covers these frames" +"lower","bcb05001",1063,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1064,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1065,305,20000,2,"no logbook page covers these frames" +"lower","bcb05001",1066,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1069,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1215,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1216,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1217,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1218,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1220,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1222,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1225,305,20000,2,"no logbook page covers these frames" +"lower","bcb05001",1226,305,20000,2,"no logbook page covers these frames" +"lower","bcb05001",1227,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1228,305,20000,2,"no logbook page covers these frames" +"lower","bcb05001",1229,305,20000,1,"no logbook page covers these frames" +"lower","bcb05001",1230,305,20000,2,"no logbook page covers these frames" +"lower","bcb05001",1231,305,20000,1,"no logbook page covers these frames" +"lower","bcc162",2438,305,16000,86,"logbook names a different lens; nominal scale already sizes it" +"lower","bcc401",1219,305,10000,12,"logbook height is not a named multiple of the catalogue's" +"lower","bcc822",701,305,15000,11,"logbook height or frame range not read" +"lower","bcc94147",5511,305,52000,1,"logbook height is not a named multiple of the catalogue's" +"upper","bc5596",26212,153,12000,8,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bc79027",19995,153,13000,10,"spacing rejects the logbook's height; #54's 10.764 still applies" +"upper","bcb98013",97924,153,40000,1,"logbook height is not a named multiple of the catalogue's; #54's 10.764 still applies" +"upper","bcc03004",71640,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71641,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71642,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71644,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71645,153,30000,6,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71646,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71647,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71649,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71710,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71712,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71714,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71715,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71716,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71718,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71719,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71941,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71943,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71944,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71947,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71948,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71949,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71970,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71971,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71972,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71973,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71974,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71975,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71976,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71978,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71979,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71990,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71991,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71992,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71993,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71994,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71995,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71996,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71997,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71998,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",71999,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72030,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72031,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72032,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72033,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72034,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72035,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72036,153,30000,6,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72037,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72038,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72039,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72040,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72041,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72043,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72044,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72045,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72047,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72048,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72049,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72222,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72223,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72226,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72227,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72228,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72229,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72250,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72251,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72253,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72254,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72256,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72259,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72270,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72271,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72272,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72273,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72274,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72275,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72276,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72277,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72279,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72280,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72282,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72283,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72285,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72287,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72288,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72289,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72380,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72381,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72382,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72383,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72384,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72386,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72387,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72389,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72460,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72462,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72463,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72464,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72466,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72467,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72468,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03004",72469,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",72030,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",72031,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",72032,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",72033,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",72034,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",72035,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",72036,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",72037,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",72039,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75030,153,35000,7,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75031,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75032,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75033,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75034,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75036,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75037,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75038,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75039,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75110,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75111,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75112,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75113,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75114,153,35000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75115,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75116,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75117,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75118,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75220,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75221,153,35000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75222,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75223,153,35000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75224,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75226,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75227,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75228,153,35000,6,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",75229,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76600,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76601,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76602,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76603,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76604,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76605,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76606,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76607,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76608,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76609,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76680,153,35000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76681,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76682,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76683,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76684,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76685,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76686,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76687,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76688,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03006",76689,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69751,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69753,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69754,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69755,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69756,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69757,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69758,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69920,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69923,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69924,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69925,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69926,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69927,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69928,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69962,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69963,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69964,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69967,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",69969,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",70040,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",70042,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",70043,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",70045,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",70046,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",70048,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",70049,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72830,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72831,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72832,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72833,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72834,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72835,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72836,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72838,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72839,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72941,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72942,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72943,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72944,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72945,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72946,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72948,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72949,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72991,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72992,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72994,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72997,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72998,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",72999,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73130,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73131,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73133,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73134,153,35000,8,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73135,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73136,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73137,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73138,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73139,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73190,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73191,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73194,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73195,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73196,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73197,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73198,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73199,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73210,153,35000,7,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73212,153,35000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73213,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73215,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73216,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73217,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73218,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73219,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73310,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73311,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73312,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73313,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73314,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73315,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73316,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73317,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73318,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73319,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73330,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73331,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73332,153,35000,6,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73333,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73334,153,35000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73335,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73336,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73337,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",73339,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",74940,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",74941,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",74942,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",74943,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",74944,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",74945,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",74946,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",74947,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",74948,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03007",74949,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72130,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72131,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72133,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72134,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72135,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72136,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72137,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72138,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72139,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72860,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72861,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72865,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72867,153,35000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72868,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72869,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72990,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72991,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72992,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72994,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72995,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72997,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",72999,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73010,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73012,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73013,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73014,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73017,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73018,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73019,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73060,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73061,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73062,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73063,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73064,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73065,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73066,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73067,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73068,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",73069,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74121,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74122,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74123,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74124,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74128,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74363,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74365,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74366,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74367,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74368,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74369,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74680,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74683,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74684,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74685,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74686,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74688,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74689,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74990,153,35000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74991,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74992,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74993,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74994,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74996,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74997,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74998,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",74999,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78010,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78011,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78012,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78013,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78015,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78017,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78019,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78030,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78032,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78033,153,35000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78035,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78036,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78037,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78038,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78210,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78211,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78212,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78213,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78215,153,35000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78217,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78218,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",78219,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",79792,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",79793,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",79796,153,35000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",79798,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03008",79799,153,35000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",70881,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",70882,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",70885,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",70886,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",70888,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",70980,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",70982,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",70983,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",70988,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",70989,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71090,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71091,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71092,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71093,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71094,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71095,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71096,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71098,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71099,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71121,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71123,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71126,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71128,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71550,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71551,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71552,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71553,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71554,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71555,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71556,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71557,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71558,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71559,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71650,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71651,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71652,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71653,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71654,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71655,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71656,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71657,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71658,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71659,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71670,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71671,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71672,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71673,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71674,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71675,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71676,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71677,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71678,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71679,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71690,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71691,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71692,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71693,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71694,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71695,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71696,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71697,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71698,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71699,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71720,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71721,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71722,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71723,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71724,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71725,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71726,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71728,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71729,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71732,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71733,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71734,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71736,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71737,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71738,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71739,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71780,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71781,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71782,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71783,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71785,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71786,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71787,153,30000,5,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71788,153,30000,4,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71789,153,30000,6,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71800,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71802,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71803,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71804,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71805,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71806,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71807,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71808,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71840,153,30000,3,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71841,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71842,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71843,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71844,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71846,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71848,153,30000,1,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc03046",71849,153,30000,2,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc05001",62500,153,30000,106,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc05001",64000,153,30000,32,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc05001",64020,153,30000,28,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc05001",65550,153,30000,25,"no logbook page covers these frames; #54's 10.764 still applies" +"upper","bcc05001",67070,153,30000,26,"no logbook page covers these frames; #54's 10.764 still applies" diff --git a/inst/notes/terrain-correction.md b/inst/notes/terrain-correction.md index d6a7716..d4a47a2 100644 --- a/inst/notes/terrain-correction.md +++ b/inst/notes/terrain-correction.md @@ -326,7 +326,8 @@ catalogue stores whole metres of a converted figure, so `bc7280`'s 20,000 ft is `fly_footprint(dem = )` consults `inst/extdata/flying_height_rolls.csv` before #54's repair, keyed on roll, height, lens and scale together — which reaches exactly the 1,001 measured -frames — and marks those frames `"corrected_roll_table"`. A height slip must still reconcile +frames (1,300 since fly#71 added the upper tail, below) — and marks those frames +`"corrected_roll_table"`. A height slip must still reconcile the frame's own ratio, as #54's does. A scale-wrong row cannot be asked that, since the ratio is against the wrong scale, but its height must clear the ground. The table also reaches a frame whose catalogued height sits *below* the terrain, which is how a height missing two @@ -334,12 +335,61 @@ digits arrives. **Only on the DEM route**: without `dem`, a scale-wrong frame is from its wrong scale. **What the controls said about #54.** Roll `bc78065`'s 1:2000 frames are catalogued at 4,115 m -(13,500 ft), and its logbook reads **1.35** — 1,350 ft. That is a decimal point, ×10, not -×10.764. Round feet agree across the planned-altitude rolls #54 repairs (under ÷10, `bc5596` -8,600, `bc78078` 3,100, `bc79103` 7,200, `bcc00085` 18,000–19,000; not under ÷10.764), while -the 2003 rolls, which carry measured per-frame heights, fit ÷10.764 better. Both factors put -all 1,589 frames in the band, which is why #54 could not see it. Recorded as fly#71, not -changed here. +(13,500 ft), and its logbook reads **1.35**, which is 1,350 ft. That is a decimal point, ×10, not +×10.764. Both factors put all 1,589 frames in the band, which is why #54 could not see it. Recorded +as fly#71 and settled there (next section). + +## #54's slipped frames, read against the logbooks (fly#71) + +fly#71 applied the same instrument to #54's 1,589 slipped frames. These are the upper tail that +÷10.764 brings into the band. They sit on 13 rolls. Logbook pages cover the slipped frames of only +six, flown 1978-2000; the five besides the control were fetched and transcribed by a reader +who saw the images and the column spec and no catalogue value: + +| roll | catalogue m | logbook ft | vs ÷10 | vs ÷10.764 | +|---|---|---|---|---| +| `bc78065` (the control) | 4,115 | 1,350 | 0.00% | +7.6% | +| `bc78078` | 9,449 | 3,100 | 0.00% | +7.6% | +| `bc79027` | 19,995 | 6,500 | −0.9% | +6.7% | +| `bc79103` | 21,946 | 7,200 | 0.00% | +7.6% | +| `bcc00085` | 54,860 / 56,390 / 57,910 | 18,000 / 18,500 / 19,000 | ≤0.01% | +7.6% | +| `bcb98013` frame 52 | 97,924 | 24,000 | −25% | −20% | + +**On every roll a logbook reaches, bar the one-frame typo at the foot of the table, the height +is the catalogue's divided by 10**: exactly, to within the catalogue's whole-metre rounding, on +four, and 0.9% off on `bc79027`. The logbook sits 6.7–7.6% above ÷10.764 on each. Width +follows height above ground, not above sea level, so on the tabled roll-heights #54 drew the +frames 7.6–9.9% narrow (median per roll-height; 15–19% in area). `bc78065` produced the hypothesis, so the four +other rolls are the independent confirmation. + +The same rule as the lower tail, with the named factors 1/10 and 1/10.764, settles **6 +roll-heights (299 frames)**, carried in `flying_height_rolls.csv` with `tail = "upper"`, factor +0.1 and cause `height_decimal_dropped`. The table is consulted before #54's repair, so those +frames become `"corrected_roll_table"` and are sized from the logbook's height. A roll-height +whose logbook named 1/10.764 would be excluded ("logbook confirms #54's 10.764") rather than +tabled, since the repair already sizes it. No roll did. + +**Where #54's factor still applies, 1,290 frames**, each listed in +`flying_height_rolls_excluded.csv` with a reason ending "#54's 10.764 still applies": + +- **The six 2003/2005 rolls** (`bcc03004/06/07/08/46`, `bcc05001`; 1,271 frames) have no logbook + link at all. The five 2003 rolls carry measured per-frame heights, so their 1,054 frames sit on + 458 roll-heights, round feet cannot speak for them, and their ratio to scale × focal fits + ÷10.764 better (1.06–1.11 against 1.15–1.22 under ÷10). **`bcc05001` is the open case**: 217 + frames on 5 heights that land within 6 ft of round 500-ft altitudes under ÷10 (20,505 to + 22,005 ft). Round feet favour ÷10, while its ratio (0.98 against 1.08) favours ÷10.764. With no + logbook to decide between them it stays on #54's factor. +- **`bc5596` frames 204–211** have no logbook page. Their 26,212 m is ten times the + 2,621 m of frames 141–203 on the same roll, which points to ÷10, but a same-roll sibling is not + one of the two instruments (fly#74). +- **`bc79027`**: the logbook reads 1/10, but **spacing rejects it**. The implied overlap is 0.82, + outside the 0.557–0.780 window. The page itself records "80% fwd overlap", so the spacing is + right and this roll does not fit the window's ~60% design assumption. The rule fixed in fly#60 + is not moved for one roll. +- **`bcb98013` frame 52** is not a slip. The other 207 frames on the roll read 7,924 m, and 97,924 + is that figure with a leading 9. Neither factor recovers it: ÷10.764 gives 9,097 m, about 17% wide + in linear size. The page the catalogue links is headed roll 15BCB**99**013, flown 1999 at + 24,000 ft, so it may not be this roll's page at all (fly#74). Reproduce with `data-raw/height_calibrate-lower_tail_rolls.R`, which prints every figure in this section; `test-fly_footprint_height_rolls.R` holds the table to the sweep and the diff --git a/man/fly_footprint.Rd b/man/fly_footprint.Rd index f0361d4..b2ca22b 100644 --- a/man/fly_footprint.Rd +++ b/man/fly_footprint.Rd @@ -208,7 +208,9 @@ above the aircraft, which means \code{flying_height} is not in metres ASL. inheriting whatever is wrong with it, and the catalogue's is about 10.76 times (3.28084 squared) too large on 1,589 film frames from 13 rolls flown between 1974 and 2005 — a feet-to-metres conversion applied the wrong way round, which -draws a 1:35000 frame 110 km across. A film frame states its height above +draws a 1:35000 frame 110 km across. On six roll-heights flown 1978-2000 the flight +logbooks show the factor is exactly 10 instead (fly#71), and those frames are +carried in the roll table below. A film frame states its height above ground twice, as \code{flying_height} minus terrain and as scale times focal length, so the two are compared, and \code{height_source} records the outcome: @@ -223,9 +225,10 @@ elevation} \item{\code{"corrected_roll_table"}}{they disagree, and the frame sits on one of the roll-heights in \code{inst/extdata/flying_height_rolls.csv}, which the province's flight logbooks and the spacing between adjacent frames settled -(fly#60): a height with one or two digits dropped (a logbook "20.0", -thousands of feet, catalogued as 2,000 ft), or a correct height beside a -wrong \code{scale}. Sized from the measured height; matched on \code{film_roll}, +(fly#60, fly#71): 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, or a correct height beside a wrong \code{scale}. 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 @@ -241,8 +244,9 @@ all for another reason), one the DEM does not cover, or \code{flying_height} or \code{"implausible"} even where \code{focal_length} is missing} } -Measured over the whole catalogue, the correction applies to those 1,589 -frames and to nothing else. A digital frame's \code{scale} is a nominal figure, not +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: 299 by the roll table, +the rest by 10.76. 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-71-slip-factor-ten/README.md b/planning/archive/2026-09-issue-71-slip-factor-ten/README.md new file mode 100644 index 0000000..6b06fd1 --- /dev/null +++ b/planning/archive/2026-09-issue-71-slip-factor-ten/README.md @@ -0,0 +1,51 @@ +## Outcome + +fly#54 divides `FLYING_HEIGHT` by 3.28084² on 1,589 slipped film frames. The logbook pages +covering those frames were read blind (a subagent saw only the images and the column spec). On +every roll a page reaches, bar a one-frame typo, the crew's height is the catalogue's divided by +10, and ÷10.764 is short on each. Six roll-heights (299 frames, 1978–2000) now ride +`flying_height_rolls.csv` at factor 0.1 (`height_decimal_dropped`). Both roll tables gain a +`tail` column. The 1,290 slipped frames the table does not reach are listed in +`_excluded.csv` with a reason saying #54's 10.764 still applies. `fly_height_slip_factor()` is +unchanged: the 2003 rolls have no logbook and fit 10.764 better. + +The code fix was one comparison: the roll-table gate read `tab_factor > 1`, which silently +handed a 0.1 row back to #54's repair. It now reads `!= 1`, and a mocked test goes red under +the old form. Every `/code-check` finding across three rounds was false prose, not code. Each was +a set-level claim ("pre-2000", "every page reads /10", "exactly", "7.6% narrow") that had never +been checked member by member, and one fix created a new instance. Only enumerating every claim +against the CSVs (27, then 39) ended the loop. The two round-2 findings with the most weight: +width follows height above ground, not above sea level, so ASL ratios understate the error; and +`bcc05001` lands on round 500 ft under ÷10, which makes that roll undecided rather than a clear +÷10.764 case. + +## Measurement + +| roll | catalogue m | logbook ft | vs ÷10 | logbook above ÷10.764 | +|---|---|---|---|---| +| bc78065 (control) | 4,115 | 1,350 | 0.00% | +7.6% | +| bc78078 | 9,449 | 3,100 | 0.00% | +7.6% | +| bc79027 | 19,995 | 6,500 | −0.9% | +6.7% | +| bc79103 | 21,946 | 7,200 | 0.00% | +7.6% | +| bcc00085 ×3 | 54,860–57,910 | 18,000–19,000 | ≤0.01% | +7.6% | +| bcb98013 f52 | 97,924 | 24,000 | −25% | — (leading-digit typo) | + +- Width shortfall under #54 on the tabled roll-heights: 7.6–9.9% (median per roll-height), + 15–19% in area. +- Tabled: 6 roll-heights, 299 frames, reaching exactly 299 in the catalogue. +- Left on #54: 1,290 frames. That is 1,271 on the 2003/2005 rolls, which have no logbook link; + 8 on bc5596, which has no page; 10 on bc79027, where spacing reads 0.82 overlap and the page + logs "80% fwd overlap"; and 1 on bcb98013. +- The lower-tail rows of both tables are identical to main on every original column. +- Wrong turn kept: the plan named the cause `height_decimal_added`. The issue's own wording + ("a dropped decimal point") gave `height_decimal_dropped`. + +## Evidence + +`data-raw/flying_height_logbooks.csv` (rows for `bc78078`, `bc79027`, `bc79103`, `bcb98013` and +`bcc00085`). `data-raw/height_calibrate-lower_tail_rolls.R` reproduces every figure. The +per-claim verification tables are `review-round2.md` and `review-round3.md` in this directory. +The narrative is the fly#71 section of `inst/notes/terrain-correction.md`. Follow-up: +fly#74 (bcb98013 f52, bc5596). + +Closed by: PR for #71 (branch `71-pre-2000-slip-factor`) diff --git a/planning/archive/2026-09-issue-71-slip-factor-ten/findings.md b/planning/archive/2026-09-issue-71-slip-factor-ten/findings.md new file mode 100644 index 0000000..6b27f03 --- /dev/null +++ b/planning/archive/2026-09-issue-71-slip-factor-ten/findings.md @@ -0,0 +1,121 @@ +# Findings — fly#54's ÷10.764 looks like ÷10 on the pre-2000 slipped rolls (#71) + +## Issue context + +## Problem + +fly#54 repairs 1,589 film frames by dividing `FLYING_HEIGHT` by 3.28084² = 10.764. Measured +while settling fly#60, that factor looks right for the 2003 rolls and **about 7.6% wrong for +the pre-2000 ones**, where the defect appears to be a dropped decimal point, ×10. + +Two independent signs, both from public data: + +- **A flight logbook.** Roll `bc78065` is catalogued at 4,115 m (13,500 ft) on its 1:2000 + frames. Its scanned logbook page (`flight_log_url`), read with the catalogue values withheld, + gives TRUE HEIGHT **1.35**, i.e. 1,350 ft. ÷10 gives 411 m; ÷10.764 gives 382 m. +- **Round feet.** Planned altitudes are round numbers of feet. Under ÷10 the pre-2000 slipped + heights land exactly on them: bc5596 8,600 ft, bc78065 1,350, bc78078 3,100, bc79103 7,200, + bcc00085 18,000 / 18,500 / 19,000. Under ÷10.764 they don't. + +The ratio to `scale × focal_length` points the same way. Ordinary frames centre on r = 1.03: + +| roll | year | frames | r ÷10 | r ÷10.764 | +|---|---|---|---|---| +| bc5596 | 1974 | 8 | 0.957 | 0.856 | +| bc78065 | 1978 | 24 | 0.975 | 0.880 | +| bc78078 | 1978 | 15 | 0.957 | 0.884 | +| bc79027 | 1979 | 10 | 0.970 | 0.898 | +| bc79103 | 1979 | 24 | 1.031 | 0.929 | +| bcc00085 | 2000 | 236 | 1.030 | 0.944 | +| bcc03004 / 06 / 07 / 08 / 46 | 2003 | 1,054 | 1.15–1.22 | 1.06–1.11 | +| bcc05001 | 2005 | 217 | 1.078 | 0.979 | + +The 2003 rolls carry measured per-frame heights, not planned ones, so round feet cannot speak +for them, and there ÷10.764 is the better fit. + +## Why it matters + +A pre-2000 slipped frame is drawn about 7.6% narrow (15% in area). Every one of the 1,589 lands +in the band under either factor, which is why #54's band test could not separate them. + +## What would settle it + +The logbook pages for the other 12 slipped rolls, read the same way as fly#60's +(`data-raw/height_calibrate-lower_tail_rolls.R`, stage 4). If they agree, the fix is to carry +the pre-2000 rolls in `flying_height_rolls.csv` with factor 1/10, not to change +`fly_height_slip_factor()`. + +## Evidence + +`data-raw/flying_height_logbooks.csv` (the `CONTROL_bc78065` rows), +`inst/extdata/flying_height_sweep.csv` (`set == "upper_tail"`), and the fly#60 section of +`inst/notes/terrain-correction.md`. + + +## Exploration (plan mode, 2026-09-26) + +- Slipped roll-heights (sweep `upper_tail`, in band after ÷K) sit on 13 rolls. The issue's table + lists 12 and leaves out `bcb98013` frame 52: 97,924 m on a roll whose other 207 frames read + 7,924 m. That is an extra leading digit. +- Logbook links: pages exist for the slipped frames of bc78078 (p6), bc79027 (p4), bc79103 (p5), + bcb98013 (p1) and bcc00085 (p1). The control bc78065 has already been read. There is no page for + bc5596 frames 204–211 (the catalogue URL is NA for 141–211), and there are none for the six + 2003/2005 rolls (bcc03004/06/07/08/46, bcc05001). +- bc5596 204–211 catalogue 26,212 m, exactly ×10 of frames 141–203 at 2,621 m on the same roll. + That is a sibling witness, recorded here and not used as an instrument. +- `R/fly_footprint.R` `tabled` accepted only `factor == 1` or `factor > 1`, so a 1/10 row would be + dropped silently and handed to #54's repair. + +## Blind logbook reading (2026-09-26) + +The five pages were fetched into `data-raw/.cache/logbooks/` and transcribed by a subagent that saw +the images and the column spec only, with no catalogue values. The rows are appended to +`data-raw/flying_height_logbooks.csv`. + +| roll | catalogue m | logbook ft | logbook m | vs ÷10 | vs ÷10.764 | +|---|---|---|---|---|---| +| bc78065 (control) | 4,115 | 1,350 | 411.5 | 0.0000 | +7.63% | +| bc78078 | 9,449 | 3,100 | 944.9 | 0.0000 | +7.64% | +| bc79027 | 19,995 | 6,500 | 1,981.2 | −0.92% | +6.65% | +| bc79103 | 21,946 | 7,200 | 2,194.6 | 0.0000 | +7.64% | +| bcc00085 | 54,860 / 56,390 / 57,910 | 18,000 / 18,500 / 19,000 | | ≤0.01% | +7.64% | +| bcb98013 f52 | 97,924 | 24,000 | 7,315.2 | −25% | −20% | + +- ÷10 is exact to 1e-4 on six of the seven 1978-2000 roll-heights (bcc00085 is 2000, so "pre-2000" in the issue is loose). bc79027 is 0.9% off + (19,995 = 6,560 ft ×10), inside the script's 2% rounding tolerance. ÷10.764 misses every one + by 6.7–7.6%. +- bcb98013 fits no named factor. Its page is also headed roll **15BCB99013**, flight + B-010-E-99, 1999, where the file prefix says 98013, and its 24,000 ft differs from the 7,924 m + (26,000 ft) the roll's other frames carry. Either the catalogue links the wrong scan, or the page + belongs to a sibling roll. Frame 52 stays on #54's ÷10.764, which gives 9,097 m against the + 7,924 m its roll-mates carry. + +## Verdict after the script rerun (2026-09-26) + +- **28 roll-heights corrected (1,300 frames)**: the 22 lower-tail rows unchanged, plus 6 upper-tail + rows at factor 0.1, `height_decimal_dropped`, covering **299 frames** (bc78065, bc78078, bc79103, + and bcc00085 ×3). The corrections reach exactly 1,300 frames in the whole catalogue. +- **Upper tail excluded: 466 roll-heights, 1,290 frames**, all still repaired by #54's ÷10.764. + 464 have no logbook page: the six 2003/2005 rolls, whose per-frame measured heights give each + frame its own roll-height, and bc5596. Of the other two, bcb98013 f52 fits no named factor. + **bc79027 reads 1/10 in the logbook, but spacing rejects it**: overlap under the corrected + height is 0.82 against the random-frame window, and its own page records "80% fwd overlap". So + the spacing is right and the 60% design assumption does not hold for this roll. The rule fixed + in fly#60 is not moved for one roll; it stays on ÷10.764. +- The lower-tail rows of both tables are identical to main on every original column + (`all.equal` TRUE after dropping `tail`). +- Plan review (Plan agent): adopted accepting only 1/10 in the upper tail. A logbook that + confirms 10.764 is excluded with the reason "logbook confirms #54's 10.764" and not relabelled. + Also adopted: the accounting test split by tail, and the lint on the long roxygen line. Not + applicable: the 1/K round-trip blocker, since the factor is carried as an index and 1/K is never + shipped. Recorded: bc78065 is the reading that produced the hypothesis, so only the five newly + read rolls are independent confirmation. +- Checked end to end against the shipped table: a bc78065 1:2000 frame over 50 m ground gives + `corrected_roll_table` with `height_agl` 361.5 (logbook 1,350 ft). A bcc03004 frame stays + `corrected_unit_slip` at 6,605.6. + +## Errors Encountered + +| Error | Resolution | +|-------|------------| +| The pinned ÷10.764 assertion was written with the wrong sign (logbook is 7.6% *above* ÷K) | Flipped to `> 0.06`; suite then green | diff --git a/planning/archive/2026-09-issue-71-slip-factor-ten/progress.md b/planning/archive/2026-09-issue-71-slip-factor-ten/progress.md new file mode 100644 index 0000000..ce3ba3c --- /dev/null +++ b/planning/archive/2026-09-issue-71-slip-factor-ten/progress.md @@ -0,0 +1,25 @@ +# Progress — fly#54's ÷10.764 looks like ÷10 on the pre-2000 slipped rolls (#71) + +## Session 2026-09-26 + +- Plan-mode exploration — phases approved by user; gate decision: add a `tail` column to both + shipped roll tables +- Created branch `71-pre-2000-slip-factor` off main +- Scaffolded PWF baseline from issue #71 with approved phases +- Next: start Phase 1 +- Phase 1: 5 logbook pages fetched and transcribed blind by a subagent (commit eef7572) +- Plan agent review arrived; 3 findings adopted (see findings.md) +- Phases 2–4: `tab_factor != 1` gate, mocked 0.1 test (red before the fix), script refactored into + `settle()` over both tails, tables regenerated with `tail`, sweep test split by tail. Full suite: + one failure (my assertion sign), fixed; roll tests 202 pass +- Cause name `height_decimal_dropped`, not the plan's `height_decimal_added`: it is the issue's + own wording ("a dropped decimal point") +- Phase 5: fly#71 section in `inst/notes/terrain-correction.md`, a CLAUDE.md Key Decision, the + vignette count updated to 28, follow-up fly#74 filed (bcb98013 typo, bc5596 sibling ×10) +- `/code-check`, three rounds (review-round1..3.md), no code bugs in any of them. Every finding was + a false set-level claim in prose: "pre-2000" where bcc00085 is 2000, "every page /10" where + bcb98013 is not, ASL ratios quoted as width, and an unenforced "never corrected_unit_slip". + Round 1's fix produced a new instance, so rounds 2 and 3 enumerated every claim (27, then 39) + against the CSVs; round 3 left one false headline, now fixed. Round 2 also turned up that + `bcc05001` lands on round 500 ft under ÷10, so that roll is undecided rather than a ÷10.764 case +- Full suite: FAIL 0 | PASS 2348 diff --git a/planning/archive/2026-09-issue-71-slip-factor-ten/review-round1.md b/planning/archive/2026-09-issue-71-slip-factor-ten/review-round1.md new file mode 100644 index 0000000..3291b78 --- /dev/null +++ b/planning/archive/2026-09-issue-71-slip-factor-ten/review-round1.md @@ -0,0 +1,52 @@ +# Code-check round 1 — fly#71 staged diff + +Reviewer: subagent, 2026-09-26. Worked in a copy of the tracked tree under the session +scratchpad; no repo file other than this one was touched. + +## Verified (no finding) + +- `R/fly_footprint.R:1178-1179` — the `tab_factor != 1` gate. Ran + `test-fly_footprint_height_rolls.R` in a copy: all pass. Restored `tab_factor > 1` in the + copy: exactly the new test ("a factor below 1 is applied ahead of #54's slip...") fails, 2 + expectations, no errors. The guard fires. +- `tabled` and `slipped` stay mutually exclusive (`slipped <- disputed & !tabled & ...`), so + `height_source` assignment order cannot mislabel. A 0.1 frame whose `r_tabled` leaves the + band falls back to #54, and only at the upper edge: that needs terrain about 78 m below + sea level for bc78065 and is impossible for bcc00085, so the "never corrected_unit_slip" + roxygen holds for the tabled keys. +- Counts in the prose match the tables: 22 + 6 = 28 roll-heights (vignette); upper tabled + frames 24+15+24+68+127+41 = 299, and 1,589 - 299 = 1,290 = upper excluded frames (roxygen, + code comment). +- The script: `settle()` holds the factor as an index, so `named[fi]` is bit-identical to + `1/K` and `is_f()` compares it within 1e-9. `stopifnot(!anyNA(v$cause[v$accept]))` covers a + factor reaching `accept` with no cause. `v$reason != ...` on accepted rows is NA, but + `FALSE & NA` is FALSE, so the "; #54's 10.764 still applies" suffix cannot land on them. + `key4` is defined before the new cross-tail `anyDuplicated` check. Every upper excluded + reason contains "10.764", and no lower one does. + +## Findings + +- **[fragile] `R/fly_footprint.R:686`, `:202-208`, `:1110`, `:1144`, + `man/fly_footprint.Rd:211`, `tests/testthat/test-fly_footprint_height_rolls.R:7` — "pre-2000" + is false for half the tabled roll-heights.** + The roxygen says "On six **pre-2000** roll-heights the flight logbooks show the factor is + exactly 10". The comments say "the 299 on pre-2000 rolls" and "on the pre-2000 rolls the + crew's height is the catalogue's divided by exactly 10". + `bcc00085` has `photo_year` 2000 in `flying_height_sweep.csv`, and its own logbook row in + `data-raw/flying_height_logbooks.csv` reads "flight C-057-FS-00, 2000-SEP-23". So 3 of the 6 + tabled roll-heights, and 236 of the 299 frames, are from 2000, not before it. + This is a population claim in the user-facing docs, and CLAUDE.md's #60 entry ("×10 on + pre-2000 rolls (fly#71)") will likely be restated from it at archive time. + Fix: use "1978-2000", or "rolls before 2003". + The `fly_height_slip_factor()` comment has a smaller version of the same problem. It says + that what reaches 10.764 is "the 2003-2005 rolls ... plus the few frames no page settles". + Three pre-2003 roll-heights also reach it (19 frames): + - `bc5596` (1974, 8 frames, no page); + - `bcb98013` f52 (1 frame, fits no factor); + - `bc79027` (1979, 10 frames). Its page *does* read /10, and the spacing rule refused it. + + So "no page settles" misdescribes bc79027. That is deliberate per the stated tradeoffs, + but the comment says otherwise. + +No other issues found. The code change, the script refactor, the regenerated tables and +the tests are consistent with each other and with the #54 and #60 constraints in CLAUDE.md. diff --git a/planning/archive/2026-09-issue-71-slip-factor-ten/review-round2.md b/planning/archive/2026-09-issue-71-slip-factor-ten/review-round2.md new file mode 100644 index 0000000..01d798c --- /dev/null +++ b/planning/archive/2026-09-issue-71-slip-factor-ten/review-round2.md @@ -0,0 +1,79 @@ +# Code-check round 2 — fly#71 staged diff + +Reviewer: subagent, 2026-09-26. Method: enumerated every quantitative / scope claim the diff +adds or changes, then checked each against `flying_height_sweep.csv` (upper_tail, in band after +÷3.28084², band [1/1.6, 1.6]), `flying_height_rolls.csv`, `flying_height_rolls_excluded.csv`, +`flying_height_logbooks.csv`, `data-raw/.cache/upper_tail_verdict.rds` and the cached +catalogue centroids (`data-raw/.cache/centroids/`). No repo file other than this one touched. + +## Claim table + +| # | claim | location | verdict | evidence | +|---|---|---|---|---| +| 1 | 1,589 slipped frames on 13 rolls, 1974–2005 | roxygen/Rd, vignette | TRUE | sweep: 1,589, 13 rolls, years 1974–2005 | +| 2 | ×10 and ×10.764 both land all 1,589 in band | R comment l.203 | TRUE | 1,589/1,589 in band under ÷10 | +| 3 | x10 and x10.764 are 7.6% apart | R comment, script | TRUE | 10.764/10 = 1.0764 | +| 4 | six rolls before 2003 whose slipped frames a page covers | R comment, notes ("only six, 1978–2000") | TRUE | bc78065, bc78078, bc79027, bc79103, bcb98013 (1998), bcc00085 (2000); bc5596 204–211 uncovered (pages start at 212) | +| 5 | on five of the six "the crew's height is the catalogue's divided by **exactly** 10" | R/fly_footprint.R ~l.205 | FALSE for one | bc79027: 6,500 ft = 1,981.2 m vs 1,999.5 m, −0.9%. Notes correctly say "exactly … on four, and 0.9% off on bc79027" | +| 6 | 19 older frames the table does not reach | R comment | TRUE | bc5596 8 + bc79027 10 + bcb98013 1 | +| 7 | 2003–2005 rolls "carry measured per-frame heights" | R comment, CLAUDE.md, notes, script | FALSE for bcc05001 | bcc05001: 217 frames on 5 heights (106 on one) | +| 8 | "every frame is its own roll-height (463 of them)" | notes | FALSE | 1,271 frames on 463 roll-heights; 148 roll-heights hold one frame, the rest 2–8 (2003) or 25–106 (bcc05001) | +| 9 | "Round feet cannot speak for them" | notes | FALSE for bcc05001 | ÷10 gives 20,505 / 20,997 / 21,004 / 21,506 / 22,005 ft — all within 6 ft of a 500-ft figure; ÷10.764 gives 19,050 / 19,507 / 19,513 / 19,980 / 20,443. Round feet speak, and for ÷10. (2003 rolls: 12% vs 5% within 5 ft of 100 ft, ≈ chance — claim holds there) | +| 10 | ratio fits ÷10.764 better: 2003 1.06–1.11 vs 1.15–1.22; bcc05001 0.98 vs 1.08 | notes | TRUE | per-roll medians 1.060–1.111 / 1.154–1.216; 0.979 / 1.078 | +| 11 | six 2003/2005 rolls = 1,271 frames; + stragglers = 1,290 | notes, CLAUDE.md | TRUE | 232+178+231+179+234+217 = 1,271; +19 | +| 12 | 6 roll-heights, 299 frames tabled at 0.1 | everywhere | TRUE | csv; catalogue reach per key = measured (centroid cache) | +| 13 | 1,300 frames reached by the table | notes | TRUE | 1,001 + 299 | +| 14 | 28 roll-heights | vignette | TRUE | 28 rows | +| 15 | table rows: catalogue/logbook/vs ÷10/vs ÷10.764 | notes table | TRUE | recomputed: bc79027 −0.9% / +6.7%; bcb98013 −25% / −20%; others 0.00% / +7.6% | +| 16 | "÷10.764 is 6.7–7.6% short on each" | notes, CLAUDE.md | FALSE (direction) | logbook is 6.7–7.6% *above* ÷10.764; ÷10.764 is 6.3–7.1% *short* of the logbook (1 − 1/1.0764 = 7.1%) | +| 17 | "#54 drew those frames about 7.6% narrow (15% in area)" | notes; test comments ("7.6% narrow", twice) | FALSE | width ∝ height above ground, so the error is (fh/10.764 − elev)/(logbook_m − elev): per roll-height medians 0.901–0.924, i.e. **7.6–9.9% narrow, 15–19% in area**. 7.6% is the best case (bc78078), not typical; the mocked test's own fixture gives 332.3/361.5 = 8.1% | +| 18 | bcb98013: other 207 frames read 7,924 m | notes | TRUE | centroid cache: 207 at 7,924, 1 at 97,924, frames 1–208 | +| 19 | bcb98013 ÷10.764 = 9,097 m, "about 15% wide in linear size" | notes | FALSE (ASL ratio, not linear size) | 9,097/7,924 = 1.148 is ASL; linear size ∝ AGL: (9,097 − 1,042)/(7,924 − 1,042) = 1.17 → ~17% wide | +| 20 | page headed 15BCB99013, 1999, 24,000 ft | notes | TRUE | logbooks rows 201–203 | +| 21 | bc5596 204–211 at 26,212 m, "exactly ten times" 2,621 m of 141–203 | notes | TRUE (to whole-metre rounding) | centroid cache: 2,621 on 141–203, 26,212 on 204–211 | +| 22 | bc79027 implied overlap 0.82, window 0.557–0.780, page says 80% fwd | notes, CLAUDE.md | TRUE | verdict rds p_corrected 0.819; logbook row 198 note | +| 23 | 6 tabled roll-heights "flown 1978–2000", factor "exactly 10" | roxygen/Rd | TRUE | all within catalogue rounding of ÷10 | +| 24 | "the two corrections … apply to those 1,589 and nothing else: 299 by table, rest by 10.76" | roxygen/Rd | TRUE (current data) | 299 + 1,290 = 1,589 | +| 25 | "Checked before the 10.76 slip above, so a frame on a tabled roll is **never** `corrected_unit_slip`" | roxygen/Rd | FALSE as a code guarantee | `slipped <- disputed & !tabled & in_band(r_repaired)`; a keyed 0.1 frame whose `r_tabled` is > 1.6 while `r_repaired` ≤ 1.6 (a ~7–8% window above the band edge, reachable with a different DEM) is not tabled and IS slipped. Not reached by MRDEM today (max r under ÷10 is 1.18) | +| 26 | named factors of a set are at least 7.6% apart, so 2% cannot reach two | script | TRUE | | +| 27 | "logbook confirms #54" exclusion: no roll did | notes | TRUE | no such reason in excluded csv | + +## Code + +- `tab_factor != 1` gate, `tab_factor == 1` exact compare against CSV-read 0.1/1: correct. +- `settle()` / verdict / `confirms_54` / reason suffix / `stopifnot` reconciliations: no bug found. +- Test file: reconciliation per tail, pinned ÷10 finding, mocked gate test with premise check — sound. + The mocked table omits `tail`, which the code never reads; fine. + +## Findings + +- **[fragile — false prose]** inst/notes/terrain-correction.md (new fly#71 section, the + 2003/2005 bullet) — "They carry measured per-frame heights, so every frame is its own + roll-height (463 of them). Round feet cannot speak for them." False on both counts: 1,271 + frames sit on 463 roll-heights, and `bcc05001` is 217 frames on **five** heights, which under + ÷10 are 20,505 / 20,997 / 21,004 / 21,506 / 22,005 ft — round 500-ft planned altitudes, + i.e. round feet do speak for bcc05001 and favour ÷10, against its ratio (0.98 vs 1.08) + favouring ÷10.764. The exclusion itself stands (no logbook), but "so leaving them on #54's + factor is not a default" is overstated for bcc05001. Same "carry measured per-frame heights" + over all six rolls in CLAUDE.md (fly#71 Key Decision), R/fly_footprint.R comment above + `fly_height_slip_factor()`, and should be scoped to the 2003 rolls. +- **[fragile — false prose]** inst/notes/terrain-correction.md "so #54 drew those frames about + 7.6% narrow (15% in area)"; tests/testthat/test-fly_footprint_height_rolls.R comments + "7.6% narrow" (pinned-finding block and the mocked-gate test) — width scales with height + above ground, so the measured shortfall is 7.6–9.9% linear, 15–19% area, per tabled + roll-height median; 7.6% is the minimum. The mocked test's own fixture is 8.1%. +- **[fragile — false prose]** inst/notes/terrain-correction.md and CLAUDE.md "÷10.764 is + 6.7–7.6% short on each" — the 6.7–7.6% is the logbook's excess over ÷10.764 (the table's + own "+7.6%" column); ÷10.764 is 6.3–7.1% short. +- **[fragile — false prose]** inst/notes/terrain-correction.md, bcb98013 bullet — "÷10.764 + gives 9,097 m, about 15% wide in linear size": 15% is the ASL ratio; linear size goes with + AGL, (9,097 − 1,042)/(7,924 − 1,042) = 1.17, about 17% wide. +- **[fragile — false prose]** R/fly_footprint.R comment above `fly_height_slip_factor()` — "on + five of the six rolls … the crew's height is the catalogue's divided by exactly 10": one of + the five, bc79027, is 0.9% off (the notes say so correctly). +- **[fragile — overclaim]** R/fly_footprint.R roxygen (and man/fly_footprint.Rd) "Checked before + the 10.76 slip above, so a frame on a tabled roll is never `\"corrected_unit_slip\"`" — the + code does not guarantee it: a keyed factor-0.1 frame with `r_tabled` just above 1.6 and + `r_repaired` inside the band falls through `!tabled` into `slipped`. Unreachable on the + shipped data with MRDEM; reachable with another DEM. Either say "on the measured frames" or + make a keyed-but-out-of-band 0.1 frame not fall to #54. diff --git a/planning/archive/2026-09-issue-71-slip-factor-ten/review-round3.md b/planning/archive/2026-09-issue-71-slip-factor-ten/review-round3.md new file mode 100644 index 0000000..e1a53fe --- /dev/null +++ b/planning/archive/2026-09-issue-71-slip-factor-ten/review-round3.md @@ -0,0 +1,78 @@ +# Code-check round 3 — fly#71 staged diff + +Reviewer: subagent, 2026-09-26. Method: enumerated every quantitative / scope claim the diff +adds or changes (including those rewritten since round 2) and checked each against +`flying_height_sweep.csv` (upper_tail, in band after ÷3.28084², band [1/1.6, 1.6]), +`flying_height_rolls.csv`, `flying_height_rolls_excluded.csv`, `data-raw/flying_height_logbooks.csv`, +the cached logbook pages (`data-raw/.cache/logbooks/`) and the cached catalogue centroids. +No repo file other than this one touched. + +## Claim table + +| # | claim | location | verdict | evidence | +|---|---|---|---|---| +| 1 | 1,589 slipped frames, 13 rolls, 1974–2005 | roxygen/Rd, vignette, test premise | TRUE | sweep: 1,589; 13 rolls; years 1974, 1978, 1979, 1998, 2000, 2003, 2005 | +| 2 | ×10 and ×10.764 are 7.6% apart, both land all 1,589 in band | R ~l.203, script | TRUE | 10.764/10 = 1.0764; round 2 #2 | +| 3 | "on five of the six rolls before 2003 whose slipped frames a page covers ... ÷10, to 0.9% (the sixth is a one-frame typo no factor fits)" | R ~l.205 | TRUE | covered: bc78065, bc78078, bc79027, bc79103, bcc00085 (÷10 within 0.92%), bcb98013 (frame 52: −25% / −20%) | +| 4 | roll-heights that also pass spacing are in the roll table, consulted first | R ~l.207 | TRUE | 6 upper rows; `tabled` computed before `slipped <- disputed & !tabled` | +| 5 | what reaches 10.764 is the 2003–2005 rolls (no logbook) + 19 older frames | R ~l.209 | TRUE | excluded upper: 1,271 (bcc03004/06/07/08/46, bcc05001) + bc5596 8 + bc79027 10 + bcb98013 1 = 1,290. "No logbook": no page cached for any bcc03*/bcc05001 roll although the fetch covered upper-tail rolls (bcc00085 and bcb98013 pages were fetched); consistent, not independently queried | +| 6 | 2003 rolls "carry measured per-frame heights and fit 10.764 better"; bcc05001 undecided | R, CLAUDE.md | TRUE | 1,054 frames on 458 roll-heights (148 single-frame, max 8); ratio medians 1.06–1.11 vs 1.15–1.22 (round 2 #10) | +| 7 | factor 0.1 "where the height is ten times too LARGE ... rolls flown before 2003" | R ~l.252 | TRUE | tabled rolls 1978, 1978, 1979, 2000 | +| 8 | every #54-slipped roll-height not reached is in `_excluded.csv` | R ~l.258 | TRUE | test reconciles set keys; 1,589 = 299 + 1,290 | +| 9 | six roll-heights flown 1978–2000, factor "exactly 10" | roxygen/Rd | TRUE | 1350 ft = 411.48 vs 411.5; 3100 = 944.88 vs 944.9; 7200 = 2194.56 vs 2194.6; 18000/18500/19000 ft vs 5486.0/5639.0/5791.0 — all ≤0.01% | +| 10 | "Checked before the 10.76 slip above" | roxygen/Rd | TRUE | code order (round-2 "never" overclaim removed) | +| 11 | 299 by roll table, rest by 10.76 | roxygen/Rd, R ~l.1111 | TRUE | all 299 sweep frames land in band under the logbook height (r 0.894–1.180), so every one is `tabled` | +| 12 | "the 1978-2000 rolls #54 divides by 10.764, whose logbooks read ten times less" | R ~l.1146 | TRUE | | +| 13 | CLAUDE.md headline: "#54's slipped frames are ÷10 **wherever a logbook reaches them**, and ÷10.764 stays for the rest" | CLAUDE.md fly#71 Key Decision | FALSE | a logbook reaches bcb98013 frame 52 (page rows 1–123 at 24,000 ft) and it is not ÷10 (−25%); bc79027's logbook reaches it and reads ÷10 yet it stays on ÷10.764. The body qualifies both; the bolded headline does not | +| 14 | "÷10 on every slipped roll a page covers bar a one-frame typo (bc78065, bc78078, bc79027, bc79103, bcc00085)" | CLAUDE.md | TRUE | as #3 | +| 15 | "6.7–7.6% above ÷10.764 on each" | CLAUDE.md, notes | TRUE | bc79027 +6.65%, others +7.64–7.65% | +| 16 | 6 roll-heights (299 frames), factor 0.1, cause `height_decimal_dropped`, `tail` column in both tables | CLAUDE.md, notes | TRUE | csv: 24+15+24+68+127+41 = 299 | +| 17 | "they and three stragglers (1,290 frames) stay on it, each listed ... with a reason saying so" | CLAUDE.md, notes | TRUE | 3 straggler rolls; all 466 upper excluded rows end "#54's 10.764 still applies"; frames 1,279 + 10 + 1 = 1,290 | +| 18 | gate `!= 1`, `> 1` handed 0.1 rows to #54 | CLAUDE.md, R, test | TRUE | code l.1181 | +| 19 | bc79027 reads ÷10, spacing rejects, page logs 80% vs ~60% window; overlap 0.82, window 0.557–0.780 | CLAUDE.md, notes | TRUE | round 2 #22; excluded reason "spacing rejects..." 10 frames | +| 20 | bcb98013 frame 52 leading-digit typo no factor recovers | CLAUDE.md, notes | TRUE | 97,924 vs 207 frames at 7,924; logbook 24,000 ft fits neither within 2% | +| 21 | notes: 1,001 → 1,300 frames reached | notes l.329 | TRUE | 1,001 + 299 | +| 22 | "Logbook pages cover the slipped frames of only six, flown 1978-2000" | notes | TRUE | bc5596's page (bc5596_5597_3) starts at 212, slipped are 204–211; no pages for 2003/2005 rolls | +| 23 | notes table rows (catalogue m, logbook ft, vs ÷10, vs ÷10.764) | notes | TRUE | recomputed: 0.00/+7.6 ×3 rolls, bc79027 −0.9/+6.7, bcc00085 ≤0.01/+7.6, bcb98013 −25/−20; bc79027 catalogue 19,995 (frames 205–214) | +| 24 | "exactly ... on four, and 0.9% off on bc79027" | notes | TRUE | | +| 25 | tabled roll-heights drawn 7.6–9.9% narrow (median per roll-height), 15–19% in area | notes, test comment | TRUE | width ratio (fh/K − elev)/(h_log − elev) medians 0.901–0.924; squared 0.812–0.853 (14.7–18.8%) | +| 26 | "bc78065 produced the hypothesis, so the four other rolls are the independent confirmation" | notes | TRUE | four = bc78078, bc79027, bc79103, bcc00085 | +| 27 | "No roll did" (confirm 10.764) | notes | TRUE | no such reason in excluded csv | +| 28 | six 2003/2005 rolls = bcc03004/06/07/08/46 + bcc05001, 1,271 frames | notes | TRUE | sweep | +| 29 | five 2003 rolls: 1,054 frames on 458 roll-heights | notes | TRUE | recomputed 1,054 / 458 | +| 30 | bcc05001 217 frames, 5 heights, within 6 ft of round 500 ft under ÷10 (20,505–22,005); ratio 0.98 vs 1.08 | notes | TRUE | round 2 #9/#10; 5 excluded rows, 217 frames | +| 31 | bc5596 204–211: 26,212 m "exactly ten times" 2,621 m of 141–203 | notes | TRUE to whole-metre rounding | centroid cache: 2,621 on 141–203, 26,212 on 204–211; 10.0008×, both roundings of 8,600 / 86,000 ft | +| 32 | bcb98013 ÷10.764 = 9,097 m, ~17% wide linear | notes | TRUE | (9,097 − 1,042)/(7,924 − 1,042) = 1.17 | +| 33 | page headed 15BCB99013, 1999, 24,000 ft | notes | TRUE | logbooks csv note "flight B-010-E-99, date 1999-AUG-02" | +| 34 | vignette "On 28 roll-heights" | vignette | TRUE | 22 + 6 rows | +| 35 | test: "within 1% of ÷10, over 6% above ÷10.764" on every tabled slipped roll-height | test | TRUE | max 0.01% / min 7.64% | +| 36 | test mock: bc78065 4,115 m vs 1,350 ft; fixture 8.1% narrow within 7.6–9.9% | test | TRUE | 332.3/361.5 = 0.919 | +| 37 | script: named factors ≥7.6% apart so 2% cannot reach two | script | TRUE | | +| 38 | script: 2003 rolls cannot speak on round feet | script | TRUE | round 2 #9 (≈ chance for 2003) | +| 39 | script: "among slipped frames ... condition 3 is a sanity check and the logbook alone decides the factor" | script | TRUE (characterisation) | the factor is the logbook's; condition 3 still gates (it excluded bc79027), which "sanity check" does not deny | + +## Code + +- Gate `tab_factor != 1 & in_band(r_tabled)`: correct; 0.1 read from CSV as double, `== 1` exact + compare only against 1. All 299 sweep frames pass `in_band(r_tabled)`. +- `tabled` precedes `slipped`; `fh_used[tabled]` ≤ 5,791 m so the 16,000 m ceiling cannot refuse + a tabled bcc00085 frame even though its catalogued 54,860 m exceeds it (film frames are seeded + from scale, so `over_ceiling` seeding exclusion does not starve `first$elev`). +- Script: `settle()`, index-based factor match, `confirms_54`, reason suffix (NA-safe via + `!v$accept`), reconciliation `stopifnot`s, cross-tail key uniqueness — no bug found. +- Test: per-tail reconciliation and mocked gate test sound; mocked table omits `tail`, which the + code never reads. + +## Findings + +- **[low — false prose]** CLAUDE.md, fly#71 Key Decision headline — "#54's slipped frames are ÷10 + **wherever a logbook reaches them**, and ÷10.764 stays for the rest". A logbook reaches + `bcb98013` frame 52 (page rows 1–123, 24,000 ft) and it is not ÷10 (−25%); and `bc79027`'s + logbook reaches it and reads ÷10, yet it stays on ÷10.764 because spacing rejects it. The body + of the same bullet qualifies both ("bar a one-frame typo", "bc79027 reads ÷10 but spacing + rejects it"), but the bolded headline is the set-level summary a reader takes away, and it is + the same shape round 1 flagged ("every roll a page covers is /10"). Suggest e.g. "#54's + slipped frames are ÷10 wherever a logbook reads them, and are tabled where spacing agrees; + ÷10.764 stays for the rest". + +Everything else in the claim table checks out against the data; no code bugs found. diff --git a/planning/archive/2026-09-issue-71-slip-factor-ten/task_plan.md b/planning/archive/2026-09-issue-71-slip-factor-ten/task_plan.md new file mode 100644 index 0000000..a045536 --- /dev/null +++ b/planning/archive/2026-09-issue-71-slip-factor-ten/task_plan.md @@ -0,0 +1,139 @@ +# Task: fly#54's ÷10.764 looks like ÷10 on the pre-2000 slipped rolls (#71) + +## Problem + +fly#54 repairs 1,589 film frames by dividing `FLYING_HEIGHT` by 3.28084² = 10.764. Measured +while settling fly#60, that factor looks right for the 2003 rolls and **about 7.6% wrong for +the pre-2000 ones**, where the defect appears to be a dropped decimal point, ×10. + +Two independent signs, both from public data: + +- **A flight logbook.** Roll `bc78065` is catalogued at 4,115 m (13,500 ft) on its 1:2000 + frames. Its scanned logbook page (`flight_log_url`), read with the catalogue values withheld, + gives TRUE HEIGHT **1.35**, i.e. 1,350 ft. ÷10 gives 411 m; ÷10.764 gives 382 m. +- **Round feet.** Planned altitudes are round numbers of feet. Under ÷10 the pre-2000 slipped + heights land exactly on them: bc5596 8,600 ft, bc78065 1,350, bc78078 3,100, bc79103 7,200, + bcc00085 18,000 / 18,500 / 19,000. Under ÷10.764 they don't. + +The ratio to `scale × focal_length` points the same way. Ordinary frames centre on r = 1.03: + +| roll | year | frames | r ÷10 | r ÷10.764 | +|---|---|---|---|---| +| bc5596 | 1974 | 8 | 0.957 | 0.856 | +| bc78065 | 1978 | 24 | 0.975 | 0.880 | +| bc78078 | 1978 | 15 | 0.957 | 0.884 | +| bc79027 | 1979 | 10 | 0.970 | 0.898 | +| bc79103 | 1979 | 24 | 1.031 | 0.929 | +| bcc00085 | 2000 | 236 | 1.030 | 0.944 | +| bcc03004 / 06 / 07 / 08 / 46 | 2003 | 1,054 | 1.15–1.22 | 1.06–1.11 | +| bcc05001 | 2005 | 217 | 1.078 | 0.979 | + +The 2003 rolls carry measured per-frame heights, not planned ones, so round feet cannot speak +for them, and there ÷10.764 is the better fit. + +## Context (from plan-mode exploration) + +fly#54 divides `FLYING_HEIGHT` by 3.28084² on 1,589 film frames. fly#60's control roll +`bc78065` read 1,350 ft in its logbook, which fits ÷10, not ÷10.764. Round feet point the same +way on the other pre-2000 rolls. If that holds, those frames are drawn about 7.6% too narrow. +The issue's own fix: carry the pre-2000 rolls in `flying_height_rolls.csv` with factor 1/10, +**not** change `fly_height_slip_factor()`. + +What exploration established (read-only probes, 2026-09-26): + +- **Slipped roll-heights** (sweep `upper_tail`, in band after ÷K): 13 rolls. The issue's table + lists 12 and leaves out **`bcb98013` frame 52**: 97,924 m on a roll whose other 207 frames read + 7,924. That is an extra leading digit, not a slip. Neither ÷10 nor ÷10.764 gives 7,924. +- **Logbook links in the catalogue** for the frames that slipped: + | roll | slipped frames | page | + |---|---|---| + | bc78065 | 38–61 | already read (CONTROL, 1.35) | + | bc78078 | 77–91 | `bc78078_6.jpg` (55–91) | + | bc79027 | 205–214 | `bc79027_4.jpg` (190–214) | + | bc79103 | 294–317 | `bc79103_5.jpg` | + | bcb98013 | 52 | `bcb98013_1.jpg` | + | bcc00085 | 1–236 (3 heights) | `bcc00085_1.jpg` | + | bc5596 | 204–211 | **none** (frames 141–211 have no URL; page 3 starts at 212) | + | bcc03004/06/07/08/46, bcc05001 | all | **none** | + + So the logbooks can settle 5 rolls plus the control. They cannot settle the six 2003/2005 rolls, + and those keep #54's ÷10.764, which the issue says fits them better. They also cannot settle + `bc5596`: its slipped 26,212 m is exactly ×10 of the neighbouring frames' 2,621 m, but that + witness is noted and not used. +- **`R/fly_footprint.R:1152`** accepts a tabled row only where `tab_factor == 1` or + `tab_factor > 1`. A factor of 0.1 is rejected silently, so that one line has to change. The + table is already consulted before the slip, for any out-of-band frame, and the keys cannot + collide: the slipped heights differ from every lower-tail height. +- **`data-raw/height_calibrate-lower_tail_rolls.R`** stages 4–5 do the logbook join, the verdict + and the write. They are keyed to `lower` and to the factors `c(1, 10, 100)`. +- **`test-fly_footprint_height_rolls.R:192`** expects the factor set to be `c(1, 10, 100)`, and + `:199` expects every lower-tail roll-height to be either corrected or excluded. + +## Decision for the gate (stored data) — DECIDED: `tail` column + +**Where the slipped roll-heights the logbook cannot settle get recorded.** Chosen: add a +`tail` column (`"lower"` / `"upper"`) to both shipped tables and list the untabled upper-tail +roll-heights in `flying_height_rolls_excluded.csv`, with a reason that says #54's ÷10.764 still +applies. Accounting stays complete ("an unlisted roll is unmeasured"). Alternative: keep +`excluded` lower-tail only and record the upper tail in the note alone. That changes no schema, +but the 2003/2005 rolls stay findable only in prose. + +## Phases + +### Phase 1: Read the pre-2000 logbooks blind +- [x] Fetch the five pages above into `data-raw/.cache/logbooks/` with the script's existing + `fetch_logbooks()`. It is guarded by `dir.exists`, so call it for these rolls explicitly +- [x] Transcribe them through a subagent. It gets the image paths and the CSV column spec, and + it gets **no catalogue heights**, as fly#60 did. Append the rows to + `data-raw/flying_height_logbooks.csv` with `control = FALSE` +- [x] Record the reading in `findings.md`: which factor each roll names, and the `bcb98013` typo + +### Phase 2: Tests first +- [x] `test-fly_footprint_height_rolls.R`: allow a factor below 1. A frame on a tabled 1/10 + roll-height is sized from `height_m`, so it is `corrected_roll_table` and not + `corrected_unit_slip`. A 2003 frame still takes ÷10.764 +- [x] Mutation check: restore `tab_factor > 1` and confirm the new test goes red +- [x] Update the sweep-holding test. Every upper-tail slipped roll-height is either tabled or + excluded (per the gate decision). The factor set becomes `c(1, 10, 100)` plus whatever the + logbooks name for the upper tail + +### Phase 3: Code +- [x] `R/fly_footprint.R`: `tab_factor > 1` → `tab_factor != 1` in `tabled`. Update the comments + and the roll-table warning text, which says "dropped digits", to cover a height recorded ten + times too large +- [x] Update the `fly_height_slip_factor()` comment. Its claims "the only candidate that survives + the terrain" and "fires on the 1,589 and on nothing else" now hold only for the frames the + table does not reach +- [x] `fly_height_roll_table()` roxygen/comment: `factor` can be below 1 + +### Phase 4: Calibration script and shipped tables +- [x] Refactor stages 4–5 of `height_calibrate-lower_tail_rolls.R` into one function run over + a set and its named factors: lower tail `c(1, 10, 100)`, slipped upper tail `c(1/10, 1/K)`. + The rule stays the one fixed in fly#60 (≥50% covered, ≥90% agree, no lens conflict, spacing + fits). New causes: `height_decimal_added` (1/10) and `unit_slip` (1/K) +- [x] Print round-feet distance under ÷10 and ÷K as a non-gating witness. State that spacing + cannot separate 7.6% +- [x] Rerun the script. Regenerate `flying_height_rolls.csv` and `_excluded.csv`, and diff the + lower-tail rows, which must come back unchanged +- [x] Run `devtools::test()` and `lintr` + +### Phase 5: Docs +- [x] `inst/notes/terrain-correction.md`: replace the fly#71 paragraph with the result +- [x] Add a CLAUDE.md Key Decision line +- [x] Draft the NEWS entry (in the PR body). NEWS and the version bump are written by `/gh-pr-merge` +- [x] File a follow-up issue ([fly#74](https://github.com/NewGraphEnvironment/fly/issues/74)) for `bcb98013` frame 52 (a typo that #54's ÷10.764 draws ~15% wide) + and for bc5596 (×10 of its neighbours, no logbook), unless the logbook settles the first + +## Verification +- The script's controls pass (spacing, `bcc228`, `bc7349`), and `bc78065` reads back factor 1/10 +- The lower-tail rows of `flying_height_rolls.csv` are byte-identical before and after +- `devtools::test()` is green, and the mutation (`> 1`) goes red +- `fly_footprint(dem =)` on a synthesized `bc78065` 1:2000 frame gives `height_agl` near + 411 m − elev and `height_source == "corrected_roll_table"` + +## Validation + +- [x] Tests pass +- [x] `/code-check` clean on each commit +- [ ] 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 1eb19bb..4577bbb 100644 --- a/tests/testthat/test-fly_footprint_height_rolls.R +++ b/tests/testthat/test-fly_footprint_height_rolls.R @@ -3,7 +3,9 @@ # fly#54 left the lower tail — frames reading under half the height their scale implies — # "implausible", because three remedies fit it equally when pooled. Per roll they do not: # the scanned logbooks and the spacing between adjacent frames settle 22 roll-heights, -# shipped in `inst/extdata/flying_height_rolls.csv`. These tests hold `fly_footprint()` to +# shipped in `inst/extdata/flying_height_rolls.csv`. fly#71 read the same logbooks against +# #54's slipped frames and added the 1978-2000 roll-heights whose height is ten times too +# large, where #54 divides by 10.764. These tests hold `fly_footprint()` to # that table and hold the table to the sweep it was measured from. # # Every fixture row is keyed to a REAL table row, over level ground, so each expected @@ -186,44 +188,105 @@ test_that("the table holds against the sweep and the logbooks it was measured fr excl <- utils::read.csv(system.file("extdata/flying_height_rolls_excluded.csv", package = "fly")) s <- utils::read.csv(system.file("extdata/flying_height_sweep.csv", package = "fly")) - lt <- s[s$set == "lower_tail", ] + 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. + 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) + expect_identical(nrow(sets$upper), 1589L) # premise: #54's census - # Contract: the named slips, one cause each, and nothing else. - expect_setequal(unique(tab$factor), c(1, 10, 100)) + # Contract: the named slips, one cause each, and nothing else. The upper tail names only + # 1/10: a logbook confirming 10.764 is excluded, since #54's repair already sizes it. + expect_setequal(unique(tab$tail), c("lower", "upper")) + expect_setequal(unique(excl$tail), c("lower", "upper")) + expect_setequal(unique(tab$factor[tab$tail == "lower"]), c(1, 10, 100)) + expect_setequal(unique(tab$factor[tab$tail == "upper"]), 0.1) expect_identical(tab$cause, c(`1` = "scale_wrong", `10` = "height_digit_dropped", - `100` = "height_two_digits_dropped")[as.character(tab$factor)], + `100` = "height_two_digits_dropped", + `0.1` = "height_decimal_dropped")[as.character(tab$factor)], ignore_attr = TRUE) expect_false(anyNA(tab)) 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))) - # Every lower-tail roll-height is either corrected or excluded with a reason — never both, - # never neither — and the frame counts reconcile to the census exactly. key <- function(d) paste(d$film_roll, d$flying_height, d$focal_length, d$scale_n) - lt_keys <- table(key(lt)) expect_length(intersect(key(tab), key(excl)), 0) - expect_setequal(c(key(tab), key(excl)), names(lt_keys)) - expect_identical(as.integer(lt_keys[key(tab)]), as.integer(tab$frames_measured)) - expect_identical(as.integer(lt_keys[key(excl)]), as.integer(excl$frames_measured)) - expect_identical(sum(tab$frames_measured) + sum(excl$frames_measured), nrow(lt)) - - # Each row's corrected ratio recomputed from the sweep, at the precision it is published. - for (i in seq_len(nrow(tab))) { - d <- lt[key(lt) == key(tab)[i], ] - r <- median((tab$height_m[i] - d$elev) / (d$scale_n * d$focal_length / 1000)) - expect_equal(round(r, 3), tab$r_corrected[i], tolerance = 1e-9, info = key(tab)[i]) - # A height-slip row must land in the band; a scale-wrong row by definition does not. - band <- fly_height_ratio_band() - if (tab$factor[i] == 1) { - expect_true(r < band[1], info = key(tab)[i]) - } else { - expect_true(r >= band[1] && r <= band[2], info = key(tab)[i]) + for (tl in names(sets)) { + set <- sets[[tl]] + tb <- tab[tab$tail == tl, ] + ex <- excl[excl$tail == tl, ] + # Every roll-height of the set is either corrected or excluded with a reason — never + # both, never neither — and the frame counts reconcile to the census exactly. + set_keys <- table(key(set)) + expect_setequal(c(key(tb), key(ex)), names(set_keys)) + expect_identical(as.integer(set_keys[key(tb)]), as.integer(tb$frames_measured), info = tl) + expect_identical(as.integer(set_keys[key(ex)]), as.integer(ex$frames_measured), info = tl) + 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. + 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(r < band[1], info = key(tb)[i]) + } else { + expect_true(in_band(r), info = key(tb)[i]) + } + # The height used is the logbook's, converted; and it is the catalogue's times the + # factor to within the catalogue's rounding, which is what makes the factor the + # defect's name. + expect_equal(tb$height_m[i], round(tb$logbook_ft[i] * 0.3048, 1), info = key(tb)[i]) + expect_true(abs(tb$height_m[i] / (tb$flying_height[i] * tb$factor[i]) - 1) <= 0.02, + info = key(tb)[i]) } - # The height used is the logbook's, converted; and it is the catalogue's times the factor - # to within the catalogue's rounding, which is what makes the factor the defect's name. - expect_equal(tab$height_m[i], round(tab$logbook_ft[i] * 0.3048, 1), info = key(tab)[i]) - expect_true(abs(tab$height_m[i] / (tab$flying_height[i] * tab$factor[i]) - 1) <= 0.02, - info = key(tab)[i]) } + # fly#71's finding, pinned: on every tabled slipped roll-height the logbook sits within + # 1% of the catalogue divided by 10, and over 6% above it divided by 10.764 — which is + # the height #54 drew these frames from. + u <- tab[tab$tail == "upper", ] + expect_true(all(abs(u$height_m / (u$flying_height / 10) - 1) < 0.01)) + expect_true(all(u$height_m / (u$flying_height / fly_height_slip_factor()) - 1 > 0.06)) +}) + + +test_that("a factor below 1 is applied ahead of #54's slip, and only where it is tabled", { + skip_if_no_terra() + # fly#71: a 1978 roll #54 divides by 10.764 whose logbook says 10 — bc78065 is + # catalogued at 4,115 m where the crew wrote 1,350 ft. A factor below 1 must reach the + # table exactly as one above it does; the gate once accepted only `factor > 1`, which + # handed these frames to #54's repair and drew them 7.6-9.9% narrow. + testthat::local_mocked_bindings( + fly_height_roll_table = function() { + data.frame( + film_roll = "bc78065", flying_height = 4115L, focal_length = 153L, scale_n = 2000L, + factor = 0.1, height_m = 411.5, stringsAsFactors = FALSE + ) + } + ) + rf <- roll_fixture()[c(1, 1), ] + rf$film_roll <- c("bc78065", "bc78066") # the second shares everything but the roll + rf$scale <- "1:2000" + rf$focal_length <- 153 + rf$flying_height <- 4115 + dem <- roll_dem(50) + # Premise: both readings land in the band here, so nothing but the table separates them. + band <- fly_height_ratio_band() + nominal <- 2000 * 0.153 + expect_true(all(c(411.5 - 50, 4115 / fly_height_slip_factor() - 50) / nominal >= band[1] & + c(411.5 - 50, 4115 / fly_height_slip_factor() - 50) / nominal <= band[2])) + fp <- suppressWarnings(fly_footprint(rf, dem = dem)) + expect_identical(fp$height_source, c("corrected_roll_table", "corrected_unit_slip")) + expect_equal(fp$height_agl[1], 411.5 - 50) + expect_equal(fp$height_agl[2], 4115 / fly_height_slip_factor() - 50) + expect_identical(fp$flying_height, rf$flying_height) }) diff --git a/vignettes/airphoto-selection.Rmd b/vignettes/airphoto-selection.Rmd index f772100..7d6b713 100644 --- a/vignettes/airphoto-selection.Rmd +++ b/vignettes/airphoto-selection.Rmd @@ -407,10 +407,11 @@ times too large on 1,589 film frames from 13 rolls flown between 1974 and draw a 1:35000 frame 110 km across. A film frame states its height twice, once as `flying_height` and once as scale times focal length, so the two are compared. Where dividing by that factor brings them back into agreement the -corrected height is used and the frame is marked `"corrected_unit_slip"`. On 22 +corrected height is used and the frame is marked `"corrected_unit_slip"`. On 28 roll-heights the province's flight logbooks and the spacing between adjacent -frames settled the disagreement — dropped digits in the height, or a right height -beside a wrong scale — and those frames are sized from the measured height and +frames settled the disagreement — dropped digits in the height, a height ten +times too large where the factor is 10 and not 10.76, or a right height beside a +wrong scale — and those frames are sized from the measured height and marked `"corrected_roll_table"`. Where they disagree any other way nothing can say which is wrong, so the frame is sized from nominal scale and marked `"implausible"`. Your `flying_height` column is never overwritten. The bundled frames all agree with their own scale: