Skip to content

Commit

Permalink
Experimental Alignment Feature (#234)
Browse files Browse the repository at this point in the history
* WIP

* working on rotating projections before raster

* Working rotation before rasterization

* Slight cleanup

* Reduce plot render size to target size

* Workign on reticle adjust

* Star selection working

* Add align on object, clean up alignment system

* Improved navigation, fixed immediate exit bug

* Bury align menu option
  • Loading branch information
brickbots authored Sep 12, 2024
1 parent 6f9b842 commit 4957e5a
Show file tree
Hide file tree
Showing 13 changed files with 658 additions and 58 deletions.
2 changes: 1 addition & 1 deletion python/PiFinder/integrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def integrator(shared_state, solver_queue, console_queue, log_queue, is_debug=Fa
except queue.Empty:
pass

if next_image_solve:
if type(next_image_solve) is dict:
solved = next_image_solve

# see if we can generate alt/az
Expand Down
2 changes: 1 addition & 1 deletion python/PiFinder/keyboard_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def run_script(script_name, q, log_queue):
script = script_file.readlines()
length = len(script)
for idx, script_line in enumerate(script):
sleep(0.5)
sleep(0.1)
script_line = script_line.strip()
logger.debug("(%i/%i)\t%s", idx, length, script_line)
script_tokens = script_line.split(" ")
Expand Down
6 changes: 6 additions & 0 deletions python/PiFinder/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ def main(
gps_queue: Queue = Queue()
camera_command_queue: Queue = Queue()
solver_queue: Queue = Queue()
alignment_command_queue: Queue = Queue()
alignment_response_queue: Queue = Queue()
ui_queue: Queue = Queue()

# init queues for logging
Expand All @@ -216,6 +218,8 @@ def main(
"camera": camera_command_queue,
"console": console_queue,
"ui_queue": ui_queue,
"align_command": alignment_command_queue,
"align_response": alignment_response_queue,
}
cfg = config.Config()

Expand Down Expand Up @@ -334,6 +338,8 @@ def main(
camera_image,
console_queue,
solver_logqueue,
alignment_command_queue,
alignment_response_queue,
verbose,
),
)
Expand Down
142 changes: 106 additions & 36 deletions python/PiFinder/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,11 @@ def __init__(self, colors, resolution, mag_limit=7, fov=10.2):
self.raw_stars = hipparcos.load_dataframe(f)

# Image size stuff
self.target_size = max(self.resolution)
self.diag_mult = 1.422
self.render_size = (
int(self.target_size * self.diag_mult),
int(self.target_size * self.diag_mult),
)
self.render_size = resolution
self.render_center = (
int(self.render_size[0] / 2),
int(self.render_size[1] / 2),
)
self.render_crop = [
int((self.render_size[0] - self.target_size) / 2),
int((self.render_size[1] - self.target_size) / 2),
int((self.render_size[0] - self.target_size) / 2) + self.target_size,
int((self.render_size[1] - self.target_size) / 2) + self.target_size,
]

self.set_mag_limit(mag_limit)
# Prefilter here for mag 7.5, just to make sure we have enough
Expand Down Expand Up @@ -125,7 +114,7 @@ def set_fov(self, fov):
# Used for vis culling in projection space
self.limit = limit

self.image_scale = int(self.target_size / limit)
self.image_scale = int(self.render_size[0] / limit)
self.pixel_scale = self.image_scale / 2

# figure out magnitude limit for fov
Expand All @@ -140,6 +129,39 @@ def set_fov(self, fov):
mag_setting = mag_range[0] - ((mag_range[0] - mag_range[1]) * perc_fov)
self.set_mag_limit(mag_setting)

def radec_to_xy(self, ra: float, dec: float) -> tuple[float, float]:
"""
Converts and RA/DEC to screen space x/y for the current projection
"""
markers = pandas.DataFrame(
[(Angle(degrees=ra)._hours, dec)], columns=["ra_hours", "dec_degrees"]
)

# required, use the same epoch as stars
markers["epoch_year"] = 1991.25
marker_positions = self.earth.observe(Star.from_dataframe(markers))

markers["x"], markers["y"] = self.projection(marker_positions)

# prep rotate by roll....
roll_rad = (self.roll) * (np.pi / 180)
roll_sin = np.sin(roll_rad)
roll_cos = np.cos(roll_rad)

# Rotate them
markers = markers.assign(
xr=((markers["x"]) * roll_cos - (markers["y"]) * roll_sin),
yr=((markers["y"]) * roll_cos + (markers["x"]) * roll_sin),
)

# Rasterize marker positions
markers = markers.assign(
x_pos=markers["xr"] * self.pixel_scale + self.render_center[0],
y_pos=markers["yr"] * -1 * self.pixel_scale + self.render_center[1],
)

return markers["x_pos"][0], markers["y_pos"][0]

def plot_markers(self, marker_list):
"""
Returns an image to add to another image
Expand All @@ -159,10 +181,21 @@ def plot_markers(self, marker_list):

markers["x"], markers["y"] = self.projection(marker_positions)

# prep rotate by roll....
roll_rad = (self.roll) * (np.pi / 180)
roll_sin = np.sin(roll_rad)
roll_cos = np.cos(roll_rad)

# Rotate them
markers = markers.assign(
xr=((markers["x"]) * roll_cos - (markers["y"]) * roll_sin),
yr=((markers["y"]) * roll_cos + (markers["x"]) * roll_sin),
)

# Rasterize marker positions
markers = markers.assign(
x_pos=markers["x"] * self.pixel_scale + self.render_center[0],
y_pos=markers["y"] * -1 * self.pixel_scale + self.render_center[1],
x_pos=markers["xr"] * self.pixel_scale + self.render_center[0],
y_pos=markers["yr"] * -1 * self.pixel_scale + self.render_center[1],
)
# now filter by visiblity
markers = markers[
Expand Down Expand Up @@ -192,10 +225,10 @@ def plot_markers(self, marker_list):
# Draw pointer....
# if not within screen
if (
x_pos > self.render_crop[2]
or x_pos < self.render_crop[0]
or y_pos > self.render_crop[3]
or y_pos < self.render_crop[1]
x_pos > 0
or x_pos < self.render_size[0]
or y_pos > 0
or y_pos < self.render_size[1]
):
# calc degrees to target....
deg_to_target = (
Expand All @@ -210,6 +243,7 @@ def plot_markers(self, marker_list):
tmp_pointer = self.pointer_image.copy()
tmp_pointer = tmp_pointer.rotate(-deg_to_target)
ret_image = ImageChops.add(ret_image, tmp_pointer)

else:
_image = ImageChops.offset(
self.markers[symbol],
Expand All @@ -218,7 +252,7 @@ def plot_markers(self, marker_list):
)
ret_image = ImageChops.add(ret_image, _image)

return ret_image.rotate(self.roll).crop(self.render_crop)
return ret_image

def update_projection(self, ra, dec):
"""
Expand Down Expand Up @@ -254,25 +288,57 @@ def plot_starfield(self, ra, dec, roll, constellation_brightness=32):
self.const_end_star_positions
)

pil_image = self.render_starfield_pil(constellation_brightness)
return pil_image.rotate(self.roll).crop(self.render_crop)
pil_image, visible_stars = self.render_starfield_pil(constellation_brightness)
return pil_image, visible_stars

def render_starfield_pil(self, constellation_brightness):
"""
If return_plotted_stars this will return a tuple:
(image, visible_stars)
Mainly for the new alignment system
"""
ret_image = Image.new("L", self.render_size)
idraw = ImageDraw.Draw(ret_image)

# prep rotate by roll....
roll_rad = (self.roll) * (np.pi / 180)
roll_sin = np.sin(roll_rad)
roll_cos = np.cos(roll_rad)

# constellation lines first
if constellation_brightness:
# convert projection positions to screen space
# using pandas to interate

# roll the constellation lines
self.const_edges_df = self.const_edges_df.assign(
sxr=(
(self.const_edges_df["sx"]) * roll_cos
- (self.const_edges_df["sy"]) * roll_sin
),
syr=(
(self.const_edges_df["sy"]) * roll_cos
+ (self.const_edges_df["sx"]) * roll_sin
),
exr=(
(self.const_edges_df["ex"]) * roll_cos
- (self.const_edges_df["ey"]) * roll_sin
),
eyr=(
(self.const_edges_df["ey"]) * roll_cos
+ (self.const_edges_df["ex"]) * roll_sin
),
)

const_edges = self.const_edges_df.assign(
sx_pos=self.const_edges_df["sx"] * self.pixel_scale
sx_pos=self.const_edges_df["sxr"] * self.pixel_scale
+ self.render_center[0],
sy_pos=self.const_edges_df["sy"] * -1 * self.pixel_scale
sy_pos=self.const_edges_df["syr"] * -1 * self.pixel_scale
+ self.render_center[1],
ex_pos=self.const_edges_df["ex"] * self.pixel_scale
ex_pos=self.const_edges_df["exr"] * self.pixel_scale
+ self.render_center[0],
ey_pos=self.const_edges_df["ey"] * -1 * self.pixel_scale
ey_pos=self.const_edges_df["eyr"] * -1 * self.pixel_scale
+ self.render_center[1],
)

Expand Down Expand Up @@ -318,10 +384,16 @@ def render_starfield_pil(self, constellation_brightness):
& (visible_stars["y"] < self.limit)
]

