Skip to content

Commit

Permalink
Implement needed functionality for DOF simulation rust-side, switches…
Browse files Browse the repository at this point in the history
… to f64 to increase precision. Adds focusing in/out to Telescope Genserver
  • Loading branch information
documents-design committed Jan 3, 2023
1 parent 0f60d47 commit 766eee9
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 75 deletions.
70 changes: 46 additions & 24 deletions lib/optics/parabola.ex
Expand Up @@ -83,9 +83,9 @@ defmodule Optics.Parabola do
end)
end

@spec reflection_coords(number, number, number) ::
@spec reflection_coords_onaxis(number, number, number) ::
{:ok, %Optics.Segment{}}
def reflection_coords(focal_length, y, source_distance) do
def reflection_coords_onaxis(focal_length, y, source_distance) do
x = x_coord_on_parabola(focal_length, y)
{:ok, v1} = Segment.make(x, y, source_distance, 0.0)
normal = normal_coords(focal_length, y)
Expand All @@ -95,18 +95,34 @@ defmodule Optics.Parabola do
Segment.make(x, y, x_coord, 0.0)
end

@spec non_parallel_rayfan_coords(float, float, float, integer) :: list(%Segment{})
def reflection_angle(f, y, source_distance, source_height) do
x = x_coord_on_parabola(f, y)
{:ok, v1} = Segment.make(x, y, source_distance, source_height)
normal = normal_coords(f, y)
angle = angle_between_segments(v1, normal)
(angle_with_x_axis(v1) + (2.0 * angle)) - :math.pi()
end

@spec reflection_coords(float, float, float, float) ::
{:ok,
%Optics.Segment{}}
def reflection_coords(focal_length, y, source_distance, source_height) do
output_angle = reflection_angle(focal_length, y, source_distance, source_height)
x = x_coord_on_parabola(focal_length, y)
ray_length = 2.5 * focal_length
Segment.make(
x,
y,
x + abs(ray_length * :math.cos(output_angle)),
y + ray_length * (:math.sin(output_angle * -1))
)
end

@spec non_parallel_rayfan_coords(float, float, float, float, integer) :: list(%Segment{})
@doc """
Returns a list of rays (Segments) and their reflections.
iex> rays = Optics.Parabola.non_parallel_rayfan_coords(400.0, 57.0, 10000.0, 4)
iex> last_ray = rays |> List.last()
iex> %Optics.Segment{a: %Optics.Point{x: 2.030625, y: 57.0},b: %Optics.Point{x: 416.836314941448, y: 0.0}} = last_ray
iex> rays = Optics.Parabola.non_parallel_rayfan_coords(400.0, 57.0, 99999999999999.0, 4)
iex> last_ray = rays |> List.last()
iex> 400.0 = Float.round(last_ray.b.x, 2)
"""
def non_parallel_rayfan_coords(focal_length, radius, source_distance, rays) do
def non_parallel_rayfan_coords(focal_length, radius, source_distance, source_height, rays) do
base_y = -radius
base_x = source_distance
step = abs(radius) / rays * 2
Expand All @@ -116,7 +132,7 @@ defmodule Optics.Parabola do
y = base_y + r * step
x = x_coord_on_parabola(focal_length, y)
{:ok, s1} = Segment.make(x, y, base_x, 0)
{:ok, s2} = reflection_coords(focal_length, y, source_distance)
{:ok, s2} = reflection_coords(focal_length, y, source_distance, source_height)
[s1, s2]
end)
end
Expand All @@ -135,21 +151,27 @@ defmodule Optics.Parabola do
floaty_equal(s1.a.y, s2.a.y, n) and floaty_equal(s1.b.y, s2.b.y, n)
end

@spec non_parallel_rayfan_coords_rs(float, float, float, integer) :: list(%Segment{})
@spec non_parallel_rayfan_coords_rs(float, float, float, float, integer) :: list(%Segment{})
@doc """
Returns a list of rays (Segments) and their reflections, but implemented rust-side
iex> rays = Optics.Parabola.non_parallel_rayfan_coords(400.0, 57.0, 10000.0, 4)
iex> rays_rs = Optics.Parabola.non_parallel_rayfan_coords_rs(400.0, 57.0, 10000.0, 4)
iex> %Optics.Segment{a: %Optics.Point{x: 2.030625, y: 57.0},b: %Optics.Point{x: 416.836314941448, y: 0.0}} = rays |> List.last()
iex> true = Optics.Parabola.sfe(rays |> List.last(), rays_rs |> List.last(), 2)
iex> rays = Optics.Parabola.non_parallel_rayfan_coords(400.0, 57.0, 99999999999999.0, 4)
iex> rays_rs = Optics.Parabola.non_parallel_rayfan_coords_rs(400.0, 57.0, 99999999999999.0, 4)
iex> 400.0 = Float.round(List.last(rays).b.x, 2)
iex> 400.0 = Float.round(List.last(rays_rs).b.x, 2)
iex> true = Optics.Parabola.sfe(rays |> List.last(), rays_rs |> List.last(), 2)
iex> rays = Optics.Parabola.non_parallel_rayfan_coords(400.0, 57.0, 10000.0, 0.0, 4)
iex> rays_rs = Optics.Parabola.non_parallel_rayfan_coords_rs(400.0, 57.0, 10000.0, 0.0, 4)
iex> true = Optics.Parabola.sfe(rays |> List.last(), rays_rs |> List.last(), 8)
iex> rays = Optics.Parabola.non_parallel_rayfan_coords(400.0, 57.0, 99999999999999.0, 0.0, 4)
iex> rays_rs = Optics.Parabola.non_parallel_rayfan_coords_rs(400.0, 57.0, 99999999999999.0, 0.0, 4)
iex> true = Optics.Parabola.sfe(rays |> List.last(), rays_rs |> List.last(), 8)
iex> rays = Optics.Parabola.non_parallel_rayfan_coords(400.0, 57.0, 10000.0, 10.0, 4)
iex> rays_rs = Optics.Parabola.non_parallel_rayfan_coords_rs(400.0, 57.0, 10000.0, 10.0, 4)
iex> true = Optics.Parabola.sfe(rays |> List.last(), rays_rs |> List.last(), 8)
iex> rays = Optics.Parabola.non_parallel_rayfan_coords(400.0, 57.0, 99999999999999.0, 10.0, 4)
iex> rays_rs = Optics.Parabola.non_parallel_rayfan_coords_rs(400.0, 57.0, 99999999999999.0, 10.0, 4)
iex> true = Optics.Parabola.sfe(rays |> List.last(), rays_rs |> List.last(), 8)
"""
def non_parallel_rayfan_coords_rs(focal_length, radius, source_distance, rays) do
Optics.RxopticsNif.non_parallel_rayfan_coords(focal_length, radius, source_distance, rays)
def non_parallel_rayfan_coords_rs(focal_length, radius, source_distance, source_height, rays) do
Optics.RxopticsNif.non_parallel_rayfan_coords(focal_length, radius, source_distance, source_height, rays)
end
end
4 changes: 3 additions & 1 deletion lib/optics/rxoptics_nif.ex
@@ -1,7 +1,9 @@
defmodule Optics.RxopticsNif do
use Rustler, otp_app: :scope, crate: :rxopticsnif

def non_parallel_rayfan_coords(_focal_length, _radius, _source_distance, _rays), do: error()
def non_parallel_rayfan_coords(_focal_length, _radius, _source_distance, _source_height, _rays), do: error()

def reflection_angle(_focal_length, _radius, _source_distance, _source_height), do: error()

