// Simple numbering for non-book documents #let equation-numbering = "(1)" #let callout-numbering = "1" #let subfloat-numbering(n-super, subfloat-idx) = { numbering("1a", n-super, subfloat-idx) } // Theorem configuration for theorion // Simple numbering for non-book documents (no heading inheritance) #let theorem-inherited-levels = 0 // Theorem numbering format (can be overridden by extensions for appendix support) // This function returns the numbering pattern to use #let theorem-numbering(loc) = "1.1" // Default theorem render function #let theorem-render(prefix: none, title: "", full-title: auto, body) = { if full-title != "" and full-title != auto and full-title != none { strong[#full-title.] h(0.5em) } body } // Some definitions presupposed by pandoc's typst output. #let content-to-string(content) = { if content.has("text") { content.text } else if content.has("children") { content.children.map(content-to-string).join("") } else if content.has("body") { content-to-string(content.body) } else if content == [ ] { " " } } #let horizontalrule = line(start: (25%,0%), end: (75%,0%)) #let endnote(num, contents) = [ #stack(dir: ltr, spacing: 3pt, super[#num], contents) ] #show terms.item: it => block(breakable: false)[ #text(weight: "bold")[#it.term] #block(inset: (left: 1.5em, top: -0.4em))[#it.description] ] // Some quarto-specific definitions. #show raw.where(block: true): set block( fill: luma(230), width: 100%, inset: 8pt, radius: 2pt ) #let block_with_new_content(old_block, new_content) = { let fields = old_block.fields() let _ = fields.remove("body") if fields.at("below", default: none) != none { // TODO: this is a hack because below is a "synthesized element" // according to the experts in the typst discord... fields.below = fields.below.abs } block.with(..fields)(new_content) } #let empty(v) = { if type(v) == str { // two dollar signs here because we're technically inside // a Pandoc template :grimace: v.matches(regex("^\\s*$")).at(0, default: none) != none } else if type(v) == content { if v.at("text", default: none) != none { return empty(v.text) } for child in v.at("children", default: ()) { if not empty(child) { return false } } return true } } // Subfloats // This is a technique that we adapted from https://github.com/tingerrr/subpar/ #let quartosubfloatcounter = counter("quartosubfloatcounter") #let quarto_super( kind: str, caption: none, label: none, supplement: str, position: none, subcapnumbering: "(a)", body, ) = { context { let figcounter = counter(figure.where(kind: kind)) let n-super = figcounter.get().first() + 1 set figure.caption(position: position) [#figure( kind: kind, supplement: supplement, caption: caption, { show figure.where(kind: kind): set figure(numbering: _ => { let subfloat-idx = quartosubfloatcounter.get().first() + 1 subfloat-numbering(n-super, subfloat-idx) }) show figure.where(kind: kind): set figure.caption(position: position) show figure: it => { let num = numbering(subcapnumbering, n-super, quartosubfloatcounter.get().first() + 1) show figure.caption: it => block({ num.slice(2) // I don't understand why the numbering contains output that it really shouldn't, but this fixes it shrug? [ ] it.body }) quartosubfloatcounter.step() it counter(figure.where(kind: it.kind)).update(n => n - 1) } quartosubfloatcounter.update(0) body } )#label] } } // callout rendering // this is a figure show rule because callouts are crossreferenceable #show figure: it => { if type(it.kind) != str { return it } let kind_match = it.kind.matches(regex("^quarto-callout-(.*)")).at(0, default: none) if kind_match == none { return it } let kind = kind_match.captures.at(0, default: "other") kind = upper(kind.first()) + kind.slice(1) // now we pull apart the callout and reassemble it with the crossref name and counter // when we cleanup pandoc's emitted code to avoid spaces this will have to change let old_callout = it.body.children.at(1).body.children.at(1) let old_title_block = old_callout.body.children.at(0) let children = old_title_block.body.body.children let old_title = if children.len() == 1 { children.at(0) // no icon: title at index 0 } else { children.at(1) // with icon: title at index 1 } // TODO use custom separator if available // Use the figure's counter display which handles chapter-based numbering // (when numbering is a function that includes the heading counter) let callout_num = it.counter.display(it.numbering) let new_title = if empty(old_title) { [#kind #callout_num] } else { [#kind #callout_num: #old_title] } let new_title_block = block_with_new_content( old_title_block, block_with_new_content( old_title_block.body, if children.len() == 1 { new_title // no icon: just the title } else { children.at(0) + new_title // with icon: preserve icon block + new title })) align(left, block_with_new_content(old_callout, block(below: 0pt, new_title_block) + old_callout.body.children.at(1))) } // 2023-10-09: #fa-icon("fa-info") is not working, so we'll eval "#fa-info()" instead #let callout(body: [], title: "Callout", background_color: rgb("#dddddd"), icon: none, icon_color: black, body_background_color: white) = { block( breakable: false, fill: background_color, stroke: (paint: icon_color, thickness: 0.5pt, cap: "round"), width: 100%, radius: 2pt, block( inset: 1pt, width: 100%, below: 0pt, block( fill: background_color, width: 100%, inset: 8pt)[#if icon != none [#text(icon_color, weight: 900)[#icon] ]#title]) + if(body != []){ block( inset: 1pt, width: 100%, block(fill: body_background_color, width: 100%, inset: 8pt, body)) } ) } // arxivai — Quarto Typst template partial. // Replaces Quarto's default `article()` with the arxivai preprint layout. // Both packages are importable from raw ```{=typst}``` blocks in the body: // #arxivai.operation-box(...) #arxivai.app-figure(...) #lettreai.badge(...) #import "lettreai.typ" as lettreai #import "arxivai.typ" as arxivai // --------------------------------------------------------------------------- // Re-theme Quarto's automatic callouts (same contract as lettreai: block // nesting kept structurally identical to Quarto's default so the crossref // figure show rule in definitions.typ keeps working). // --------------------------------------------------------------------------- #let callout( body: [], title: "Callout", background_color: rgb("#dddddd"), icon: none, icon_color: black, body_background_color: white, ) = { let accent = if icon_color == black { lettreai.colors.oxford-blue } else { icon_color } block( breakable: false, fill: lettreai.tint(accent, 0.94), stroke: (left: 3pt + accent, rest: 0.5pt + lettreai.tint(accent, 0.5)), width: 100%, radius: 3pt, block( inset: 1pt, width: 100%, below: 0pt, block( fill: lettreai.tint(accent, 0.84), width: 100%, inset: 8pt, )[#if icon != none [#text(accent, weight: 900)[#icon] ]#text(fill: lettreai.shade(accent, 0.25), weight: 700)[#title]]) + if (body != []) { block( inset: 1pt, width: 100%, block(fill: body_background_color, width: 100%, inset: 8pt, body)) } ) } // --------------------------------------------------------------------------- // arXiv preprint article — same signature contract as Quarto's default, // extended with keywords and running-title. // --------------------------------------------------------------------------- #let article( title: none, subtitle: none, authors: none, keywords: (), running-title: none, book: false, date: none, abstract-title: none, abstract: none, thanks: none, cols: 1, lang: "en", region: "US", font: none, fontsize: 10.5pt, title-size: 1.7em, subtitle-size: 1.15em, heading-family: none, heading-weight: "bold", heading-style: "normal", heading-color: black, heading-line-height: 0.65em, mathfont: none, codefont: none, linestretch: 1, sectionnumbering: none, linkcolor: none, citecolor: none, filecolor: none, toc: false, toc_title: none, toc_depth: none, toc_indent: 1.5em, doc, ) = { // PDF metadata. set document(title: title) set document( author: authors.map(a => lettreai.to-string(a.name)).join(", ", last: " & "), ) if authors != none and authors != () set document( keywords: keywords.map(k => lettreai.to-string(k)), ) if keywords != none and keywords != () set par(justify: true, leading: linestretch * 0.65em) set text(lang: lang, region: region, size: fontsize) show math.equation: set text(font: mathfont) if mathfont != none set heading(numbering: sectionnumbering) // Running header + footer; book mode gets chapter-aware heads and the // O'Reilly outer-margin geometry. let rt = if running-title != none { running-title } else if title != none { title } else { [] } set page( header: if book { arxivai.book-header() } else { arxivai.arxiv-header(rt) }, footer: if book { arxivai.book-footer() } else { arxivai.arxiv-footer() }, ) set page(margin: arxivai.book-margin) if book // Resolve fonts: explicit Quarto fonts win, else theme defaults. let resolved-font = if font != none { font } else { lettreai.body-font } let resolved-code = if codefont != none { codefont } else { lettreai.mono-font } let resolved-heading-color = if heading-color != black { heading-color } else { lettreai.colors.oxford-blue } let resolved-link = if linkcolor != none { rgb(lettreai.to-string(linkcolor)) } else { lettreai.colors.oxford-blue } // Paint everything with the shared theme, then the figure rules. show: lettreai.theme-rules.with( fonts: resolved-font, code-font: resolved-code, heading-color: resolved-heading-color, link-color: resolved-link, ) show: arxivai.arxiv-figure-rules // Book chapter rule is the innermost show, so it transforms level-1 // headings FIRST (inner rules apply first), replacing them with chapter // openers before the theme's h1 rule could wrap them in a container. // Synthetic non-outlined headings (e.g. the outline's own title) are // excluded inside book-heading-rules. show: doc2 => if book { arxivai.book-heading-rules(doc2) } else { doc2 } show ref: set text(fill: rgb(lettreai.to-string(citecolor))) if citecolor != none // Preprint front matter. arxivai.arxiv-title-block( title: title, subtitle: subtitle, authors: authors, date: date, ) if abstract != none { arxivai.arxiv-abstract( abstract, abstract-title: if abstract-title == none { [Abstract] } else { abstract-title }, ) } arxivai.arxiv-keywords(keywords) // Book mode: the front matter is a title page of its own. if book { pagebreak(weak: true) } // Optional table of contents (its own page in book mode). if toc { block(above: 0.5em, below: 2em, lettreai.lettreai-outline( title: if toc_title == none { auto } else { toc_title }, depth: toc_depth, indent: toc_indent, )) if book { pagebreak(weak: true) } } if cols > 1 { columns(cols, doc) } else { doc } } #let brand-color = (:) #let brand-color-background = (:) #let brand-logo = (:) #set page( paper: "a4", margin: (x: 2.4cm,y: 2.6cm,), numbering: "1", columns: 1, ) #show: doc => article( title: [Two readings of a position], subtitle: [Historical planetary models, historical star catalogues, and the animations that keep the two readings apart], authors: ( ( name: [Gerd Graßhoff], affiliation: [Philosophisches Institut, Forschungsinstitut des Deutschen Museums], email: [gerd.grasshoff\@hu-berlin.de], orcid: [0000-0002-4450-357X] ), ), abstract: [A question about a place in the sky, asked of a historical text, has two answers. The table reading is the longitude a named model produces from its own parameters. The sky reading is the apparent ecliptic longitude of date from the JPL ephemeris DE441 at the same instant. This paper describes a small expert that keeps the two answers side by side. The Almagest model was recomputed from a transcribed parameter file and checked against R. H. van Gent's calculator: on 200 dates and seven bodies the worst difference was far below one arcminute. The same ephemeris, read through Skyfield, agreed with JPL Horizons to 0.035 arcminutes on 50 Julian Dates for the Sun. Over those 200 dates the table reading and the sky reading differ by degrees, and for Mercury the largest difference was 12.03 degrees. Star catalogues from CDS are loaded with the counts printed in their ReadMe files. Charts follow the sky-view convention of d3-celestial and are drawn with D3. Six animations are computed from sourced numbers. Four further animations are gaps, because their parameters have not been read off a page. ], abstract-title: "Abstract", font: ("Source Sans 3",), sectionnumbering: "1.1", toc_title: [Table of contents], toc_depth: 3, doc, ) = Two readings Many records in early astronomy were computed from the tables then in use. A longitude in such a record is evidence about the table, and only afterwards evidence about the sky. The expert described here therefore answers a position question twice. The table reading is a function of a named model, a body, and an Egyptian date counted from noon on 1 Thoth of year 1 of Nabonassar. The sky reading is the apparent ecliptic longitude of date at the Julian Date that R. H. van Gent's calculator assigns to that day count. The difference is reported with the two longitudes. It is not folded back into either of them. The Julian Date has the form written in his script: the day count, plus 1,448,637, plus the fraction $\(22 -\(17 + 34\/60\)\/60\)\/24$. That expression is stored in the parameter file. It was not re-derived from a page of an edition. = Sources and licences Van Gent's pages on the history of astronomy, the calendar, and celestial cartography are mirrored locally and cited by URL and access date @vangent. The mirror is a source. It is not redistributed, and his JavaScript is not copied into the library. The Handy Tables, the Toledan Tables, and the Parisian Alfonsine Tables remain gaps. The Alfonsine Tables of Toledo, Tycho's solar theory, his model of Saturn, and Kepler's Martian models are recorded in the companion paper, with a provenance on each value. A digitised copy of the Prutenic Tables is stored and has not been transcribed. The parameter transcription for the Almagest is his. Toomer's translation is the edition one would re-read @toomer1984. The bibliographic line used here is the one printed in the Stellarium sky-culture description of the Greek Almagest. The page images in the PDAI collection `greek-astronomy/Ptolemy1984` were not re-read for the numbers in the parameter file, and the file records that gap. The star catalogues are the CDS editions of Ptolemy and Ulugh Beg @verbunt2012, of Tycho in the editions compared by Verbunt and van Gent @verbunt2010tycho, and of Hevelius @verbunt2010hevelius, together with the Hipparcos main catalogue @esa1997 and the constellation boundaries of Davenhall and Leggett @davenhall1989. Each catalogue is stored with the ecliptic longitude and latitude and the magnitude as printed, the epoch named in the ReadMe, the variant rows where the editors published them, and the Hipparcos number the editors printed as a cross-identification. Stellarium sky cultures are kept as separate files with their own licences. The MUL.APIN culture and the Greek Almagest culture are CC BY-ND 4.0 @hoffmann-mulapin@schmidt-almagest. Their paintings are not redrawn. A line in those files is a sequence of Hipparcos numbers. With the full file loaded, that line list can be drawn as published. #ref(, supplement: [Figure]) is the Small She-Bear from the Greek Almagest culture. The sequence of stars was not changed. The sky charts follow the coordinate convention of d3-celestial @frohn2015: in sky view, north is up and east is to the left. The drawing itself uses D3 version 7. The BSD-3 notice and the D3 notice are kept with the code. DE441 is the ephemeris of Park, Folkner, Williams, and Boggs @park2021. The kernel used here is part 1, 1,651,119,104 bytes. Positions are read with Skyfield 1.55. A separate bibliographic record for Skyfield was not checked. = The model registry One interface returns a longitude, a latitude, and the construction points the animation draws: the deferent centre, the equant, and the epicycle centre. Each model has a parameter file. A number in that file cites a locus, or the file says that the locus was not checked. An empty parameter leaves the model pending. No value is filled in to make a figure. #table( columns: (20%, 20%, 20%, 20%, 20%), align: (auto,auto,auto,auto,auto,), table.header([Model], [Bodies], [Parameters], [Checked against], [Status],), table.hline(), [Almagest], [7 bodies], [van Gent], [200 dates], [transcribed], [Handy Tables], [], [unread], [], [gap], [Prutenic], [], [digitised copy stored], [], [not transcribed], [Tychonic], [], [braheOO-02 and Carman 2020], [], [partial, in the companion paper], [Rudolphine], [], [KGW 10], [], [partial, tables not yet trusted], [Kepler, 1609--1611], [Mars], [Carman 2026a], [35 entries], [partial], [ACT, Systems A and B], [Moon], [Neugebauer 1975, unread], [], [gap], [Toledan Tables], [], [unread], [], [gap], [Eudoxan spheres], [], [equal radii], [page not checked], [reconstruction], ) The Almagest comparison used his calculator with the equation of time switched off, and the same choice in the reimplementation. On 200 dates from Nabonassar year 1, seven bodies, 1400 longitudes, the worst difference was $6.8 times 10^(- 12)$ arcminutes. The limit set in advance was one arcminute. The sky reading was checked separately. Fifty Julian Dates inside the span of the kernel were sent to JPL Horizons for the geocentric Sun, observer ecliptic longitude of date, and computed locally at the same UT1 instant. The worst difference was 0.035 arcminutes. A comparison by calendar date, made earlier, was several degrees off before 1582, because Horizons treats those dates as Julian. The gate does not mix the two calendars. On the 200 Egyptian dates, the table reading and the sky reading differ by degrees. The largest difference was 8.02° for the Sun, 8.76° for the Moon, 12.03° for Mercury, 10.73° for Venus, 8.87° for Mars, 4.92° for Jupiter, and 7.30° for Saturn. Mercury's difference is the one van Gent's introduction leads one to expect, and it is here a measured maximum on this set of dates, not a quotation. Inner planets take their mean longitude from the mean Sun. Using the true solar longitude instead shifts Mercury and Venus by the solar equation, about two and a half degrees at the epoch, on every date. That error was found against the calculator and removed. The outer planets did not show it. = Catalogues The loaded counts match the ReadMe files: Ptolemy 1028 stars, Ulugh Beg 1018, the 1627 edition of Tycho 1007 with 82 variant rows, Hevelius 1564. Ulugh Beg's zodiacal sign is zero-based in the editors' note, and the loader follows that note. Latitude is positive to the north, as the formula notes state. The boundary file for equinox 1875 contributes 1565 records @davenhall1989. Hipparcos cross-identifications printed by Verbunt and van Gent are stored with the historical rows. Catalogue I/239 is loaded at the ReadMe count, 118218 records @esa1997. Two hundred and sixty-three records leave the decimal right ascension blank. Those places are taken from the sexagesimal columns of the same record. A short gold dash in #ref(, supplement: [Figure]) runs from each Ptolemy place to the Hipparcos place of the same star, reduced to the apparent ecliptic of 1 January 137 TT, with the proper motion published in I/239. Each catalogue row is also written as a set of statements: membership in a constellation, a location, a magnitude, and a sameness with a Hipparcos number when the editors printed one. The statements use the predicates already defined for the normalisation model. They do not add a second structure for stars. = Charts A chart request is a JSON document: projection, sky view or globe view, stars, lines, and bodies. The same document is the contract for the drawing program and for the HTTP call that asks for a chart. Sky view reflects the horizontal axis, so east lies to the left when north is up. Globe view does not. An extremal boundary is not stroked. A membership line is dashed. A culture line, when the Hipparcos positions exist, is drawn in a second ink and labelled with the culture's name. A body may be marked as a table reading or a sky reading, and the two marks differ. The request for #ref(, supplement: [Figure]) is `requests/corona.json` in the expert package. The stars are the eight entries of Corona Borealis in the Ptolemy table of Verbunt and van Gent, seq 111 to 118, at the ecliptic coordinates printed there. The dashed line is a membership statement among three of them. A closed extremal path is present in the request and is not drawn. The colophon names the view, the projection, that omission, and a hash of the request. Two runs of the same request produce the same file. #figure([ #box(image("figures/corona.pdf", width: 78.0%)) ], caption: figure.caption( position: bottom, [ Corona Borealis in the Ptolemy catalogue. The dark dashed stroke is a membership line. The short gold dashes run to the Hipparcos places at 1 January 137 TT. An extremal path in the same request is not drawn. ]), kind: "quarto-float-fig", supplement: "Figure", ) #figure([ #box(image("figures/she-bear.pdf", width: 70.0%)) ], caption: figure.caption( position: bottom, [ The Small She-Bear in the Greek Almagest sky culture. The line is the published Hipparcos sequence, drawn in equatorial coordinates. ]), kind: "quarto-float-fig", supplement: "Figure", ) Print output is a PDF from the same SVG, at 300 dots per inch. The figures in this paper are those PDFs. = Animations An animation is a sequence of frames bound to a locus. A quotation is shown only after it has been checked against the text. None of the quotations aimed at below were checked against a page image in this run, so none are printed. The frames that could be computed from the parameter file and the catalogues were computed. The others say what is missing. #grid( columns: (1fr, 1fr), gutter: 1em, rows: 1, rect(stroke: none, width: 100%)[ #figure([ #box(image("figures/mars-day0.pdf")) ], caption: figure.caption( position: bottom, [ Mars on Egyptian day 0. Filled mark: Almagest longitude 357.48°. Open mark: DE441 longitude 355.16°. ]), kind: "quarto-float-fig", supplement: "Figure", ) ], rect(stroke: none, width: 100%)[ #figure([ #box(image("figures/mars-day425.pdf")) ], caption: figure.caption( position: bottom, [ The same construction on Egyptian day 425. Table longitude 228.85°, sky longitude 227.67°. ]), kind: "quarto-float-fig", supplement: "Figure", ) ], ) The Mars sequence covers 780 Egyptian days. The equant sequence holds the date fixed and adds the Earth, the deferent, the equant ray, and the epicycle one stage at a time (#ref(, supplement: [Figure])). Both sequences are table readings of the Almagest parameters. The sky mark on the Mars frames is the DE441 longitude. The books are Almagest IX--XI. The sentences were not checked, and the caption says so. #figure([ #box(image("figures/equant.pdf", width: 70.0%)) ], caption: figure.caption( position: bottom, [ The equant construction for Mars at Egyptian day 200, after all four stages. Table longitude 129.73°. ]), kind: "quarto-float-fig", supplement: "Figure", ) Jupiter and Saturn are drawn from the same model at Nabonassar years 2340 through 2351. The identification of year 2350 with 1603 is the arithmetic $1603 + 747$. It was not checked against a page of #emph[De stella nova]. Both marks in that animation are table readings. One star of Corona Borealis, Ptolemy seq 111, longitude 194.67° in the catalogue, is carried forward by the Almagest precession rate stored in the parameter file, one degree in 36525 days (#ref(, supplement: [Figure])). The open mark is Hipparcos 76267 at 196.09°, the apparent ecliptic of 1 January 137 TT. It stays at the catalogue epoch. The filled mark is the catalogue longitude plus the Almagest rate. The two readings of the boundary of Corona Borealis discussed by Grasshoff and Hoffmann and by Gysembergh, Williams, and Zingg stay two readings @grasshoff2024@gysembergh2025. Extremal stars are not drawn as a rectangle. No sentence from either paper is copied here. #figure([ #box(image("figures/precession.pdf", width: 70.0%)) ], caption: figure.caption( position: bottom, [ Catalogue longitude of Corona Borealis seq 111, carried 6.5° by the Almagest rate. The open mark is Hipparcos 76267 at the catalogue epoch. ]), kind: "quarto-float-fig", supplement: "Figure", ) Sirius is Ptolemy seq 818 in Canis Maior, Hipparcos 32349, ecliptic longitude 77.67° in that table. The animation gives the solar longitude from the Almagest model and the elongation from that star. A heliacal rising also depends on the horizon and on a latitude. Those are not computed (#ref(, supplement: [Figure])). The hippopede is the curve of the modern two-sphere reconstruction, drawn with equal radii (#ref(, supplement: [Figure])). Neugebauer's discussion is named in the parameter file. No page of it was checked, and the caption says that the radii are not a measured parameter of Eudoxus. #grid( columns: (1fr, 1fr), gutter: 1em, rows: 1, rect(stroke: none, width: 100%)[ #figure([ #box(image("figures/sirius.pdf")) ], caption: figure.caption( position: bottom, [ Solar elongation from the catalogue longitude of Sirius. The horizon is not part of the frame. ]), kind: "quarto-float-fig", supplement: "Figure", ) ], rect(stroke: none, width: 100%)[ #figure([ #box(image("figures/hippopede.pdf")) ], caption: figure.caption( position: bottom, [ The hippopede with equal radii, labelled as a reconstruction. ]), kind: "quarto-float-fig", supplement: "Figure", ) ], ) Four named animations remain gaps. Copernicus waits on a reading of #emph[De revolutionibus]. The Rudolphine frame waits on a trusted reading of KGW 10. Tycho's nova of 1572 waits on a sourced position. The Babylonian lunar step functions wait on the zone boundaries in Neugebauer. A gap frame prints that reason and draws no substitute curve. = Five observations, as annotations The companion paper on calculation tools gives the record format. The five plates below are the annotations: the report, the stated place on a short stretch of the ecliptic, and the Almagest derivation at Duke's Julian day. On the first of them the model lies 3.3 arcminutes from the stated longitude of 81;00, and DE441 lies 33.4 arcminutes from it. The same paper carries one entry of the 1609 ephemerides and the admission for Tycho's Saturn, where no opposition longitude was read. #figure([ #box(image("figures/mars-t1.pdf", width: 78.0%)) ], caption: figure.caption( position: bottom, [ Mars t1. Stated longitude 81;00. The model is 3.3 arcminutes west of it. DE441 is 33.4 arcminutes east of it. ]), kind: "quarto-float-fig", supplement: "Figure", ) #figure([ #box(image("figures/mars-t2.pdf", width: 78.0%)) ], caption: figure.caption( position: bottom, [ Mars t2. Stated longitude 148;50. The model is 5.8 arcminutes west of it. DE441 is 91.6 arcminutes east of it. ]), kind: "quarto-float-fig", supplement: "Figure", ) #figure([ #box(image("figures/mars-t3.pdf", width: 78.0%)) ], caption: figure.caption( position: bottom, [ Mars t3. Stated longitude 242;34. The model is 3.8 arcminutes west of it. DE441 is 29.8 arcminutes east of it. ]), kind: "quarto-float-fig", supplement: "Figure", ) #figure([ #box(image("figures/mars-t4.pdf", width: 78.0%)) ], caption: figure.caption( position: bottom, [ Mars t4. Stated longitude 241;36. The model is 2.6 arcminutes west of it. DE441 is 34.1 arcminutes east of it. ]), kind: "quarto-float-fig", supplement: "Figure", ) #figure([ #box(image("figures/mars-t5.pdf", width: 78.0%)) ], caption: figure.caption( position: bottom, [ Mars t5. Stated longitude 212;15. The model is 4.8 arcminutes east of it. DE441 is 21.6 arcminutes east of it. ]), kind: "quarto-float-fig", supplement: "Figure", ) Each animation is written as SVG frames, a screen PNG, a PDF, PNG files at 300 and 600 dots per inch, an MP4, a WebM, and a frame strip. A single HTML page holds a scrubber. The clock is D3's timer, and the same page can be stepped by hand. = Limits The Almagest parameters are a transcription whose primary check is still open. The agreement with van Gent's calculator shows that the reimplementation follows his transcription. It does not show that the transcription follows Toomer's pages. The sky gate covers the Sun at 50 instants. It does not by itself certify every planet at every date. The planet sky readings use the same kernel, the same time scale, and the same ecliptic of date. Mars, Jupiter, and Saturn are taken at the barycentre, because part 1 of DE441 does not carry a separate body for them. Delta T is not introduced as a separate series. Both sides of the Horizons gate are UT1. A few hours of Delta T move the Sun by arcminutes, which is why the instants have to be the same ones. Pending models stay pending. The boundary controversy is left as two readings. Culture paintings under a no-derivatives licence are not redrawn. The line lists are drawn as published. #bibliography(("astronomical-expert-bibliography.bib"))