Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Cedar Detect / Solve and Solidify Align #256

Merged
merged 24 commits into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -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
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.

<a href='https://ko-fi.com/brickbots' target='_blank'><img height='35' style='border:0px;height:46px;' src='https://az743702.vo.msecnd.net/cdn/kofi3.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' />
Binary file modified bin/cedar-detect-server-aarch64
Binary file not shown.
Binary file modified bin/cedar-detect-server-arm64
Binary file not shown.
48 changes: 48 additions & 0 deletions migration_source/v1.x.x.sh
Original file line number Diff line number Diff line change
@@ -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"

7 changes: 7 additions & 0 deletions migration_source/v2.1.0.sh
Original file line number Diff line number Diff line change
@@ -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

49 changes: 13 additions & 36 deletions pifinder_post_update.sh
Original file line number Diff line number Diff line change
@@ -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"

99 changes: 28 additions & 71 deletions python/PiFinder/solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion python/PiFinder/tetra3
36 changes: 24 additions & 12 deletions python/PiFinder/ui/align.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
[
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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(),
)

Expand All @@ -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])
Expand Down Expand Up @@ -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 (
Expand All @@ -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(
Expand Down
Loading
Loading