# Rotate them
visible_stars = visible_stars.assign(
xr=((visible_stars["x"]) * roll_cos - (visible_stars["y"]) * roll_sin),
yr=((visible_stars["y"]) * roll_cos + (visible_stars["x"]) * roll_sin),
)

# convert star positions to screen space
visible_stars = visible_stars.assign(
x_pos=visible_stars["x"] * self.pixel_scale + self.render_center[0],
y_pos=visible_stars["y"] * -1 * self.pixel_scale + self.render_center[1],
x_pos=visible_stars["xr"] * self.pixel_scale + self.render_center[0],
y_pos=visible_stars["yr"] * -1 * self.pixel_scale + self.render_center[1],
)

for x_pos, y_pos, mag in zip(
Expand All @@ -336,13 +408,11 @@ def render_starfield_pil(self, constellation_brightness):
if plot_size < 0.5:
idraw.point((x_pos, y_pos), fill=fill)
else:
idraw.ellipse(
[
x_pos - plot_size,
y_pos - plot_size,
x_pos + plot_size,
y_pos + plot_size,
],
idraw.circle(
(round(x_pos), round(y_pos)),
radius=plot_size,
fill=(255),
width=0,
)
return ret_image

return ret_image, visible_stars
Loading

0 comments on commit 4957e5a

Please sign in to comment.