Skip to content

fix: resolve lint and mypy errors in sys_utils_nixos.py#396

Closed
mrosseel wants to merge 51 commits intobrickbots:releasefrom
mrosseel:fix/sys-utils-nixos-ci
Closed

fix: resolve lint and mypy errors in sys_utils_nixos.py#396
mrosseel wants to merge 51 commits intobrickbots:releasefrom
mrosseel:fix/sys-utils-nixos-ci

Conversation

@mrosseel
Copy link
Collaborator

Summary

  • Fix E402 lint errors from gi.require_version() breaking import order
  • Fix dict | None syntax (requires Python 3.10) → Optional[dict]
  • Fix utils.pifinder_home (doesn't exist) → utils.home_dir / "PiFinder"
  • Add dbus/gi to mypy ignore list (no type stubs available)

Test plan

  • CI lint, mypy, smoke_tests, unit_tests all pass

🤖 Generated with Claude Code

mrosseel and others added 30 commits March 17, 2025 22:19
This reverts commit c872394.
Add support for Stellarium+ Mobile

The critical command here is the ack command. Most of the additional commands aren't strictly necessary but speed up the connection process (otherwise Stellarium times out and moves on).
The SD image module provides filesystems, but toplevel builds need
a minimal stub to evaluate successfully.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Required for NixOS module system to accept devMode setting.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Required when module has both options and config sections.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replaces FIXME placeholders with actual SRI hashes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Uses Pi5 runner when RUNNER_LABELS variable is set, falls back to
ubuntu with QEMU emulation otherwise.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Filter to only Pi 4B device tree (CM4 incompatible with our overlays)
- Use shorthand DTS syntax for PWM overlay

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use J2000 as epoch when Stellarium is connected

Stellarium seems to use J2000 as its epoch, while SkySafari uses JNOW by default.

Technically we don't need to do position_of_radec etc. when J2000 is the input, but I thought this was a simpler change.

Reference to Stellarium using J2000 - https://sourceforge.net/p/stellarium/discussion/278769/thread/fc15d042/ 
Update to Cedar was done here - smroid/cedar-server@f2f6b69

* Skip epoch conversion for Stellarium
WimDeMeester and others added 21 commits February 21, 2026 11:43
Adds an environment variable flag (PIFINDER_USE_FAKE_SYS_UTILS) to prefer the fake sys_utils for local development and testing.

When the flag is truthy ("1", "true", "yes") the fake module is used; otherwise the code attempts to import the real module and falls back to the fake on ImportError. This makes local dev setup deterministic and simplifies testing without altering existing fallback behavior.
* i18n: add zh locale, Chinese font, and menu updates

* ui: update fonts and menu structure for zh
* Rework gpsd gps code to fix issue with early dongle which never reported sky

* Fix for auth related solver crashes
* Add Harris Globular Cluster catalog loader

* Nox fixes and full database

* Unit Test change, Harris Loader fix, corrected catalog

* ruffed Harris Loader fix
* Add T9 search option and tests

* Isolate calc_utils stub in T9 tests

* Add T9 search option to text entry search

* Improve T9 digit filtering

* Move translator definition outside of _name_to_t9_digits

* Ensure T9 translator is built from keypad mapping

* Add cached T9 digit mapping for catalog searches
* Add comments

* Need to change dictionary solve in both integrator.py and solver.py --> Now plate solves but chart doesn't move when PiFinder moved

* Fix at scope: Missing name space pointing.

* Edit comment

* Fix at scope: Typo

* Debug at scope: Increased threshold for using IMU tracking to 0.1 deg. Smoother. BUT IMU tracking is L/R and Up/Down reversed!?

* imu_pi.py: Disable BNO055 config to rotate the IMU data

* Disable using the old method to calculate Euler angles (imu["pos"])

* Update comments

* Remove debug logging that was added that was dumping too much to the log file.

* Additional logging printing for debugging

* Rename func

* Add new class ImuDeadReckoning

* Update ImuDeadReckoning() class

* Remove unused dictionary entry (now using the new ImuDeadReckoning() class)

* Update integrator.py to use ImuDeadReckoning()

* Debug on PiFinder in test mode --> The charts move up/down and left/right reversed?

* Remove cached astro_data/comets.txt

* Fix .gitignore to ignore astro_data/comets.txt

* Correct spelling in comments

* Update README. Tested and runs OK. Reproduces functionality of the existing system.

* Add __init__.py to pointing_model/

* Refactor: Define axis_angle2quat()

* Update README

* First attempt at non-upright support using roll offset

* Fix bug -runs on PiFinder in simulation mode

* Update README with EQ approach

* Move helper functions to quaternion_transforms.py

* Update name space of quaternion functions

* Move ImuDeadReckoning to imu_dead_reckoning.py

* Fix import of custome package

* Add ImuDeadReckoningEq and associated funcs for EQ frame

* Refactor integrator.py to encapsulate the altaz calculation

* Refactor integrator.py - Encapsulate another code block out of the while loop

* Fix - use last_image_solve to calculate cam2scope_offset

* Refactor

* Update comments

* Use flag in ImuDeadReckoning rather than solved["Alt"] to determine if plate solved coordinate exists

* Add helper functions of the EQ versions (currently just a copy of the altaz version)

* Refactor: Move if to where funcs are called

* integrator.py: Modify the EQ version

* Init commit of RaDecRoll data class

* Draft: calculating q_cam2scope (alignment)

* Reorder

* Fix

* Fix: was not returning ra, dec of scope

* Set alignment between scope and camera

* Add comments

* Remove Horiz version

* integrator.py: Fix to get it working for EQ version

* Remove unused code block

* Move initialization of logger to outside try:

* Move init of solved dict to external module. Desktop-tested.

* Remove setting of  flip_alt_offset --> Not used. Desktop-tested.

* Remove setting of location and datetime in the integrator.py loop --> not used. Desktop-tested.

* Remove unused datetime. Desktop-tested.

* Rename func

* Refactor to set_alignment(). Desktop-tested.

* Lint and update comments

* Add comment

* Rename class to ImuDeadReckoning . Desktop-tested.

* Add .get() methods

* Add type hints

* Add comments

* Add comments

* Type hints. Desktop-check

* Re-order

* Import RaDecRoll

* Add TODO

* Add sky test notes. --> Sky test OK

* Fix tuple type hints

* Remove else: for clarity

* RaDecRoll: Make it a @DataClass and ddd support for None

* Add note

* imu_dead_reckoning: Disallow None as input to keep the interface clean and simple

* Fix missing import

* Rename object pointing_tracker --> imu_dead_reckoning to make it more explicit. Desktop-tested

* Remove returning unused dead_reckoning_flag

* Change invalid imu measurement from None to np.nan. Desktop-tested

* Remove unused code

* Move to class-level type hints

* Rename confusing func name

* Move calculation of screen direction quaternion to a separate func for re-usability & testing

* screen_direction: Sketch other types. use axis_angle2quat() for flat v2. --> Desktop-tested

* Fix latex equation formatting

* Add photo and explanation in README for q_imu2cam

* Move assert location for clarity.

* Update README

* Update q_imu2cam transformations for other types (not all done yet)

* Update comments

* Remove commented out code that change the IMU output axes.

* Add type hints to integrator

* Update README

* Reorder input parameter order for consistency

* Update set_screen_direction with v3 Flat and as_dream

* Update comments

* Add get_roll_by_mount_type(). --> Desktop-tested

* Updated README with sky test observations --> Sky Test

* Update TODO

* Update altaz in integrator.py to see if this helps with the catalog issue?

* Refactored altaz calcuatlion. Now catalog works in altaz mount mode.

* Add note

* Update README

* Add note to requirements_dev.txt

* Ruff: camera_interface.py

* Ruff: imu_pi.py

* Ruff: imu_print_measurements.py

* Move imu_print_measurements.py to pointing_model/

* Ruff: integrator.py

* Ruff: astro_coords.py

* Ruff: imu_dead_reckoning.py

* Ruff: quaternion_transforms.py

* Move pip numpy-quaternion from requirements_dev.txt to requirements.txt

* Fix screen_direction calculation bug for as_dream

* Convert np.quaternion --> quaternion.quaternion to be compatible with Ruff

* requirements.txt: Fix missing =

* Ruff: imu_pi.py: Remove config that's no longer used because screen_direction isn't set here.

* Ruff

* Fix type hints

* Lint

* RaDecRoll: Add .initialize() method

* Rename --> .reset()

* RaDecRoll: Add method to set using quaternions

* Change to using RaDecRoll class for inerfacing

* Update comments

* Update README

* Fix circular import. Desktop test --> OK

* Nox format

* Fix issue from linting that was causing align to fail

* Refactor. Destop test --> OK (inc. align)

* Move functionality from integrator.py to imu_dead_reckoning.py

* Add commens. Update README

* astro_coords: Remove dependence on quaternion_transforms

* Rename func --> get_q_eq()

* Remove commented out code block

* Update README issues list

* Now shows RA difference to aim as -180 to +180 degrees rather than 0 to 360 degrees

* README - detail out TODO & issues

* Lint

* Add quaternion to mypy.overrides to ignore type hints

* Rename --> initialized_solved_dict

* Fix bug in ra_diff calculation: Was causing a crash when catalogs selected in EQ mode

* Remove commented out code block

* Remove unused IMU data: "pos", "imu_pos", etc. which stored Euler angles that are no longer used

* Remove "imu_pos", "pos" etc. for Euler angles

* Remove unused Euler angle functions/methods quat_to_euler() and get_euler()

* Address TODO items

* ToDo items: Remove unused variables

* Use solve dict from astro_coords

* Update README

* Update README

* Update README

* Remove scipy spatial rotation package import - no longer needed for Euler angles

* Nox format

* Nox format for changes merged from main

* Nox format

* Change requirements.txt numpy=1.26.2 --> numpy=1.26.4 preferred by numpy-quaternion

* Update README - remove parts that are no longer relevant (virtual env, horiz frame, etc)

* Change np.quaternion --> quaternion.quaternion so that it passes Nox type checking

* Remove imu_print_measurements.py -- used for debugging

* Sky-tested. Wrote notes in README.

* Add issue to README

* Fix typo in pyproject.toml --> Should pass nox

* Fix: adapt to changes in imu_pi.py from merging main

* Add details to issue in README

* Fix README

* Edit README

* Add test notes to README

* Formatting

* Fixed q_imu2cam for 'right'

* Fix rotation q_imu2cam for the "straight" type

* Fix typo in comment

* Remove TODO in comments that's done

* Update comments

* Add notes from testing to README

* Decrease IMU_MOVED_ANG_THRESHOLD from 0.1 deg to 0.06 deg, which is same as the value used in the original code (when converted to deg).

* Lint

* For testing: Disable 180 degree rotation of roll

* Update README

* Refactor: Rename functions/methodds

* Update README & comments

* Remove duplicate code and use the function from quaternion_transforms.py

* For southern hemisphere, show south up.

* Big change: Change sign of rol calculated by quaternion_transforms

* Update comments

* Merge issue fix: camera_interface.py: Now "focus" shows image. Still issue in test mode.

* Copy pre-merge versions of integrator.py to debug merge conflict issues.

* Copy integrator from the last merge from main (ebca2) before the auto exposure feature was added.

* Copy integrator from main and pre-merge for debugging

* Fix merge: Compared with camera_interface.py in main and restored changes for Auto Exposure that was lost in merge

* Update as_dream --> as_bloom from main

* Add TODO

* Update initialized_solved_dict() to have same dict keys as main.

* Copy from main and pre-merge for debugging

* Fix merge issues. Align `solve.py` with main

* Update comment

* Resolving merge conflicts in integrator.py

* Attempt to align the merge with both branches

* Add comments

* Initialize Imu.__reading_diff

* Remove files added for debugging

* add constellation is None fix

* Adding readme and screen covers

* Add instructions to build guide how to test leds and buttons. (brickbots#371)

* Add a testing the backlight step and a warning to test the backlight before installing the GPS.

* Add instructions to for testing the keypad

* Sqm improvements (brickbots#372)

* Adding first version of sqm measurement

* Added UI screen, some refactoring for jupyter debugging

* Fix crash on no solution

* No shared_state crash

* Protect against various crash situations

* Swapping centroid y/x

* Added bortle classes, radius=3

* Now try radius=2

* Raw output for camera in shared state

* Introducing bias image

* feeding linear image through pipeline

* Delete and ignore comets file, sqm fixes

* Fix bias image bug

* Missing bias image arguments

* Another raw_image mention removed

* Add debugging output

* Merged main with the location changes

* Final version of sqm ui

- still has the bias frames, will be removed next commit
- added bortle descriptions in the ui

* Add unit test

* Removed bias, added performance code

* speed up per-star calculations

* Less info logging, translations, fix nox error

* fix messages

* Updated other languages with translation

* Perform SQM every 5 secs, prettify the SQM ui

* Fix linting error

* Some raw and pedestal changes

* Add default dark frames

* Better noise floor

* fix exposure

* leave exposure

* Noise floor fixes

* processed vs raw

* marking menu for pro sqm

* add sqm calibration

* Immediately reload measurements

* improve sweeps and camera type, maybe camera type should be reverted

* Raw pipeline

* Cleanup

* Cleanup after partial PR review

* Refactoring camera handling and sqm calibration

* Do sqm calibration captures in manual mode

* small AE and sqm fixes

* Solve stuck

* show background during sqm

* Fix name error

* another fix

* resize

* Stretch image

* Fix SQM sweep capture for production readiness

- PNG Processing: Changed sweep to save 8-bit processed PNGs using capture() method
- GPS Time Integration: Sweep now uses GPS datetime from shared_state
- Altitude Calculation: Added automatic altitude calculation from RA/Dec
- Metadata Saving: Implemented comprehensive metadata saving with save_sweep_metadata.py
- Progress Bar Fix: Updates progress for every image (not every 10th)
- Background Clamping: Modified SQM calculator to clamp negative backgrounds to 1 ADU
- Type Hints: Added proper Optional[] type hints for mypy compliance

* Fix cedar-detect shared memory crashes and variable shadowing

Cedar-detect shared memory fix:
- Fixed intermittent FileNotFoundError when cedar-detect has shared memory issues
- Client now gracefully handles missing/stale shared memory
- Automatically falls back to non-shmem mode instead of crashing
- Updated tetra3 submodule with robust error handling

Variable shadowing fix:
- Fixed linter error where 'time' variable shadowed 'time' module import
- Renamed to 'obs_time' for clarity in altitude calculation

* Add real-time progress UI for exposure sweep
* fix crash on sweep

* time based progress of sweep

* Fix sweep

* Use GPS time

* Fix json writing

* fix altitude calc

* Improve exposure sweep

* Better counting and solver crah

* more solver indenting

* Improve sweep UI

* Handle daytime calibration

* Add correction ui and help

* Add sqm debugging sweeps

* Adding SNR AE

* Add rotating constellation/SQM display to title bar

Adds a rotating info display that alternates between constellation name
and SQM value every 3 seconds with a smooth cross-fade animation.

* Add exposure time display to SQM view

Shows current camera exposure time (e.g., "400ms" or "0.40s") in the
top right of the SQM view for diagnostic purposes.

Cherry-picked from test-upstream branch.

* Simplify Bortle class display on SQM view

- Remove redundant title text display
- Add guard for missing details
- Use base font for consistency

* Remove dual pipeline references from SQM code

- Remove unused value_raw from SQMState
- Rename update_sqm_single_pipeline to update_sqm
- Rename sqm_raw to sqm_uncorrected (more accurate name)
- Remove raw SQM display from UI
- Clean up outdated comments

* Refactor rotating display into compact helper class

Extract RotatingInfoDisplay class to encapsulate state and animation logic.
Reduces code by ~100 lines while preserving cross-fade animation behavior.

* Derive SNR controller thresholds from camera profile

- Add from_camera_profile() factory method to ExposureSNRController
- Calculate min/target/max background from bit_depth and bias_offset
- Target background just above min (no benefit to higher values)
- Remove arbitrary 400ms min_exposure floor (now 10ms)
- Remove 5-second rate limiting (averaging handled in SQM code)

* Update tetra3 submodule to match main

* Fix KeyError on T_solve when solver fails

Check if T_solve exists in solved dict before accessing it.
Previously checked wrong dict (solution vs solved) or didn't check at all.

* Fix update_sqm parameter name mismatch after merge

* Fix KeyError when deleting optional solution fields

* Fix camera type detection for SNR controller

* Remove duplicate solver code block from bad merge

* Add saturation detection to SNR controller

* Exclude saturated stars from SQM instead of reducing exposure

Move saturation handling from auto-exposure to SQM calculation:
- SQM: detect saturated stars (max aperture pixel >= 250) and exclude
  from mzero calculation by setting flux to -1
- Auto-exposure: remove saturation detection, just target background level

This allows longer exposures for better background SNR while ignoring
bright saturated stars that would corrupt the photometric calibration.

* Fix TypeError in SQM correction UI

colors.get() only takes intensity, not red=True keyword argument.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix SQM correction: value_raw attribute does not exist

SQM state only has value, source, last_update - not value_raw.
Store source in metadata instead.

* Target minimum background for shorter exposures

Change target_background from min*1.25 to min+2 for more linear
camera response and less saturation risk.

* Use adaptive noise floor for SNR auto-exposure

- Add noise_floor to shared state (solver writes, camera reads)
- SNR controller uses adaptive noise floor + 2 as min_background
- Falls back to static camera profile if noise floor not yet available

* UX: Show 'Saving...' during correction save, target exact noise floor

- Show 'Saving...' message immediately before creating zip
- Remove +2 margin from noise floor - target exact value for shortest exposure

* Add noise_floor and imu_delta to correction metadata

* Add full SQM calculation details to shared state and correction metadata

Includes: mzero, mzero_std, background, pedestal, extinction, star counts,
saturation info, etc. - everything needed for calibration validation.
* Show star count in SQM view
* Move star count to top line in SQM view

* Add bracketed exposures to correction package

Captures 3 exposures: base, +1 stop, -1 stop (or base, -1, -2 if at max).
Shows progress for each bracket. Re-enables auto-exposure when done.

* Reduce exposure sweep from 100 to 20 images

Faster sweep (~1 min vs ~4 min) while still sampling exposure space.
Launch from SQM view → hold square → DEBUG.

* Use flux-weighted mean for mzero calculation

Brighter stars have higher SNR so their mzero estimates are more reliable.
Weight each star's contribution by its flux.

* Restore +2 margin above noise floor

Without margin, background_corrected ≈ 0 when at noise floor,
causing log10(~0) = very negative, making SQM too high.

* Fix SQM calculation: use bias_offset only as pedestal, disable extinction

Two fixes for SQM being ~0.4 mag too high:

1. Use only bias_offset (6.0 ADU) as pedestal instead of full noise_floor
   (7.46 ADU). Read noise and dark current are random fluctuations, not
   systematic offsets that should be subtracted.

2. Disable atmospheric extinction correction. When comparing to ground-based
   SQM meters, both measurements are through the same atmosphere, so no
   correction is needed.

* Re-enable extinction correction with 0.1 mag/airmass coefficient

* Add dual extinction correction: fixed 0.1 for meter match, altitude-based for science

- Main SQM value uses fixed 0.1 mag extinction (matches consumer SQM meters)

* Use ASTAP extinction convention: 0.28*(airmass-1), no fixed baseline

* better default for camera

* small corrections to sqm calculation and athmospheric correcitons

* Simplify SQM API, fix read noise calculation, add Cedar fallback

* Set SNR as default for testing

* Fix PID auto-exposure crash and improve sweep recovery

- Fix PID integral crash: Reset integral when error changes sign
  (prevents -487,500µs crash when going from too many → too few stars)
- Start sweep at 400ms instead of 25ms for faster recovery
- Add sleep mode logging to diagnose camera blocking issues
- Revert default to PID (SNR exposures too long on clear nights)

* Update tests for sweep starting at 400ms

Tests now reflect the new sweep pattern that starts at 400ms
instead of 25ms for faster recovery.

* Adjust GPS antenna holder sizing

* Rework gpsd gps code to fix issue with early dongle which never reported sky (brickbots#373)

* Adding release notes

* Remove reference to assembled kit version

* Remove reference to assembled kit version

* Clean up

* Add TODO

* Nox & Edit comment

* Fix freezing when chart selected

* Lint

* Fix freezing on align.py: Same as for chart.py

* Rename method

* Validate input to .set_power_state()

* status.py: Add checks to prevent it from freezing.

* imu_pi.py: Add comment

* Rename status "LST SLV" to "LAST SLV" to avoid confusion with LST

* status.py: Deal with case when cam_solve_time = 0. Show "CAM_FAILED" explicitly as "F"

* Fix chart and align not showing "No solve yet"

* Remove file for debugging

* Move initialized_solved_dict() to solver.py and rename to get_initialized_solved_dict()

* Fix: "can't plot yet" kept flashing up

* Add PiMount option for inserts and no_inserts

* Add support for Stellarium+ Mobile (brickbots#375)

Add support for Stellarium+ Mobile

The critical command here is the ack command. Most of the additional commands aren't strictly necessary but speed up the connection process (otherwise Stellarium times out and moves on).

* A bunch of typo/spelling fixes

* Use J2000 as input epoch when Stellarium is connected (brickbots#376)

* Use J2000 as epoch when Stellarium is connected

Stellarium seems to use J2000 as its epoch, while SkySafari uses JNOW by default.

Technically we don't need to do position_of_radec etc. when J2000 is the input, but I thought this was a simpler change.

Reference to Stellarium using J2000 - https://sourceforge.net/p/stellarium/discussion/278769/thread/fc15d042/ 
Update to Cedar was done here - smroid/cedar-server@f2f6b69

* Skip epoch conversion for Stellarium

* Rename --> set_cam2scope_alignment

* Refactor

* Fix typo

* Alt and Az swapped around. Moving the scope in Az moves the chart in Alt and vice versa. Reverting change from commit (d364fef) seems to fix this.

* Edit comments

* Move the hack for the PA to Roll calculation for testing from calc_utils to integrator.py

* Lint (Nox)

---------

Co-authored-by: TakKanekoGit <>
Co-authored-by: Mike Rosseel <mike.rosseel@gmail.com>
Co-authored-by: Richard <rich@brickbots.com>
Co-authored-by: Jens Scheidtmann <Jens.Scheidtmann@gmail.com>
Co-authored-by: oakamil <oakamil@gmail.com>
* Rename func

* Add new class ImuDeadReckoning

* Update ImuDeadReckoning() class

* Remove unused dictionary entry (now using the new ImuDeadReckoning() class)

* Update integrator.py to use ImuDeadReckoning()

* Debug on PiFinder in test mode --> The charts move up/down and left/right reversed?

* Remove cached astro_data/comets.txt

* Fix .gitignore to ignore astro_data/comets.txt

* Correct spelling in comments

* Update README. Tested and runs OK. Reproduces functionality of the existing system.

* Add __init__.py to pointing_model/

* Refactor: Define axis_angle2quat()

* Update README

* First attempt at non-upright support using roll offset

* Fix bug -runs on PiFinder in simulation mode

* Update README with EQ approach

* Move helper functions to quaternion_transforms.py

* Update name space of quaternion functions

* Move ImuDeadReckoning to imu_dead_reckoning.py

* Fix import of custome package

* Add ImuDeadReckoningEq and associated funcs for EQ frame

* Refactor integrator.py to encapsulate the altaz calculation

* Refactor integrator.py - Encapsulate another code block out of the while loop

* Fix - use last_image_solve to calculate cam2scope_offset

* Refactor

* Update comments

* Use flag in ImuDeadReckoning rather than solved["Alt"] to determine if plate solved coordinate exists

* Add helper functions of the EQ versions (currently just a copy of the altaz version)

* Refactor: Move if to where funcs are called

* integrator.py: Modify the EQ version

* Init commit of RaDecRoll data class

* Draft: calculating q_cam2scope (alignment)

* Reorder

* Fix

* Fix: was not returning ra, dec of scope

* Set alignment between scope and camera

* Add comments

* Remove Horiz version

* integrator.py: Fix to get it working for EQ version

* Remove unused code block

* Move initialization of logger to outside try:

* Move init of solved dict to external module. Desktop-tested.

* Remove setting of  flip_alt_offset --> Not used. Desktop-tested.

* Remove setting of location and datetime in the integrator.py loop --> not used. Desktop-tested.

* Remove unused datetime. Desktop-tested.

* Rename func

* Refactor to set_alignment(). Desktop-tested.

* Lint and update comments

* Add comment

* Rename class to ImuDeadReckoning . Desktop-tested.

* Add .get() methods

* Add type hints

* Add comments

* Add comments

* Type hints. Desktop-check

* Re-order

* Import RaDecRoll

* Add TODO

* Add sky test notes. --> Sky test OK

* Fix tuple type hints

* Remove else: for clarity

* RaDecRoll: Make it a @DataClass and ddd support for None

* Add note

* imu_dead_reckoning: Disallow None as input to keep the interface clean and simple

* Fix missing import

* Rename object pointing_tracker --> imu_dead_reckoning to make it more explicit. Desktop-tested

* Remove returning unused dead_reckoning_flag

* Change invalid imu measurement from None to np.nan. Desktop-tested

* Remove unused code

* Move to class-level type hints

* Rename confusing func name

* Move calculation of screen direction quaternion to a separate func for re-usability & testing

* screen_direction: Sketch other types. use axis_angle2quat() for flat v2. --> Desktop-tested

* Fix latex equation formatting

* Add photo and explanation in README for q_imu2cam

* Move assert location for clarity.

* Update README

* Update q_imu2cam transformations for other types (not all done yet)

* Update comments

* Remove commented out code that change the IMU output axes.

* Add type hints to integrator

* Update README

* Reorder input parameter order for consistency

* Update set_screen_direction with v3 Flat and as_dream

* Update comments

* Add get_roll_by_mount_type(). --> Desktop-tested

* Updated README with sky test observations --> Sky Test

* Update TODO

* Update altaz in integrator.py to see if this helps with the catalog issue?

* Refactored altaz calcuatlion. Now catalog works in altaz mount mode.

* Add note

* Update README

* Add note to requirements_dev.txt

* Ruff: camera_interface.py

* Ruff: imu_pi.py

* Ruff: imu_print_measurements.py

* Move imu_print_measurements.py to pointing_model/

* Ruff: integrator.py

* Ruff: astro_coords.py

* Ruff: imu_dead_reckoning.py

* Ruff: quaternion_transforms.py

* Move pip numpy-quaternion from requirements_dev.txt to requirements.txt

* Fix screen_direction calculation bug for as_dream

* Convert np.quaternion --> quaternion.quaternion to be compatible with Ruff

* requirements.txt: Fix missing =

* Ruff: imu_pi.py: Remove config that's no longer used because screen_direction isn't set here.

* Ruff

* Fix type hints

* Lint

* RaDecRoll: Add .initialize() method

* Rename --> .reset()

* RaDecRoll: Add method to set using quaternions

* Change to using RaDecRoll class for inerfacing

* Update comments

* Update README

* Fix circular import. Desktop test --> OK

* Nox format

* Fix issue from linting that was causing align to fail

* Refactor. Destop test --> OK (inc. align)

* Move functionality from integrator.py to imu_dead_reckoning.py

* Add commens. Update README

* astro_coords: Remove dependence on quaternion_transforms

* Rename func --> get_q_eq()

* Remove commented out code block

* Update README issues list

* Now shows RA difference to aim as -180 to +180 degrees rather than 0 to 360 degrees

* README - detail out TODO & issues

* Lint

* Add quaternion to mypy.overrides to ignore type hints

* Rename --> initialized_solved_dict

* Fix bug in ra_diff calculation: Was causing a crash when catalogs selected in EQ mode

* Remove commented out code block

* Remove unused IMU data: "pos", "imu_pos", etc. which stored Euler angles that are no longer used

* Remove "imu_pos", "pos" etc. for Euler angles

* Remove unused Euler angle functions/methods quat_to_euler() and get_euler()

* Address TODO items

* ToDo items: Remove unused variables

* Use solve dict from astro_coords

* Update README

* Update README

* Update README

* Remove scipy spatial rotation package import - no longer needed for Euler angles

* Nox format

* Nox format for changes merged from main

* Nox format

* Change requirements.txt numpy=1.26.2 --> numpy=1.26.4 preferred by numpy-quaternion

* Update README - remove parts that are no longer relevant (virtual env, horiz frame, etc)

* Change np.quaternion --> quaternion.quaternion so that it passes Nox type checking

* Remove imu_print_measurements.py -- used for debugging

* Sky-tested. Wrote notes in README.

* Add issue to README

* Fix typo in pyproject.toml --> Should pass nox

* Fix: adapt to changes in imu_pi.py from merging main

* Add details to issue in README

* Fix README

* Edit README

* Add test notes to README

* Formatting

* Fixed q_imu2cam for 'right'

* Fix rotation q_imu2cam for the "straight" type

* Fix typo in comment

* Remove TODO in comments that's done

* Update comments

* Add notes from testing to README

* Decrease IMU_MOVED_ANG_THRESHOLD from 0.1 deg to 0.06 deg, which is same as the value used in the original code (when converted to deg).

* Lint

* For testing: Disable 180 degree rotation of roll

* Update README

* Refactor: Rename functions/methodds

* Update README & comments

* Remove duplicate code and use the function from quaternion_transforms.py

* For southern hemisphere, show south up.

* Big change: Change sign of rol calculated by quaternion_transforms

* Update comments

* Merge issue fix: camera_interface.py: Now "focus" shows image. Still issue in test mode.

* Copy pre-merge versions of integrator.py to debug merge conflict issues.

* Copy integrator from the last merge from main (ebca2) before the auto exposure feature was added.

* Copy integrator from main and pre-merge for debugging

* Fix merge: Compared with camera_interface.py in main and restored changes for Auto Exposure that was lost in merge

* Update as_dream --> as_bloom from main

* Add TODO

* Update initialized_solved_dict() to have same dict keys as main.

* Copy from main and pre-merge for debugging

* Fix merge issues. Align `solve.py` with main

* Update comment

* Resolving merge conflicts in integrator.py

* Attempt to align the merge with both branches

* Add comments

* Initialize Imu.__reading_diff

* Remove files added for debugging

* add constellation is None fix

* Adding readme and screen covers

* Add instructions to build guide how to test leds and buttons. (brickbots#371)

* Add a testing the backlight step and a warning to test the backlight before installing the GPS.

* Add instructions to for testing the keypad

* Sqm improvements (brickbots#372)

* Adding first version of sqm measurement

* Added UI screen, some refactoring for jupyter debugging

* Fix crash on no solution

* No shared_state crash

* Protect against various crash situations

* Swapping centroid y/x

* Added bortle classes, radius=3

* Now try radius=2

* Raw output for camera in shared state

* Introducing bias image

* feeding linear image through pipeline

* Delete and ignore comets file, sqm fixes

* Fix bias image bug

* Missing bias image arguments

* Another raw_image mention removed

* Add debugging output

* Merged main with the location changes

* Final version of sqm ui

- still has the bias frames, will be removed next commit
- added bortle descriptions in the ui

* Add unit test

* Removed bias, added performance code

* speed up per-star calculations

* Less info logging, translations, fix nox error

* fix messages

* Updated other languages with translation

* Perform SQM every 5 secs, prettify the SQM ui

* Fix linting error

* Some raw and pedestal changes

* Add default dark frames

* Better noise floor

* fix exposure

* leave exposure

* Noise floor fixes

* processed vs raw

* marking menu for pro sqm

* add sqm calibration

* Immediately reload measurements

* improve sweeps and camera type, maybe camera type should be reverted

* Raw pipeline

* Cleanup

* Cleanup after partial PR review

* Refactoring camera handling and sqm calibration

* Do sqm calibration captures in manual mode

* small AE and sqm fixes

* Solve stuck

* show background during sqm

* Fix name error

* another fix

* resize

* Stretch image

* Fix SQM sweep capture for production readiness

- PNG Processing: Changed sweep to save 8-bit processed PNGs using capture() method
- GPS Time Integration: Sweep now uses GPS datetime from shared_state
- Altitude Calculation: Added automatic altitude calculation from RA/Dec
- Metadata Saving: Implemented comprehensive metadata saving with save_sweep_metadata.py
- Progress Bar Fix: Updates progress for every image (not every 10th)
- Background Clamping: Modified SQM calculator to clamp negative backgrounds to 1 ADU
- Type Hints: Added proper Optional[] type hints for mypy compliance

* Fix cedar-detect shared memory crashes and variable shadowing

Cedar-detect shared memory fix:
- Fixed intermittent FileNotFoundError when cedar-detect has shared memory issues
- Client now gracefully handles missing/stale shared memory
- Automatically falls back to non-shmem mode instead of crashing
- Updated tetra3 submodule with robust error handling

Variable shadowing fix:
- Fixed linter error where 'time' variable shadowed 'time' module import
- Renamed to 'obs_time' for clarity in altitude calculation

* Add real-time progress UI for exposure sweep
* fix crash on sweep

* time based progress of sweep

* Fix sweep

* Use GPS time

* Fix json writing

* fix altitude calc

* Improve exposure sweep

* Better counting and solver crah

* more solver indenting

* Improve sweep UI

* Handle daytime calibration

* Add correction ui and help

* Add sqm debugging sweeps

* Adding SNR AE

* Add rotating constellation/SQM display to title bar

Adds a rotating info display that alternates between constellation name
and SQM value every 3 seconds with a smooth cross-fade animation.

* Add exposure time display to SQM view

Shows current camera exposure time (e.g., "400ms" or "0.40s") in the
top right of the SQM view for diagnostic purposes.

Cherry-picked from test-upstream branch.

* Simplify Bortle class display on SQM view

- Remove redundant title text display
- Add guard for missing details
- Use base font for consistency

* Remove dual pipeline references from SQM code

- Remove unused value_raw from SQMState
- Rename update_sqm_single_pipeline to update_sqm
- Rename sqm_raw to sqm_uncorrected (more accurate name)
- Remove raw SQM display from UI
- Clean up outdated comments

* Refactor rotating display into compact helper class

Extract RotatingInfoDisplay class to encapsulate state and animation logic.
Reduces code by ~100 lines while preserving cross-fade animation behavior.

* Derive SNR controller thresholds from camera profile

- Add from_camera_profile() factory method to ExposureSNRController
- Calculate min/target/max background from bit_depth and bias_offset
- Target background just above min (no benefit to higher values)
- Remove arbitrary 400ms min_exposure floor (now 10ms)
- Remove 5-second rate limiting (averaging handled in SQM code)

* Update tetra3 submodule to match main

* Fix KeyError on T_solve when solver fails

Check if T_solve exists in solved dict before accessing it.
Previously checked wrong dict (solution vs solved) or didn't check at all.

* Fix update_sqm parameter name mismatch after merge

* Fix KeyError when deleting optional solution fields

* Fix camera type detection for SNR controller

* Remove duplicate solver code block from bad merge

* Add saturation detection to SNR controller

* Exclude saturated stars from SQM instead of reducing exposure

Move saturation handling from auto-exposure to SQM calculation:
- SQM: detect saturated stars (max aperture pixel >= 250) and exclude
  from mzero calculation by setting flux to -1
- Auto-exposure: remove saturation detection, just target background level

This allows longer exposures for better background SNR while ignoring
bright saturated stars that would corrupt the photometric calibration.

* Fix TypeError in SQM correction UI

colors.get() only takes intensity, not red=True keyword argument.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix SQM correction: value_raw attribute does not exist

SQM state only has value, source, last_update - not value_raw.
Store source in metadata instead.

* Target minimum background for shorter exposures

Change target_background from min*1.25 to min+2 for more linear
camera response and less saturation risk.

* Use adaptive noise floor for SNR auto-exposure

- Add noise_floor to shared state (solver writes, camera reads)
- SNR controller uses adaptive noise floor + 2 as min_background
- Falls back to static camera profile if noise floor not yet available

* UX: Show 'Saving...' during correction save, target exact noise floor

- Show 'Saving...' message immediately before creating zip
- Remove +2 margin from noise floor - target exact value for shortest exposure

* Add noise_floor and imu_delta to correction metadata

* Add full SQM calculation details to shared state and correction metadata

Includes: mzero, mzero_std, background, pedestal, extinction, star counts,
saturation info, etc. - everything needed for calibration validation.
* Show star count in SQM view
* Move star count to top line in SQM view

* Add bracketed exposures to correction package

Captures 3 exposures: base, +1 stop, -1 stop (or base, -1, -2 if at max).
Shows progress for each bracket. Re-enables auto-exposure when done.

* Reduce exposure sweep from 100 to 20 images

Faster sweep (~1 min vs ~4 min) while still sampling exposure space.
Launch from SQM view → hold square → DEBUG.

* Use flux-weighted mean for mzero calculation

Brighter stars have higher SNR so their mzero estimates are more reliable.
Weight each star's contribution by its flux.

* Restore +2 margin above noise floor

Without margin, background_corrected ≈ 0 when at noise floor,
causing log10(~0) = very negative, making SQM too high.

* Fix SQM calculation: use bias_offset only as pedestal, disable extinction

Two fixes for SQM being ~0.4 mag too high:

1. Use only bias_offset (6.0 ADU) as pedestal instead of full noise_floor
   (7.46 ADU). Read noise and dark current are random fluctuations, not
   systematic offsets that should be subtracted.

2. Disable atmospheric extinction correction. When comparing to ground-based
   SQM meters, both measurements are through the same atmosphere, so no
   correction is needed.

* Re-enable extinction correction with 0.1 mag/airmass coefficient

* Add dual extinction correction: fixed 0.1 for meter match, altitude-based for science

- Main SQM value uses fixed 0.1 mag extinction (matches consumer SQM meters)

* Use ASTAP extinction convention: 0.28*(airmass-1), no fixed baseline

* better default for camera

* small corrections to sqm calculation and athmospheric correcitons

* Simplify SQM API, fix read noise calculation, add Cedar fallback

* Set SNR as default for testing

* Fix PID auto-exposure crash and improve sweep recovery

- Fix PID integral crash: Reset integral when error changes sign
  (prevents -487,500µs crash when going from too many → too few stars)
- Start sweep at 400ms instead of 25ms for faster recovery
- Add sleep mode logging to diagnose camera blocking issues
- Revert default to PID (SNR exposures too long on clear nights)

* Update tests for sweep starting at 400ms

Tests now reflect the new sweep pattern that starts at 400ms
instead of 25ms for faster recovery.

* Adjust GPS antenna holder sizing

* Rework gpsd gps code to fix issue with early dongle which never reported sky (brickbots#373)

* Adding release notes

* Remove reference to assembled kit version

* Remove reference to assembled kit version

* Clean up

* Add TODO

* Nox & Edit comment

* Fix freezing when chart selected

* Lint

* Fix freezing on align.py: Same as for chart.py

* Rename method

* Validate input to .set_power_state()

* status.py: Add checks to prevent it from freezing.

* imu_pi.py: Add comment

* Rename status "LST SLV" to "LAST SLV" to avoid confusion with LST

* status.py: Deal with case when cam_solve_time = 0. Show "CAM_FAILED" explicitly as "F"

* Fix chart and align not showing "No solve yet"

* Remove file for debugging

* Move initialized_solved_dict() to solver.py and rename to get_initialized_solved_dict()

* Fix: "can't plot yet" kept flashing up

* Add PiMount option for inserts and no_inserts

* Add support for Stellarium+ Mobile (brickbots#375)

Add support for Stellarium+ Mobile

The critical command here is the ack command. Most of the additional commands aren't strictly necessary but speed up the connection process (otherwise Stellarium times out and moves on).

* A bunch of typo/spelling fixes

* Use J2000 as input epoch when Stellarium is connected (brickbots#376)

* Use J2000 as epoch when Stellarium is connected

Stellarium seems to use J2000 as its epoch, while SkySafari uses JNOW by default.

Technically we don't need to do position_of_radec etc. when J2000 is the input, but I thought this was a simpler change.

Reference to Stellarium using J2000 - https://sourceforge.net/p/stellarium/discussion/278769/thread/fc15d042/ 
Update to Cedar was done here - smroid/cedar-server@f2f6b69

* Skip epoch conversion for Stellarium

* Rename --> set_cam2scope_alignment

* Refactor

* Fix typo

* Alt and Az swapped around. Moving the scope in Az moves the chart in Alt and vice versa. Reverting change from commit (d364fef) seems to fix this.

* Edit comments

* Move the hack for the PA to Roll calculation for testing from calc_utils to integrator.py

* Lint (Nox)

* Make new IMU integrator conditional

* Adjust menu option name

* Always try to solve

* Blank debug image if imu indicates motion to simulate real world motion blur

* Removing cruft

* Since arrows are configurable and user choice, remove screen based correction

* Working on tracking down missing alt / calculating message

* Change config option name

---------

Co-authored-by: TakKanekoGit <>
Co-authored-by: Tak Kaneko <tak.kaneko.2@googlemail.com>
Co-authored-by: Mike Rosseel <mike.rosseel@gmail.com>
Co-authored-by: Jens Scheidtmann <Jens.Scheidtmann@gmail.com>
Co-authored-by: oakamil <oakamil@gmail.com>
- Fix E402 lint: reorder gi imports, add noqa for gi.repository
- Fix Python 3.10 syntax: dict | None → Optional[dict]
- Fix utils.pifinder_home → utils.home_dir / "PiFinder"
- Add dbus/gi to mypy ignore list (no type stubs available)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mrosseel
Copy link
Collaborator Author

Closing to retarget against fork main instead of upstream release

@mrosseel mrosseel closed this Mar 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants