Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
496 commits
Select commit Hold shift + click to select a range
f10edd8
fix(nixos): boot the selected camera — repoint extlinux DEFAULT + reboot
mrosseel Jun 26, 2026
f886940
docs(adr): correct Attic chunking claim in ADR 0001
mrosseel Jun 26, 2026
ef6b735
perf(nixos): split tetra3 + fonts out of pifinder-src
mrosseel Jun 26, 2026
9f842e6
feat(nixos): rollback to previous generation via Software screen
mrosseel Jun 26, 2026
3315105
feat(nixos): first-boot resolves full system from update manifest (st…
mrosseel Jun 27, 2026
8709bb3
build(nixos): manage tetra3 solver as a uv dependency, drop submodule
mrosseel Jun 29, 2026
19d21f5
docs(claude): comments state present facts, not change history
mrosseel Jun 29, 2026
4ddab4b
ci: add NixOS release workflow (SD image + migration tarball, fork-so…
mrosseel Jul 2, 2026
82b54e6
ci: NixOS release workflow (SD image + migration tarball, sha256, nat…
mrosseel Jul 2, 2026
a241dd0
fix(nixos): provision setuptools build backend for cedar-solve
mrosseel Jul 2, 2026
2b59040
feat(nixos): drop 4.9GB catalog images from SD image (fetched on demand)
mrosseel Jul 2, 2026
4cffb61
ci: sync release workflow + update_manifest (manifest population, mig…
mrosseel Jul 3, 2026
6875a6d
fix(migration): generate NM wifi keyfiles in Python pre-reboot with c…
mrosseel Jul 3, 2026
74bd21e
feat(nixos): libnm net-policy daemon (wired > wifi client > AP), repl…
mrosseel Jul 3, 2026
a443e2f
feat(nixos): trial/commit boot watchdog — auto-rollback only unproven…
mrosseel Jul 3, 2026
82e44a0
fix(nixos): tetra3 import straggler + identify builds by store path, …
mrosseel Jul 3, 2026
d0fc699
docs(nixos): grill session — recovery ladder, trial/confirm, update-m…
mrosseel Jul 3, 2026
f42d66b
feat(nixos): self-arming boot watchdog + failure splash — new generat…
mrosseel Jul 3, 2026
bad6f79
feat(nixos): retire pifinder-build.json, minimal migration tarball (A…
mrosseel Jul 4, 2026
badf162
feat(nixos): watchdog reports on confirmed-generation failure + recov…
mrosseel Jul 4, 2026
dc158bd
revert(migration): drop the Python keyfile generator from nixos — mig…
mrosseel Jul 4, 2026
44cebba
chore(nixos): drop Raspbian-side migration code — it runs pre-migrati…
mrosseel Jul 4, 2026
c9fc578
merge: upstream/main into nixos (Rev-4 hardware, tz fixes, obslist CS…
mrosseel Jul 4, 2026
a3334e5
feat(nixos): rev-4 hardware enablement — buzzer PWM0/GPIO12, gpio-pow…
mrosseel Jul 4, 2026
108db93
build(nixos): platform-mark hardware-only deps so uv sync works on x8…
mrosseel Jul 4, 2026
8a71ef4
fix(nixos): drop full linux-firmware (723MB) — Pi 4 needs only the Br…
mrosseel Jul 5, 2026
740ca3c
ci: sync release.yml (tarball budget guard)
mrosseel Jul 5, 2026
bbbadb5
docs(nixos): ADR 0007 — extlinux and kernels on ext4, FAT is firmware…
mrosseel Jul 5, 2026
042af10
fix(nixos): first-boot resolves from the manifest unconditionally — d…
mrosseel Jul 5, 2026
754e5aa
fix(nixos): bake the first-boot fallback target into the migration im…
mrosseel Jul 5, 2026
a1cd400
fix(nixos): first-boot writes current-build.json — the device identit…
mrosseel Jul 5, 2026
a05ec81
fix(nixos): stable udev name for the GPS UART — kernel ttyAMA numberi…
mrosseel Jul 5, 2026
51b8abf
fix(software): hide the exact running build from the unstable list
mrosseel Jul 5, 2026
6bf94c3
fix(gps): correct UBX NAV-SVINFO field offsets (off by one)
mrosseel Jul 5, 2026
b15b66d
fix(gps): decode NAV-SAT per spec for protVer >= 15 receivers
mrosseel Jul 5, 2026
77aef3b
fix(gps): don't count acquisition candidates as satellites seen
mrosseel Jul 6, 2026
a1107a5
fix(gps): count acquiring satellites as seen, not only code-locked ones
mrosseel Jul 6, 2026
21d2c95
merge: bring nixos up to date with upstream/main
mrosseel Jul 10, 2026
50603c5
fix(nixos): gate manifest availability on real cache presence
mrosseel Jul 10, 2026
336b42c
fix(nixos): override imx462 xclk to 74.25 MHz so the camera streams
mrosseel Jul 11, 2026
240d2dd
style: reformat with ruff 0.4.8 (the version CI pins)
mrosseel Jul 11, 2026
21bdc91
ci(nixos): drive lint/type/test checks with uv — this branch retired nox
mrosseel Jul 11, 2026
32e6324
feat: Gaia deep charts + limiting-magnitude entry
mrosseel Jun 18, 2026
e095fd2
feat(nixos): package gaia_stars deep-chart catalog in the system closure
mrosseel Jun 19, 2026
1ed9ab1
fix(deepchart): make healpy a hard dependency for Gaia charts
mrosseel Jul 8, 2026
a4c44c2
fix(object_details): keep text visible after drawing the simple cross…
mrosseel Jul 8, 2026
a060348
fix(types): align optional import ignores with uv environment
mrosseel Jul 11, 2026
44a3673
ci(nixos): drive lint/type/test checks with uv — this branch retired nox
mrosseel Jul 11, 2026
b6664c4
fix(sys): restart the full pifinder.service unit name so the NixOS su…
mrosseel Jul 11, 2026
6370802
feat(nixos): advertise Samba share over mDNS via avahi (_smb._tcp)
mrosseel Jul 11, 2026
028fbdb
feat(nixos): add on-device dev shell, wire into direnv, CI and docs
mrosseel Jul 12, 2026
436074e
fix(nixos): make Samba share guest-accessible like Raspbian (no passw…
mrosseel Jul 12, 2026
9ce56c5
merge: bring deepchart onto latest nixos
mrosseel Jul 12, 2026
7f471d0
fix(nixos): make Samba share guest-accessible like Raspbian (no passw…
mrosseel Jul 12, 2026
970121a
merge: bring deepchart onto latest nixos
mrosseel Jul 12, 2026
c8d7ae2
merge: bring nixos up to date with upstream/main
mrosseel Jul 13, 2026
a651263
feat(nixos): pygame SDL runpath fix + sys_utils dev fallback for loca…
mrosseel Jul 13, 2026
7e81c63
fix(nixos): drop unwired pygame keyboard drain that stole keypresses
mrosseel Jul 13, 2026
7ca7c94
refactor(nixos): gate on-device dev git/rsync behind pifinder.devMode
mrosseel Jul 13, 2026
50a0b2f
fix(imu): bit-banged I2C bus + resilient IMU monitor loop
mrosseel Jul 13, 2026
80a4513
fix(nixos): expose SkySafari and stabilize mDNS
mrosseel Jul 13, 2026
c718903
merge: bring deepchart onto latest nixos
mrosseel Jul 13, 2026
00542ad
merge: reconcile deepchart PR history
mrosseel Jul 13, 2026
30a6dc1
fix(solver): re-open cedar shmem per solver generation
mrosseel Jul 13, 2026
b0f4136
Make `nix develop` work on MacOS (apple silicon)
jscheidtmann Jun 29, 2026
0ee8f68
fix(update): report the running build, not the pre-reboot label
mrosseel Jul 13, 2026
601cfee
feat(object-chart): configurable object MARK + ANIM on Gaia chart
mrosseel Jul 13, 2026
0d08d00
fix(web): network settings apply live — no restart, honest button
mrosseel Jul 13, 2026
63c8cb1
feat: screen off burn-in protection + debug camera test mode
mrosseel Jun 18, 2026
1cc86e5
style: format camera interface with CI ruff
mrosseel Jul 11, 2026
f6f571d
merge: bring deepchart onto latest nixos
mrosseel Jul 13, 2026
ed2f7e2
feat: dev mode toggle gating the unstable update channel
mrosseel Jul 13, 2026
ae517fc
merge: update deepchart from nixos
mrosseel Jul 13, 2026
84401e8
merge: update deepchart from upstream main
mrosseel Jul 13, 2026
a6f9de5
merge: update deepchart from upstream main
mrosseel Jul 13, 2026
4d995b8
fix(object-chart): correct Gaia chart rotation to match POSS/eyepiece
mrosseel Jul 14, 2026
36ff974
feat(obj-chart): Fallback/Custom mark modes pick the glyph shape dire…
mrosseel Jul 14, 2026
33f3df7
fix(obj-chart): pre-select Fallback/Custom mark mode on menu re-entry
mrosseel Jul 14, 2026
84d865a
feat(focus): add raw multi-star focus views
mrosseel Jul 14, 2026
8b33c1d
merge: sync upstream/main into nixos
mrosseel Jul 14, 2026
2f23e8f
feat(sqm): self-calibrating SQM: raw-green photometry, per-frame blac…
mrosseel Jul 14, 2026
102a5e8
merge: bring nixos into deepchart
mrosseel Jul 14, 2026
f8a76ab
fix(main): require 7x SQUARE dev-mode gesture within a 10s window
mrosseel Jul 14, 2026
a296fce
fix(main): require 7x SQUARE dev-mode gesture within a 10s window
mrosseel Jul 14, 2026
edb35a5
fix(focus): center quadrants below title bar
mrosseel Jul 14, 2026
af27c3b
test(focus): cover all display geometries
mrosseel Jul 14, 2026
40877e0
feat(sqm): self-calibrating SQM: raw-green photometry, per-frame blac…
mrosseel Jul 14, 2026
fe331c1
fix(focus): keep preview compatible with Python 3.9
mrosseel Jul 14, 2026
88c1187
refactor(sqm): remove dead noise-floor wiring from the SQM path
mrosseel Jul 14, 2026
21ff721
fix(sqm): robust mzero — median over stars, 70% nonlinearity cut, B-V…
mrosseel Jul 14, 2026
3fd4b0e
fix(sqm): robust mzero — median over stars, 70% nonlinearity cut, B-V…
mrosseel Jul 14, 2026
a2943af
fix: accept -1 (screen off) power state and silence spurious log errors
mrosseel Jul 14, 2026
d4fe46f
feat(sqm): per-sensor sky-passband offset (V-band meter scale)
mrosseel Jul 14, 2026
6e867f5
fix(test-mode): re-apply fake GPS at startup when test mode was persi…
mrosseel Jul 14, 2026
9cefd1c
feat(sqm): per-sensor sky-passband offset (V-band meter scale)
mrosseel Jul 14, 2026
8d98731
fix: make camera test mode follow shared state so it survives restarts
mrosseel Jul 14, 2026
33c0efe
fix(focus): stabilize star centroids and quadrant tracking
mrosseel Jul 14, 2026
e9c1ad9
merge: bring nixos into deepchart
mrosseel Jul 14, 2026
ef57317
fix(sqm): compute SQM on imx462/290 and hide default until measured
mrosseel Jul 14, 2026
df332b4
fix(sqm): set imx462/imx290 raw bias_offset to measured 238 ADU
mrosseel Jul 15, 2026
d2e12f0
fix(sqm): stop PedestalEstimator overriding the measured black level
mrosseel Jul 15, 2026
edd23a4
fix(sqm): CAL wizard must send typed ReloadSqmCalibration to the solver
mrosseel Jul 15, 2026
390e471
fix(sqm): set imx462/imx290 raw bias_offset to measured 238 ADU
mrosseel Jul 15, 2026
4d4d7d8
fix(sqm): stop PedestalEstimator overriding the measured black level
mrosseel Jul 15, 2026
fab62ad
fix(sqm): CAL wizard must send typed ReloadSqmCalibration to the solver
mrosseel Jul 15, 2026
0ee63d6
fix(sqm): set imx462/imx290 raw bias_offset to measured 238 ADU
mrosseel Jul 15, 2026
5b94834
fix(sqm): stop PedestalEstimator overriding the measured black level
mrosseel Jul 15, 2026
6f8ffce
fix(sqm): compute SQM on imx462/290 and hide default until measured
mrosseel Jul 15, 2026
39470c0
feat(sqm): randomize sweep exposure order
mrosseel Jul 15, 2026
4c187c1
fix(sqm): CAL wizard must send typed ReloadSqmCalibration to the solver
mrosseel Jul 15, 2026
2f1f0e9
feat(sqm): randomize sweep exposure order
mrosseel Jul 15, 2026
cc839c9
fix(focus): show single star after quadrant view
mrosseel Jul 15, 2026
14a7a0e
feat(sqm): record effective photometry constants in sweep metadata
mrosseel Jul 15, 2026
8e50e55
feat(sqm): record effective photometry constants in sweep metadata
mrosseel Jul 15, 2026
7f7ab18
feat(focus): stretch full-frame Image view
mrosseel Jul 15, 2026
0507b39
fix(focus): refine readouts, Image contrast, and Single zoom
mrosseel Jul 15, 2026
42c1add
fix(comets): restore vectorized propagation in Nix env
mrosseel Jul 15, 2026
041c92d
docs(sqm): band offset is site-regime dependent, not a pure sensor co…
mrosseel Jul 15, 2026
fa02bec
docs(sqm): band offset is site-regime dependent, not a pure sensor co…
mrosseel Jul 15, 2026
811bfa7
fix(nixos): keep current Skyfield with batched comet support
mrosseel Jul 15, 2026
d72c840
fix(focus): center sparkline gap on readout
mrosseel Jul 15, 2026
107cb7b
fix(focus): space history from rendered number outline
mrosseel Jul 15, 2026
660096d
feat(sqm): session anchor from a reference-meter reading
mrosseel Jul 15, 2026
9a8e173
feat(sqm): session anchor from a reference-meter reading
mrosseel Jul 15, 2026
64eba8c
fix(focus): expire stale history while frames are missing
mrosseel Jul 15, 2026
e88a595
feat(sqm): measured imx296 constants from the 2025-10-31 mono sweep
mrosseel Jul 15, 2026
63030cd
feat(sqm): measured imx296 constants from the 2025-10-31 mono sweep
mrosseel Jul 15, 2026
3ad469a
merge: sqm-fix into nixos
mrosseel Jul 15, 2026
948906c
refactor(sqm): delete the processed-image (PNG) SQM path
mrosseel Jul 15, 2026
d89ff95
merge: sqm-fix into nixos (session anchor, imx296 constants, PNG-path…
mrosseel Jul 15, 2026
1f78ca4
feat(sqm): session anchor from a reference-meter reading
mrosseel Jul 15, 2026
8d4a88e
feat(sqm): measured imx296 constants from the 2025-10-31 mono sweep
mrosseel Jul 15, 2026
7e8a550
refactor(sqm): delete the processed-image (PNG) SQM path
mrosseel Jul 15, 2026
4a3af5e
refactor(sqm): rename Anchor feature to Correct; drop dead correction…
mrosseel Jul 15, 2026
672e727
fix(devshell): add bashInteractive so bash has progcomp/readline
mrosseel Jul 15, 2026
d4cba06
merge: nixos into deepchart
mrosseel Jul 15, 2026
f5955e1
fix(sqm): guard None dark_current in calc; always record sweep SQM me…
mrosseel Jul 15, 2026
85d2524
fix(sqm): create the SQM calculator lazily, on the first solve
mrosseel Jul 16, 2026
730d044
merge: sqm-fix into nixos (lazy SQM calculator creation)
mrosseel Jul 16, 2026
3754ef3
merge: nixos into deepchart (lazy SQM calculator creation)
mrosseel Jul 16, 2026
a788cdc
feat(catalog): add observable asteroids
mrosseel Jul 15, 2026
6a9b52b
test(solver): pin cedar shmem reopen-on-restart contract
mrosseel Jul 16, 2026
ff5cbb1
merge: nixos into deepchart (cedar shmem restart-recovery test)
mrosseel Jul 16, 2026
b95cf39
merge: deepchart into observable-asteroids (cedar shmem restart-recov…
mrosseel Jul 16, 2026
9d933b5
fix(web): accept comma or period decimal separator in location entry
mrosseel Jul 16, 2026
845eac3
fix(web): accept comma or period decimal separator in location entry
mrosseel Jul 16, 2026
2692406
fix(web): accept comma or period decimal separator in location entry
mrosseel Jul 16, 2026
396bc58
feat(sqm): recalibrate hq sqm_band_offset from 4 referenced sweeps
mrosseel Jul 16, 2026
c3bed19
fix(sqm): reject noise-collapsed wing boundaries; recalibrate band of…
mrosseel Jul 16, 2026
0d2edf0
feat(ui): software update screen polish
mrosseel Jul 16, 2026
5bc35d3
feat(ui): software update screen polish
mrosseel Jul 16, 2026
552b198
merge: sqm-fix into nixos (wing-boundary floor, band offset recalibra…
mrosseel Jul 16, 2026
22a1f64
merge: nixos into deepchart (wing-boundary floor, band offset recalib…
mrosseel Jul 16, 2026
6aed601
fix(ui): lift update status line 2px clear of the screen edge
mrosseel Jul 16, 2026
adc440a
fix(ui): lift update status line 2px clear of the screen edge
mrosseel Jul 16, 2026
e14251a
chore: retrigger build (built_at manifest stamp)
mrosseel Jul 16, 2026
d987aab
chore: retrigger build (built_at manifest stamp)
mrosseel Jul 16, 2026
90ad7c7
fix(software): disable migration unlock on NixOS systems
mrosseel Jul 16, 2026
720cdb2
fix(software): disable migration unlock on NixOS systems
mrosseel Jul 16, 2026
0e10a6b
refactor(ui): replace 7x-square gestures with Dev Mode menu toggle
mrosseel Jul 16, 2026
1b1fdc3
refactor(ui): replace 7x-square gestures with Dev Mode menu toggle
mrosseel Jul 16, 2026
8090ec9
feat(ui): software update screen polish
mrosseel Jul 16, 2026
fdac4d1
merge: sqm-fix into observable-asteroids (wing-boundary floor, band o…
mrosseel Jul 16, 2026
190ac91
chore: retrigger build (built_at manifest stamp)
mrosseel Jul 16, 2026
a6e8f26
refactor(software): delete the NixOS migration UI flow
mrosseel Jul 16, 2026
56e0a5f
refactor(software): delete the NixOS migration UI flow
mrosseel Jul 16, 2026
812f9b4
chore: retrigger build (built_at manifest stamp)
mrosseel Jul 16, 2026
39cf68c
chore: retrigger build (built_at manifest stamp)
mrosseel Jul 16, 2026
6d009dc
chore: retrigger build (built_at manifest stamp)
mrosseel Jul 16, 2026
aac1347
fix(sqm): derotate centroids onto the raw; growth-curve aperture corr…
mrosseel Jul 17, 2026
2d8bbb8
merge: sqm-fix into nixos (SQM rotation fix, growth-curve aperture co…
mrosseel Jul 17, 2026
43c9e9d
merge: nixos into deepchart (SQM rotation fix, growth-curve aperture …
mrosseel Jul 17, 2026
b4088f9
merge: sqm-fix into observable-asteroids (SQM rotation fix, growth-cu…
mrosseel Jul 17, 2026
57b0a9d
feat(sqm): refit imx296 band offset for the growth-curve pipeline
mrosseel Jul 17, 2026
e3cd1cb
merge: sqm-fix into nixos (imx296 band offset refit)
mrosseel Jul 17, 2026
3d743e0
merge: nixos into deepchart (imx296 band offset refit)
mrosseel Jul 17, 2026
fbe42b9
merge: sqm-fix into observable-asteroids (imx296 band offset refit)
mrosseel Jul 17, 2026
ac9d581
fix(sqm): exposure-invariant zero point; wing-safe annulus; sensor te…
mrosseel Jul 17, 2026
88dd132
merge: sqm-fix into nixos (exposure-invariant zero point, wing-safe a…
mrosseel Jul 17, 2026
af023f1
merge: nixos into deepchart (exposure-invariant zero point, wing-safe…
mrosseel Jul 17, 2026
40a4e6d
merge: sqm-fix into observable-asteroids (exposure-invariant zero poi…
mrosseel Jul 17, 2026
0e40dce
refactor(sqm): port orthogonal measurement fixes onto deepchart
mrosseel Jul 17, 2026
6c886bc
feat(sqm): black-level tracking + cloud/dew guard
mrosseel Jul 17, 2026
a4d87bc
feat(sqm): radiometer-first architecture with star-maintained calibra…
mrosseel Jul 17, 2026
f16dbde
merge: upstream main into sqm-fix (SCREEN_ROTATE_AMOUNTS x solve_rota…
mrosseel Jul 17, 2026
7e85d48
merge: sqm-fix into nixos (radiometer-first SQM, black-level tracking…
mrosseel Jul 17, 2026
fd75708
merge: nixos into deepchart (radiometer-first SQM, black-level tracki…
mrosseel Jul 17, 2026
be8b7b6
merge: sqm-fix into observable-asteroids (radiometer-first SQM + upst…
mrosseel Jul 17, 2026
95a4e59
feat(sqm): per-image sweep metadata JSON with radiometry and sensor temp
mrosseel Jul 17, 2026
be8aa92
merge: sqm-fix into nixos (per-image sweep metadata JSON)
mrosseel Jul 17, 2026
f13c527
feat(sqm): dump full rolling-window tracker state into sweep metadata
mrosseel Jul 17, 2026
85e898a
merge: sqm-fix into nixos (tracker window dumps in sweeps)
mrosseel Jul 17, 2026
ce1f5eb
fix(nixos-upgrade): decide camera boot entry against the new build, n…
mrosseel Jul 17, 2026
c09248d
fix(sqm): black-level lease + calibration evidence for imx296 charact…
mrosseel Jul 18, 2026
8829538
merge: sqm-fix into nixos (black-level lease, calibration evidence, s…
mrosseel Jul 18, 2026
b12bf46
fix(sqm): CI green — mypy Optional narrowing in color_index, ruff-for…
mrosseel Jul 18, 2026
72d8fe1
feat(telemetry): record radiometer samples as radio events
mrosseel Jul 18, 2026
1c99851
feat(sqm)!: remove SQM Correct; SWEEP takes the down menu slot
mrosseel Jul 18, 2026
4730924
merge: sqm-fix into nixos (radio telemetry, SQM Correct removal, SWEE…
mrosseel Jul 18, 2026
e9a16a9
chore(sqm): drop editorializing marking-menu comment
mrosseel Jul 18, 2026
6cfdb75
merge: sqm-fix into nixos (comment cleanup)
mrosseel Jul 18, 2026
f711f27
chore(nixos): untrack nix build result symlinks
mrosseel Jul 18, 2026
7a550fd
fix(test): drop stale UIMigration refs from test_ui_modules
mrosseel Jul 18, 2026
5544391
merge: nixos into deepchart (untrack result symlinks)
mrosseel Jul 18, 2026
1e834f9
merge: nixos into observable-asteroids
mrosseel Jul 18, 2026
0851ca5
fix(test): match test_radec_entry to current radec_entry API
mrosseel Jul 18, 2026
84a2ec3
fix(test): match test_radec_entry to current radec_entry API
mrosseel Jul 18, 2026
d7823a0
fix(test): match test_radec_entry to current radec_entry API
mrosseel Jul 18, 2026
4f195af
feat(sqm): per-frame optical-black pedestal for IMX290/IMX462
mrosseel Jul 18, 2026
6c4db85
docs(sqm): document per-frame optical-black pedestal (IMX290/462)
mrosseel Jul 18, 2026
fa9b1a2
build(nixos): drop dev caches from the runtime source closure
mrosseel Jul 18, 2026
5e8f0b1
merge: optical-black SQM into deepchart
mrosseel Jul 19, 2026
7ca99df
merge: optical-black SQM into observable-asteroids
mrosseel Jul 19, 2026
06bcd47
chore(sqm): disable black-level tracker to isolate optical-black pede…
mrosseel Jul 19, 2026
8b7d664
fix(sqm): scale photometry and wing geometry to the photometry image
mrosseel Jul 19, 2026
47f088a
fix(sqm): tracked black level supersedes static bias, wizard or profile
mrosseel Jul 19, 2026
2a9c94d
fix(sqm): wizard captures gate on actual exposure and skip clamp settle
mrosseel Jul 19, 2026
2354693
fix(nixos): content-address the kernel OB patch to stop repo-wide reb…
mrosseel Jul 19, 2026
e8ae101
feat(sqm): correct IMX462 dark-sky spectral excess
mrosseel Jul 19, 2026
53a124d
Merge pull request #30 from mrosseel/sqm-optical-black
mrosseel Jul 21, 2026
20267a1
merge: IMX462 dark-sky correction into observable-asteroids
mrosseel Jul 21, 2026
6d68b5a
merge: sync deepchart after IMX462 SQM integration
mrosseel Jul 21, 2026
2d875f9
ci(nixos): ARM-primary testable build (ubuntu-24.04-arm first, pi5 la…
mrosseel Jul 21, 2026
2bbdd5c
ci(nixos): ARM-primary testable build (ubuntu-24.04-arm first, pi5 la…
mrosseel Jul 21, 2026
9ef18cc
ci(nixos): ARM-primary testable build (ubuntu-24.04-arm first, pi5 la…
mrosseel Jul 21, 2026
fa09f51
feat(telemetry): section checklist, size cap, recomputable radio event
mrosseel Jul 22, 2026
f4a59a3
feat(telemetry): section checklist, size cap, recomputable radio event
mrosseel Jul 22, 2026
3d709e7
feat(telemetry): section checklist, size cap, recomputable radio event
mrosseel Jul 22, 2026
53784be
merge: sync nixos into deepchart (IMX462 dark-sky SQM/airglow + telem…
mrosseel Jul 23, 2026
5e950cb
merge: sync deepchart (gaia charts + SQM/airglow) into observable-ast…
mrosseel Jul 23, 2026
1ca2fe5
merge: upstream/main into nixos
mrosseel Jul 24, 2026
5fb9e03
merge: nixos (upstream/main sync) into deepchart
mrosseel Jul 24, 2026
846fd2e
merge: deepchart (upstream/main sync) into observable-asteroids
mrosseel Jul 24, 2026
e7f7a43
merge: upstream/main into nixos (full-sensor sweeps, PR #544)
mrosseel Jul 29, 2026
ab64863
merge: nixos (upstream sync, full-sensor sweeps) into deepchart
mrosseel Jul 29, 2026
ea6c0c2
merge: deepchart (upstream sync, full-sensor sweeps) into observable-…
mrosseel Jul 29, 2026
4de3901
fix(ci): manifest job checks out the PR base, not the default branch
mrosseel Jul 29, 2026
d51ae62
merge: nixos (CI manifest-checkout fix) into deepchart
mrosseel Jul 29, 2026
67b8cd2
merge: deepchart (CI manifest-checkout fix) into observable-asteroids
mrosseel Jul 29, 2026
7165024
chore(nixos): retire the preview label and correct stale channel docs
mrosseel Aug 15, 2026
1a4e718
Merge pull request #35 from mrosseel/nixos-retire-preview
mrosseel Aug 17, 2026
b8230c0
fix(nearby): rank against true angular distance, in a bounded window
mrosseel Aug 17, 2026
7567a65
Merge pull request #38 from mrosseel/fix/nearby-ranking
mrosseel Aug 17, 2026
11ff5f2
merge: current nixos into deepchart
mrosseel Aug 17, 2026
5ebd13f
perf(observations): resolve logged identities once and share the cache
mrosseel Aug 18, 2026
dc7b527
merge: current upstream/main into nixos
mrosseel Aug 18, 2026
1937756
merge: current nixos into deepchart
mrosseel Aug 18, 2026
2d8ac58
fix: skip image regeneration in non-image display modes
mrosseel Mar 18, 2026
c7ad04f
merge: current deepchart into observable-asteroids
mrosseel Aug 18, 2026
d07144d
fix(object-chart): restore fixed eyepiece orientation baseline (#41)
mrosseel Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
use nix
watch_file flake.nix flake.lock shell.nix
watch_file nixos/pkgs/uv-python.nix
watch_file python/pyproject.toml python/uv.lock
Empty file modified .github/scripts/publish_manifest.sh
100644 → 100755
Empty file.
84 changes: 76 additions & 8 deletions .github/scripts/update_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,19 @@
import argparse
import json
import re
import urllib.error
import urllib.request
from datetime import datetime, timezone
from pathlib import Path


STORE_PATH_RE = re.compile(r"^/nix/store/[a-z0-9]+-[A-Za-z0-9._+=?,-]+$")

# The binary caches devices resolve upgrades from. A build is only advertised
# as available once one of these actually serves its narinfo — see set_available.
DEV_CACHE = "https://cache.pifinder.eu/pifinder"
RELEASE_CACHE = "https://cache.pifinder.eu/pifinder-release"
DEFAULT_CACHES = (DEV_CACHE, RELEASE_CACHE)
EMPTY_MANIFEST = {
"schema": 1,
"generated_at": None,
Expand Down Expand Up @@ -49,14 +57,54 @@ def valid_store_path(value: str | None) -> bool:
return isinstance(value, str) and STORE_PATH_RE.fullmatch(value) is not None


def set_available(entry: dict) -> dict:
if valid_store_path(entry.get("store_path")):
entry["available"] = True
entry.pop("reason", None)
else:
def _narinfo_url(store_path: str, cache: str) -> str:
digest = Path(store_path).name.split("-", 1)[0]
return f"{cache.rstrip('/')}/{digest}.narinfo"


def cache_serves(store_path: str, caches, timeout: int = 15) -> bool | None:
"""Whether the closure is actually downloadable from a cache right now.

Returns True if any cache serves the narinfo (200); False only if every
cache answered and none had it (a real 404 everywhere); None if a cache
could not be reached, so presence is unknown. Publishing treats anything
other than True as "do not advertise", so a failed push or a down cache can
never advertise a build the device cannot fetch.
"""
saw_404 = False
for cache in caches:
url = _narinfo_url(store_path, cache)
try:
with urllib.request.urlopen(url, timeout=timeout) as resp:
if resp.status == 200:
return True
except urllib.error.HTTPError as exc:
if exc.code == 404:
saw_404 = True
else:
return None
except (urllib.error.URLError, OSError, TimeoutError):
return None
return False if saw_404 else None


def set_available(entry: dict, caches=DEFAULT_CACHES, verify: bool = True) -> dict:
store_path = entry.get("store_path")
if not valid_store_path(store_path):
entry["store_path"] = None
entry["available"] = False
entry.setdefault("reason", "no build")
return entry

if verify:
present = cache_serves(store_path, caches)
if present is not True:
entry["available"] = False
entry["reason"] = "not in cache" if present is False else "cache unreachable"
return entry

entry["available"] = True
entry.pop("reason", None)
return entry


Expand All @@ -79,11 +127,17 @@ def key(item: dict) -> tuple[int, int, str]:
return sorted(entries, key=key)


def resolve_verify(args: argparse.Namespace) -> tuple[tuple[str, ...], bool]:
caches = tuple(args.verify_cache) if args.verify_cache else DEFAULT_CACHES
return caches, not args.skip_cache_check


def update_build(args: argparse.Namespace) -> None:
manifest = load_manifest(args.manifest)
channels = manifest["channels"]
store_path = args.store_path or None
short_sha = (args.head_sha or args.sha)[:7]
caches, verify = resolve_verify(args)

if args.pr_number:
number = int(args.pr_number)
Expand All @@ -100,7 +154,7 @@ def update_build(args: argparse.Namespace) -> None:
"store_path": store_path,
"built_at": now_iso(),
}
set_available(entry)
set_available(entry, caches, verify)
channels["unstable"] = replace_entry(
channels["unstable"],
lambda item: item.get("kind") == "pr"
Expand All @@ -120,7 +174,7 @@ def update_build(args: argparse.Namespace) -> None:
"store_path": store_path,
"built_at": now_iso(),
}
set_available(entry)
set_available(entry, caches, verify)
channels["unstable"] = replace_entry(
channels["unstable"],
lambda item: item.get("kind") == "trunk"
Expand All @@ -135,6 +189,7 @@ def update_build(args: argparse.Namespace) -> None:

def update_release(args: argparse.Namespace) -> None:
manifest = load_manifest(args.manifest)
caches, verify = resolve_verify(args)
channel = "beta" if args.release_type == "beta" else "stable"
entry = {
"kind": "release",
Expand All @@ -150,7 +205,7 @@ def update_release(args: argparse.Namespace) -> None:
"migration_sha256_url": args.migration_sha256_url or None,
"built_at": now_iso(),
}
set_available(entry)
set_available(entry, caches, verify)
manifest["channels"][channel] = replace_entry(
manifest["channels"][channel],
lambda item: item.get("kind") == "release" and item.get("label") == args.tag,
Expand All @@ -159,6 +214,17 @@ def update_release(args: argparse.Namespace) -> None:
save_manifest(args.manifest, manifest)


def add_cache_args(sub: argparse.ArgumentParser) -> None:
# A build is advertised as available only if one of these caches actually
# serves its narinfo. Repeatable; defaults to the dev + release caches.
sub.add_argument("--verify-cache", action="append", metavar="URL")
sub.add_argument(
"--skip-cache-check",
action="store_true",
help="advertise on store-path syntax alone (tests / offline only)",
)


def parser() -> argparse.ArgumentParser:
root = argparse.ArgumentParser()
sub = root.add_subparsers(dest="command", required=True)
Expand All @@ -176,6 +242,7 @@ def parser() -> argparse.ArgumentParser:
build.add_argument("--head-repo")
build.add_argument("--head-ref")
build.add_argument("--head-sha")
add_cache_args(build)
build.set_defaults(func=update_build)

release = sub.add_parser("release")
Expand All @@ -190,6 +257,7 @@ def parser() -> argparse.ArgumentParser:
release.add_argument("--migration-sha256-url")
release.add_argument("--title")
release.add_argument("--notes")
add_cache_args(release)
release.set_defaults(func=update_release)

return root
Expand Down
27 changes: 22 additions & 5 deletions .github/workflows/nox.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
name: nox
# The nixos branch manages the Python environment with uv (pyproject.toml +
# uv.lock) instead of nox + requirements*.txt, so this runs the same checks
# (ruff, mypy, pytest) through uv. The workflow keeps upstream's "nox" name so
# PR check wiring stays identical across branches.
#
# Run on pushes to the long-lived branches and on pull requests. Restricting
# `push` to main/release means a push to a feature branch with an open PR only
# triggers the `pull_request` run (not also a `push` run), so a PR gets a single
Expand All @@ -18,9 +23,21 @@ jobs:
working-directory: ./python
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
submodules: true
- uses: wntrblm/nox@2024.04.15
with:
python-versions: "3.9"
- run: nox -s lint format type_hints smoke_tests unit_tests ui_tests
enable-cache: true
# Same ruff pin as upstream main's noxfile, so shared files keep one style.
- name: Lint
run: uvx ruff@0.4.8 check --config "builtins=['_']" .
- name: Format check
run: uvx ruff@0.4.8 format --check .
- name: Sync environment
run: uv sync --frozen
- name: Type check
run: uv run mypy PiFinder
- name: Smoke tests
run: uv run pytest -m smoke
- name: Unit tests
run: uv run pytest -m unit
- name: UI module tests
run: uv run pytest -m integration tests/test_ui_modules.py
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,16 @@ case/my_printer
.direnv/
**/.claude/*
!**/.claude/skills/
.agents/
.codex/
.serena/
astro_data/comets.txt

# users GPS data
test_ubx
test_ubx/*
python/telemetry_analysis/

# nix build result symlinks
result
result-*
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

38 changes: 25 additions & 13 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/saltstack/mirrors-nox
rev: 'v2022.11.21' # Use the sha / tag you want to point at
- repo: local
hooks:
- id: nox
files: ^.*\.py$
args:
- -f
- python/noxfile.py
- -s
- type_hints
- smoke_tests
- --
- id: ruff-lint
name: ruff lint
entry: bash -c 'cd python && ruff check'
language: system
files: ^python/.*\.py$
pass_filenames: false
- id: ruff-format
name: ruff format check
entry: bash -c 'cd python && ruff format --check'
language: system
files: ^python/.*\.py$
pass_filenames: false
- id: mypy
name: mypy type check
entry: bash -c 'cd python && mypy .'
language: system
files: ^python/.*\.py$
pass_filenames: false
- id: smoke-tests
name: smoke tests
entry: bash -c 'cd python && pytest -m smoke'
language: system
files: ^python/.*\.py$
pass_filenames: false
92 changes: 53 additions & 39 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,65 +22,46 @@ Maintainers can make `fresh` root on `main` automatically per clone (without cha
git remote set-head origin main
```

**Initialise the `tetra3` submodule in every new worktree.** `python/PiFinder/tetra3` is a git submodule (the `cedar-solve`/Tetra3 solver); the importable package is its inner `tetra3/tetra3/` dir, surfaced through the tracked symlink `python/tetra3`. `git worktree add` / `EnterWorktree` does **not** populate submodules, so a fresh worktree starts with an empty submodule dir and a dangling symlink. Any test that imports the solver then fails with `ModuleNotFoundError: No module named 'tetra3'` (or `cedar_detect_pb2`) — this is a missing checkout, **not** a code problem, so don't reach for `PYTHONPATH` hacks. Fix it once per worktree:

```bash
git submodule update --init python/PiFinder/tetra3
```

mypy also needs this: its config points at `python/PiFinder/tetra3/tetra3`, so `nox -s type_hints` can't run in a worktree until the submodule is initialised.
**No tetra3 submodule on this branch.** Unlike upstream `main`, the solver
(`cedar-solve`/Tetra3) is an ordinary uv dependency (pinned git rev in
`python/pyproject.toml`), so worktrees need no `git submodule` step.

## Development Commands

**Running Python**
Developers may have created virtual environments in directories like ".venv" or "venv". Make sure these virtual
environments are activated before any of the python based tools below.

**Development workflow uses Nox for task automation:**
```bash
nox -s lint # Code linting with Ruff (auto-fixes issues)
nox -s format # Code formatting with Ruff
nox -s type_hints # Type checking with MyPy
nox -s smoke_tests # Quick functionality validation
nox -s unit_tests # Full unit test suite
nox -s babel # I18n message extraction and compilation
nox -s web_tests # Testing the webserver, see below
```

**Direct testing with pytest:**
```bash
pytest -m smoke # Smoke tests for core functionality
pytest -m unit # Unit tests for isolated components
pytest -m integration # End-to-end integration tests
```
**This branch uses uv (pyproject.toml + uv.lock), not nox/requirements*.txt.**
All Python tooling runs through uv from `python/`:

**Development setup:**
```bash
cd python/
python3.9 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -r requirements_dev.txt
uv sync --frozen # create/refresh .venv from uv.lock
uvx ruff@0.4.8 check --config "builtins=['_']" . # lint (same pin as CI / upstream noxfile)
uvx ruff@0.4.8 format . # format
uv run mypy PiFinder # type checking
uv run pytest -m smoke # smoke tests
uv run pytest -m unit # unit tests
uv run pytest -m integration tests/test_ui_modules.py # UI module harness
```
If the .venv dir already exists, you can directly source it and run the app.

CI (`.github/workflows/nox.yml` — keeps the upstream "nox" check name) runs
exactly these commands.

Watch out for .venv directories containing virtual environments, that you need to activate first.
NixOS dev-box note: manylinux wheels (numpy etc.) need `libstdc++`; if imports
fail with `libstdc++.so.6: cannot open shared object file`, run tests with
`LD_LIBRARY_PATH=$(nix build --print-out-paths nixpkgs#stdenv.cc.cc.lib)/lib`.

**Running the application:**

First start the `cedar-detect-server` which is in `bin` (you need to use `-p 50551`, when invoking it).
Use the correct architecture suffix for cedar-detect-server according to the platform you're running on.
Use the correct architecture suffix for cedar-detect-server according to the platform you're running on.

Development setup has to have run and you should be in .venv virtual environment
```bash
cd python/
python -m PiFinder.main [options]
uv run python -m PiFinder.main [options]
```
Usual startup:

```bash
python3.9 -m PiFinder.main -fh --camera debug --keyboard local -x
uv run python -m PiFinder.main -fh --camera debug --keyboard local -x
```

## Reference Documentation
Expand Down Expand Up @@ -191,6 +172,39 @@ Tests use pytest with custom markers for different test types. The smoke tests p
- **Linting:** Ruff with Python 3.9 target, Black-compatible formatting
- **Type Checking:** MyPy with gradual typing adoption
- **Code Style:** 88-character line length, double quotes, space indentation
- **Comments:** describe what the code does now, not how it changed. No "previously / no longer / used to / moved from" — history lives in git/jj, not in comments.
- **I18n Support:** Babel integration for multi-language UI

The codebase follows modern Python practices with type hints, comprehensive testing, and automated code quality checks integrated into the development workflow.

## NixOS Development

**CRITICAL: Never run `nix build` or `nix eval` on Pi 4 targets.** The Pi 4 lacks sufficient resources and will hang/crash. Always build on pi5.local (GitHub Actions runner), push to Attic, then trigger the upgrade service:
```bash
# Build on pi5
ssh pi5.local 'nix build --no-link --print-out-paths github:mrosseel/PiFinder/nixos#nixosConfigurations.pifinder.config.system.build.toplevel'
# Push to Attic (so Pi can download signed paths)
ssh pi5.local 'attic push pifinder:pifinder <store-path>'
# Trigger upgrade on target Pi (downloads from Attic, activates, reboots)
ssh pifinder@<target-ip> 'echo "<store-path>" > /run/pifinder/upgrade-ref && sudo systemctl start --no-block pifinder-upgrade.service'
# Monitor progress
ssh pifinder@<target-ip> 'cat /run/pifinder/upgrade-status'
```

**Netboot deployment (dev Pi on proxnix NFS):**
```bash
./deploy-image-to-nfs.sh # Build and deploy to NFS
```

**Power control (Shelly plug via Home Assistant):**
```bash
~/.local/bin/pifinder-power-off.sh # Turn off PiFinder
~/.local/bin/pifinder-power-on.sh # Turn on PiFinder
```

**Check Pi status:**
```bash
ssh pifinder@192.168.5.146 # SSH to netboot Pi
systemctl status pifinder # Check service status
journalctl -u pifinder -f # Follow service logs
```
Loading
Loading