diff --git a/.gitmodules b/.gitmodules index 13f0bf5b..bbd1b94a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,3 @@ [submodule "python/PiFinder/tetra3"] path = python/PiFinder/tetra3 - url = https://github.com/esa/tetra3.git - branch = no_big_files + url = https://github.com/smroid/cedar-solve diff --git a/README.md b/README.md index 602bcbe3..99f5ffc8 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,10 @@ A plate solving telescope finder based around a Raspberry PI, imx296 camera, and For an overview of what the PiFinder™ is and how it came to be visit the official project website at [PiFinder.io](https://www.pifinder.io/build-yours) +The PiFinder™ uses the [Ceder Detect](https://github.com/smroid/cedar-detect) and +[Cedar Solve](https://github.com/smroid/cedar-solve) libraries with express permission. +Thank you to [smroid] for all your support of the PiFinder project! + ![Banner](./docs/source/images/PiFinder_v3_banner.png) The PiFinder™ is my attempt to improve my time at my telescope. I don't get nearly enough of it and I want to enjoy it as much as possible. So after years of observing with paper charts and, later, a Nexus DSC here is what I felt I was missing: * **Reliable telescope positioning:** The Nexus DSC is great, but my scope just isn't built for solid encoder integration. The slop in the way I have to couple the encoders means poor pointing accuracy. @@ -45,4 +49,3 @@ If you'd like to learn more about how it works, and potentially build your own, ## Discord Join the [PiFinder™ Discord server](https://discord.gg/Nk5fHcAtWD) for support with your build, usage questions, and suggestions for improvement. -Buy Me a Coffee at ko-fi.com diff --git a/bin/cedar-detect-server-aarch64 b/bin/cedar-detect-server-aarch64 index 85b67b0f..ed305142 100755 Binary files a/bin/cedar-detect-server-aarch64 and b/bin/cedar-detect-server-aarch64 differ diff --git a/bin/cedar-detect-server-arm64 b/bin/cedar-detect-server-arm64 index 0dfd0c29..ea792437 100755 Binary files a/bin/cedar-detect-server-arm64 and b/bin/cedar-detect-server-arm64 differ diff --git a/migration_source/v1.x.x.sh b/migration_source/v1.x.x.sh new file mode 100644 index 00000000..e17ee2c6 --- /dev/null +++ b/migration_source/v1.x.x.sh @@ -0,0 +1,48 @@ +# GPSD +sudo apt install -y gpsd +sudo dpkg-reconfigure -plow gpsd +sudo cp ~/PiFinder/pi_config_files/gpsd.conf /etc/default/gpsd + +# PWM +sudo sed -zi '/dtoverlay=pwm,pin=13,func=4\n/!s/$/\ndtoverlay=pwm,pin=13,func=4\n/' /boot/config.txt + +# Uart for GPS +sudo sed -zi '/dtoverlay=uart3\n/!s/$/\ndtoverlay=uart3\n/' /boot/config.txt + +# Migrate DB +if [ -f "/home/pifinder/PiFinder/astro_data/observations.db" ] +then + echo "Migrating astro_data DB" + python -c "from PiFinder import setup;setup.create_logging_tables();" + sqlite3 < /home/pifinder/PiFinder/migrate_db.sql + rm /home/pifinder/PiFinder/astro_data/observations.db +fi + +# Migrate Config files +if ! [ -f "/home/pifinder/PiFinder_data/config.json" ] +then + echo "Migrating config.json" + mv /home/pifinder/PiFinder/config.json /home/pifinder/PiFinder_data/config.json +fi + +# Adjust service definition +sudo systemctl disable pifinder +sudo rm /etc/systemd/system/pifinder.service +sudo cp /home/pifinder/PiFinder/pi_config_files/pifinder.service /lib/systemd/system/pifinder.service +sudo systemctl daemon-reload +sudo systemctl enable pifinder + +# add PiFinder_splash if not already in place +if ! [ -f "/lib/systemd/system/pifinder_spash.service" ] +then + sudo cp /home/pifinder/PiFinder/pi_config_files/pifinder_splash.service /lib/systemd/system/pifinder_splash.service + sudo systemctl daemon-reload + sudo systemctl enable pifinder_splash +fi + +# open permissisons on wpa_supplicant file so we can adjust network config +sudo chmod 666 /etc/wpa_supplicant/wpa_supplicant.conf + +# DONE +echo "Post Update Complete" + diff --git a/migration_source/v2.1.0.sh b/migration_source/v2.1.0.sh new file mode 100644 index 00000000..b066c187 --- /dev/null +++ b/migration_source/v2.1.0.sh @@ -0,0 +1,7 @@ +# swap tetra3 submodule +git submodule sync +git submodule update --init --recursive + +# Set up symlink +ln -s /home/pifinder/PiFinder/python/PiFinder/tetra3/tetra3 /home/pifinder/PiFinder/python/tetra3 + diff --git a/pifinder_post_update.sh b/pifinder_post_update.sh index 36b5434d..e87db3ed 100644 --- a/pifinder_post_update.sh +++ b/pifinder_post_update.sh @@ -1,51 +1,28 @@ git submodule update --init --recursive sudo pip install -r /home/pifinder/PiFinder/python/requirements.txt -# GPSD -sudo apt install -y gpsd -sudo dpkg-reconfigure -plow gpsd -sudo cp ~/PiFinder/pi_config_files/gpsd.conf /etc/default/gpsd - -# PWM -sudo sed -zi '/dtoverlay=pwm,pin=13,func=4\n/!s/$/\ndtoverlay=pwm,pin=13,func=4\n/' /boot/config.txt - -# Uart for GPS -sudo sed -zi '/dtoverlay=uart3\n/!s/$/\ndtoverlay=uart3\n/' /boot/config.txt - -# Migrate DB -if [ -f "/home/pifinder/PiFinder/astro_data/observations.db" ] +# Set up migrations folder if it does not exist +if ! [ -d "/home/pifinder/PiFinder_data/migrations" ] then - echo "Migrating astro_data DB" - python -c "from PiFinder import setup;setup.create_logging_tables();" - sqlite3 < /home/pifinder/PiFinder/migrate_db.sql - rm /home/pifinder/PiFinder/astro_data/observations.db + mkdir /home/pifinder/PiFinder_data/migrations fi -# Migrate Config files -if ! [ -f "/home/pifinder/PiFinder_data/config.json" ] +# v1.x.x +# everying prior to selecitve migrations +if ! [ -f "/home/pifinder/PiFinder_data/migrations/v1.x.x" ] then - echo "Migrating config.json" - mv /home/pifinder/PiFinder/config.json /home/pifinder/PiFinder_data/config.json + source /home/pifinder/PiFinder/migration_source/v1.x.x.sh + touch /home/pifinder/PiFinder_data/migrations/v1.x.x fi -# Adjust service definition -sudo systemctl disable pifinder -sudo rm /etc/systemd/system/pifinder.service -sudo cp /home/pifinder/PiFinder/pi_config_files/pifinder.service /lib/systemd/system/pifinder.service -sudo systemctl daemon-reload -sudo systemctl enable pifinder - -# add PiFinder_splash if not already in place -if ! [ -f "/lib/systemd/system/pifinder_spash.service" ] +# v2.1.0 +# Switch to Cedar +if ! [ -f "/home/pifinder/PiFinder_data/migrations/v2.1.0" ] then - sudo cp /home/pifinder/PiFinder/pi_config_files/pifinder_splash.service /lib/systemd/system/pifinder_splash.service - sudo systemctl daemon-reload - sudo systemctl enable pifinder_splash + source /home/pifinder/PiFinder/migration_source/v2.1.0.sh + touch /home/pifinder/PiFinder_data/migrations/v2.1.0 fi -# open permissisons on wpa_supplicant file so we can adjust network config -sudo chmod 666 /etc/wpa_supplicant/wpa_supplicant.conf - # DONE echo "Post Update Complete" diff --git a/python/PiFinder/solver.py b/python/PiFinder/solver.py index 6ae83493..29b01572 100644 --- a/python/PiFinder/solver.py +++ b/python/PiFinder/solver.py @@ -20,71 +20,12 @@ from PiFinder import utils sys.path.append(str(utils.tetra3_dir)) -import PiFinder.tetra3.tetra3 as tetra3 -from PiFinder.tetra3.tetra3 import cedar_detect_client +import tetra3 +from tetra3 import cedar_detect_client logger = logging.getLogger("Solver") -def find_target_pixel(t3, fov_estimate, centroids, ra, dec): - """ - Searches the most recent solve for a pixel - that matches the requested RA/DEC the best - """ - print(f"{len(centroids)=}") - search_center = (256, 256) - search_distance = 128 - while search_distance >= 1: - # try 5 search points - search_points = [ - [search_center[0] - search_distance, search_center[1] - search_distance], - [search_center[0] - search_distance, search_center[1] + search_distance], - [search_center[0] + search_distance, search_center[1] - search_distance], - [search_center[0] + search_distance, search_center[1] + search_distance], - [search_center[0], search_center[1]], - ] - - # probe points - min_dist = 100000 - for search_point in search_points: - solve_fails = 0 - point_sol = {} - while point_sol.get("RA_target") is None: - solve_fails += 1 - if solve_fails > 10: - print("Too many fails") - return (-1, -1) - try: - point_sol = t3.solve_from_centroids( - centroids, - (512, 512), - fov_estimate=fov_estimate, - fov_max_error=0.2, - return_matches=False, - target_pixel=[search_point[0], search_point[1]], - solve_timeout=1000, - ) - except Exception: - return (-1, -1) - - # distance... - p_dist = np.hypot( - point_sol["RA_target"] - ra, point_sol["Dec_target"] - dec - ) - if p_dist < min_dist: - search_center = search_point - min_dist = p_dist - - # cut search distance - search_distance = search_distance / 2 - - # Done? - if min_dist > 0.1: - # Didn't find a good pixel... - return (-1, -1) - return search_center - - def solver( shared_state, solver_queue, @@ -101,6 +42,8 @@ def solver( str(utils.cwd_dir / "PiFinder/tetra3/tetra3/data/default_database.npz") ) last_solve_time = 0 + align_ra = 0 + align_dec = 0 solved = { # RA, Dec, Roll solved at the center of the camera FoV: "RA_camera": None, @@ -150,15 +93,10 @@ def solver( # for this RA/DEC and set it as alignment pixel align_ra = command[1] align_dec = command[2] - align_target_pixel = find_target_pixel( - t3=t3, - fov_estimate=solved["FOV"], - centroids=centroids, - ra=align_ra, - dec=align_dec, - ) - logger.debug(f"Align {align_target_pixel=}") - align_result_queue.put(["aligned", align_target_pixel]) + + if command[0] == "align_cancel": + align_ra = 0 + align_dec = 0 state_utils.sleep_for_framerate(shared_state) @@ -192,15 +130,20 @@ def solver( logger.warn("No stars found, skipping") continue else: + _solver_args = {} + if align_ra != 0 and align_dec != 0: + _solver_args["target_sky_coord"] = [[align_ra, align_dec]] + solution = t3.solve_from_centroids( centroids, (512, 512), fov_estimate=12.0, fov_max_error=4.0, match_max_error=0.005, - return_matches=True, + # return_matches=True, target_pixel=shared_state.solve_pixel(), solve_timeout=1000, + **_solver_args, ) if "matched_centroids" in solution: @@ -238,6 +181,20 @@ def solver( solved["cam_solve_time"] = solved["solve_time"] solver_queue.put(solved) + # See if we are waiting for alignment + if align_ra != 0 and align_dec != 0: + if solved.get("x_target") is not None: + align_target_pixel = ( + solved["y_target"], + solved["x_target"], + ) + logger.debug(f"Align {align_target_pixel=}") + align_result_queue.put(["aligned", align_target_pixel]) + align_ra = 0 + align_dec = 0 + solved["x_target"] = None + solved["y_target"] = None + last_solve_time = last_image_metadata["exposure_end"] except EOFError: logger.error("Main no longer running for solver") diff --git a/python/PiFinder/tetra3 b/python/PiFinder/tetra3 index bd70e126..38c3f48f 160000 --- a/python/PiFinder/tetra3 +++ b/python/PiFinder/tetra3 @@ -1 +1 @@ -Subproject commit bd70e12657305ad758633a5045e5c0b18395e536 +Subproject commit 38c3f48f57d1005e9b65cbb26136f9f13ec0a1b0 diff --git a/python/PiFinder/ui/align.py b/python/PiFinder/ui/align.py index 21d21cae..7695c8dd 100644 --- a/python/PiFinder/ui/align.py +++ b/python/PiFinder/ui/align.py @@ -24,6 +24,14 @@ def align_on_radec(ra, dec, command_queues, config_object, shared_state) -> bool * Set the config item and the shared state * return the pixel or -1/-1 for error """ + + # Clear out any pending responses + while True: + try: + command = command_queues["align_response"].get(block=False) + except queue.Empty: + break + # Send command to solver to work out the camera pixel for this target command_queues["align_command"].put( [ @@ -36,8 +44,16 @@ def align_on_radec(ra, dec, command_queues, config_object, shared_state) -> bool received_response = False start_time = time.time() while not received_response: - # only wait a second - if time.time() - start_time > 1: + # only wait two seconds + if time.time() - start_time > 2: + command_queues["align_command"].put( + [ + "align_cancel", + ra, + dec, + ] + ) + command_queues["console"].put("Align Timeout") return False try: @@ -55,6 +71,7 @@ def align_on_radec(ra, dec, command_queues, config_object, shared_state) -> bool return False # success, set all the things... + command_queues["console"].put("Alignment Set") shared_state.set_solve_pixel(target_pixel) config_object.set_option("solve_pixel", target_pixel) return True @@ -81,17 +98,14 @@ def __init__(self, *args, **kwargs): self.star_list = np.empty((0, 2)) self.alignment_star = None self.reticle_position = ( - self.config_object.get_option("solve_pixel", (256, 256))[0] / 4, self.config_object.get_option("solve_pixel", (256, 256))[1] / 4, + self.config_object.get_option("solve_pixel", (256, 256))[0] / 4, ) # Marking menu definition self.marking_menu = MarkingMenu( left=MarkingMenuOption(), - down=MarkingMenuOption( - label="Options", - menu_jump="chart_settings", - ), + down=MarkingMenuOption(), right=MarkingMenuOption(), ) @@ -104,7 +118,7 @@ def draw_reticle(self): if not self.align_mode: self.reticle_position = self.starfield.radec_to_xy( - self.solution["RA"], self.solution["Dec"] + self.solution["RA_target"], self.solution["Dec_target"] ) x_pos = round(self.reticle_position[0]) @@ -163,9 +177,7 @@ def update(self, force=False): if self.shared_state.solve_state(): self.animate_fov() - constellation_brightness = self.config_object.get_option( - "chart_constellations", 64 - ) + constellation_brightness = 64 self.solution = self.shared_state.solution() last_solve_time = self.solution["solve_time"] if ( @@ -190,7 +202,7 @@ def update(self, force=False): self.screen.paste(image_obj) self.last_update = last_solve_time - self.draw_reticle() + self.draw_reticle() else: self.draw.rectangle( diff --git a/python/PiFinder/ui/menu_structure.py b/python/PiFinder/ui/menu_structure.py index 64c27c3e..021adce4 100644 --- a/python/PiFinder/ui/menu_structure.py +++ b/python/PiFinder/ui/menu_structure.py @@ -859,19 +859,6 @@ }, ], }, - { - "name": "Experimental", - "class": UITextMenu, - "select": "Single", - "items": [ - { - "name": "Align", - "class": UIAlign, - "stateful": True, - "preload": True, - }, - ], - }, ], }, ], diff --git a/python/pyproject.toml b/python/pyproject.toml index 9324ef27..8475104f 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -123,6 +123,7 @@ module = [ 'sklearn.*', 'PyHotKey.*', 'PiFinder.tetra3.*', + 'tetra3.*', 'grpc', 'ceder_detect_pb2', 'RPi.*', diff --git a/python/requirements.txt b/python/requirements.txt index 2f1fc893..cbb1232c 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -4,7 +4,7 @@ bottle==0.12.25 cheroot==10.0.0 dataclasses_json==0.6.7 gpsdclient==1.3.2 -grpcio==1.60.0 +grpcio==1.64.1 json5==0.9.25 luma.oled==3.12.0 luma.lcd==2.11.0 @@ -22,5 +22,4 @@ sh==1.14.3 skyfield==1.45 timezonefinder==6.1.9 tqdm==4.65.0 -grpcio==1.60.0 protobuf==4.25.2