defp error, do: :erlang.nif_error(:nif_not_loaded)
end
83 changes: 74 additions & 9 deletions lib/scope/telescope.ex
Expand Up @@ -4,6 +4,7 @@ defmodule Scope.Telescope do
position_az: 0,
position_focus: 0,
moving: :no,
focusing: :no,
home_az: false,
lower_alt_stop: false,
upper_alt_stop: false,
Expand All @@ -13,6 +14,10 @@ defmodule Scope.Telescope do
@alt_time 15
@alt_divisions 900
@az_divisions 3600
@min_focus -10.0
@max_focus 10.0
@focus_step 0.01
@focus_time_interval 8
@az_time 60
@alt_increment :math.pi() / 2 / @alt_divisions
@az_increment :math.pi() * 2 / @az_divisions
Expand All @@ -28,34 +33,41 @@ defmodule Scope.Telescope do
name: name,
position_alt: -1,
position_az: -1,
position_focus: -1,
home_az: false,
moving: :no,
focusing: :no,
lower_alt_stop: false,
upper_alt_stop: false,
lower_focus_stop: false,
upper_focus_stop: false
}}
end



def handle_call(:home, _, state), do: do_home(state)
def handle_info(:continue_move, %Telescope{moving: :no} = state), do: {:noreply, state}
def handle_info(:continue_move, %Telescope{moving: _dir} = state), do: continue_move(state)
def handle_info(:continue_focus, %Telescope{focusing: :no} = state), do: {:noreply, state}
def handle_info(:continue_focus, %Telescope{focusing: _dir} = state), do: continue_focus(state)

def handle_cast(:show, state), do: {:noreply, IO.inspect(state)}

def handle_cast(:start_focus_in, state), do: start_focus(:in, state)
def handle_cast(:start_focus_out, state), do: start_focus(:out, state)
def handle_cast(:start_move_down, state), do: start_move(:down, state)
def handle_cast(:start_move_left, state), do: start_move(:left, state)
def handle_cast(:start_move_up, state), do: start_move(:up, state)
def handle_cast(:start_move_right, state), do: start_move(:right, state)

def handle_cast(:focus_in, state), do: do_focus(:in, state)
def handle_cast(:focus_out, state), do: do_focus(:out, state)
def handle_cast(:move_up, state), do: do_move(:up, state)
def handle_cast(:move_down, state), do: do_move(:down, state)
def handle_cast(:move_left, state), do: do_move(:right, state)
def handle_cast(:move_right, state), do: do_move(:left, state)

def handle_cast(:stop_move, %Telescope{} = state), do: stop_move(state)
def handle_cast(:stop_focusing, %Telescope{} = state), do: stop_focus(state)

def valid_move_preconditions?(_, %{position_alt: -1}), do: false
def valid_move_preconditions?(:down, %{lower_alt_stop: true}), do: false
Expand All @@ -68,6 +80,8 @@ defmodule Scope.Telescope do
:down -> :move_down
:right -> :move_right
:left -> :move_left
:in -> :focus_in
:out -> :focus_out
_ -> nil
end
end
Expand All @@ -83,7 +97,24 @@ defmodule Scope.Telescope do
end
end

def valid_focus_preconditions?(_, %{position_focus: -1}), do: false
def valid_focus_preconditions?(:in, %{lower_focus_stop: true}), do: false
def valid_focus_preconditions?(:out, %{upper_focus_stop: true}), do: false
def valid_focus_preconditions?(_, _), do: true

def start_focus(dir, state) do
msg = dir_to_msg(dir)

if valid_focus_preconditions?(dir, state) and !is_nil(msg) do
GenServer.cast(self(), msg)
{:noreply, %{state | focusing: dir}}
else
{:noreply, state}
end
end

def do_move(_dir, %Telescope{moving: :no} = state), do: {:noreply, state}

def do_move(dir, %Telescope{} = state) do
case dir do
:left -> do_move_az(-1 * @az_increment, state)
Expand All @@ -94,6 +125,39 @@ defmodule Scope.Telescope do
end
end

def do_focus(_dir, %Telescope{focusing: :no} = state), do: {:noreply, state}

def do_focus(dir, %Telescope{} = state) do
inc =
case dir do
:in -> -1 * @focus_step
:out -> @focus_step
_ -> 0
end

Process.send_after(self(), :continue_focus, @focus_time_interval)
normalized_pos = min(10, max(-10, state.position_focus + inc))

{:noreply,
%{
state
| position_focus: normalized_pos,
lower_focus_stop: normalized_pos == -10,
upper_focus_stop: normalized_pos == 10
}}
end

def continue_focus(%Telescope{} = state) do
msg = dir_to_msg(state.focusing)

if !is_nil(msg) do
GenServer.cast(self(), msg)
end

{:noreply, state}
end


def do_move_az(inc, %Telescope{} = state) do
pos = state.position_az + inc
turns = trunc(pos / :math.pi())
Expand All @@ -109,16 +173,15 @@ defmodule Scope.Telescope do
lower_stop = pos <= 0
upper_stop = pos >= mmax
normalized = min(mmax, max(pos, 0))

if lower_stop or upper_stop do
GenServer.cast(self(), :stop_move)
else
Process.send_after(self(), :continue_move, @alt_time_interval)
end
{:noreply, %{state |
lower_alt_stop: lower_stop,
upper_alt_stop: upper_stop,
position_alt: normalized
}}

{:noreply,
%{state | lower_alt_stop: lower_stop, upper_alt_stop: upper_stop, position_alt: normalized}}
end

def continue_move(%Telescope{} = state) do
Expand All @@ -135,14 +198,16 @@ defmodule Scope.Telescope do
new_state = %{
state
| lower_alt_stop: true,
lower_focus_stop: true,
lower_focus_stop: false,
home_az: true,
position_alt: 0,
position_az: 0
position_az: 0,
position_focus: 0
}

{:reply, :ok, new_state}
end

def stop_move(state), do: {:noreply, %{state | moving: :no}}
def stop_focus(state), do: {:noreply, %{state | focusing: :no}}
end
5 changes: 5 additions & 0 deletions lib/scope/telescope_api.ex
Expand Up @@ -10,5 +10,10 @@ defmodule Scope.TelescopeApi do
def left(pid), do: GenServer.cast(pid, :start_move_left)
def right(pid), do: GenServer.cast(pid, :start_move_right)

def focus_in(pid), do: GenServer.cast(pid, :start_focus_in)

def focus_out(pid), do: GenServer.cast(pid, :start_focus_out)

def stop(pid), do: GenServer.cast(pid, :stop_move)
def stop_focus(pid), do: GenServer.cast(pid, :stop_focusing)
end
11 changes: 8 additions & 3 deletions native/rxopticsnif/src/lib.rs
Expand Up @@ -2,8 +2,13 @@ mod parabola;
use parabola::Segment;

#[rustler::nif]
pub fn non_parallel_rayfan_coords(focal_length: f32, radius: f32, source_distance: f32, rays: i32) -> Vec<Segment> {
parabola::non_parallel_rayfan_coords(focal_length, radius, source_distance, rays)
pub fn non_parallel_rayfan_coords(focal_length: f64, radius: f64, source_distance: f64, source_height: f64, rays: i32) -> Vec<Segment> {
parabola::non_parallel_rayfan_coords(focal_length, radius, source_distance, source_height, rays)
}

rustler::init!("Elixir.Optics.RxopticsNif", [non_parallel_rayfan_coords]);
#[rustler::nif]
pub fn reflection_angle(focal_length: f64, y: f64, source_distance: f64, source_height: f64) -> f64 {
parabola::reflection_angle(focal_length, y, source_distance, source_height)
}

rustler::init!("Elixir.Optics.RxopticsNif", [non_parallel_rayfan_coords, reflection_angle]);

0 comments on commit 766eee9

Please sign in to comment.