Skip to content
Open
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
1 change: 0 additions & 1 deletion .rspec
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
--color
--require spec_helper
5 changes: 4 additions & 1 deletion lib/multilateration.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
require "rantly"

require "matrix"

require "multilateration/version"
require "multilateration/signal_event_data_generator"
require "multilateration/signal_event"
require "multilateration/solver"
require "multilateration/time_of_arrival_strategies/source_vector_distance"

module Multilateration
# Your code goes here...
Expand Down
46 changes: 46 additions & 0 deletions lib/multilateration/signal_event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module Multilateration
class SignalEvent
include Comparable

attr_reader :coordinate, :time
private :coordinate, :time

def initialize(coordinate:, time:)
@coordinate = coordinate
@time = time
end

def to_h
{coordinate: coordinate, time: time}
end

def <=>(other)
time_of_arrival <=> other.time_of_arrival
end

def -(other)
vector - other.vector
end

def inner_product_sq
vector.inner_product(vector)
end

def time_difference_of_arrival(other)
time_of_arrival - other.time_of_arrival
end

def time_of_arrival
time
end

def distance_between(coordinate)
Math.sqrt((vector - Vector.elements(coordinate)).map { |component| component.abs**2 }.reduce(&:+))
end

protected def vector
@vector ||= Vector.elements(coordinate)
end

end
end
76 changes: 76 additions & 0 deletions lib/multilateration/signal_event_data_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
module Multilateration
class SignalEventDataGenerator

def self.generate
new(
receiver_count: 5,
coordinate_num_dimensions: 3,
coordinate_max_area: 1000,
coordinate_max_precision: 4,
signal_propagation_max_speed: 500,
signal_propagation_max_precision: 4,
).generate
end

attr_reader :receiver_count, :coordinate_num_dimensions, :coordinate_max_area, :coordinate_max_precision, :signal_propagation_max_speed, :signal_propagation_max_precision
private :receiver_count, :coordinate_num_dimensions, :coordinate_max_area, :coordinate_max_precision, :signal_propagation_max_speed, :signal_propagation_max_precision

def initialize(receiver_count:, coordinate_num_dimensions:, coordinate_max_area:, coordinate_max_precision:, signal_propagation_max_speed:, signal_propagation_max_precision:)
@receiver_count = receiver_count
@coordinate_num_dimensions = coordinate_num_dimensions
@coordinate_max_area = coordinate_max_area
@coordinate_max_precision = coordinate_max_precision
@signal_propagation_max_speed = signal_propagation_max_speed
@signal_propagation_max_precision = signal_propagation_max_precision
end

def generate(rc: receiver_count)
signal_propagation_speed = random_signal_propagation_speed
emission_timestamp = random_high_resolution_timestamp
emitter_coordinate = random_coordinate
receiver_coordinates = rc.times.map { random_coordinate }

{
signal_propagation_speed: signal_propagation_speed,
emitter_event: signal_event_for(emitter_coordinate, emission_timestamp),
receiver_events: receiver_coordinates.map { |coordinate|
signal_event_for(coordinate, propagation_timestamp(signal_propagation_speed, emission_timestamp, emitter_coordinate, coordinate))
},
}
end

private

def signal_event_for(coordinate, timestamp)
{coordinate: coordinate, time: timestamp}
end

def propagation_timestamp(signal_propagation_speed, emission_timestamp, emitter_coordinate, coordinate)
propagation_duration = distance_between_coordinates(emitter_coordinate, coordinate) / signal_propagation_speed
propagation_time = emission_timestamp - propagation_duration
end

def distance_between_coordinates(coordinate1, coordinate2)
vector1 = coordinate_to_vector(coordinate1)
vector2 = coordinate_to_vector(coordinate2)
Math.sqrt((vector1 - vector2).map { |component| component.abs**2 }.reduce(&:+))
end

def random_signal_propagation_speed(spms: signal_propagation_max_speed, spmp: signal_propagation_max_precision)
Rantly { range(1, spms) + float.round(range(0, spmp)) }
end

def random_high_resolution_timestamp
Time.at(Rantly { integer(1_000_000_000) + float })
end

def random_coordinate(cnd: coordinate_num_dimensions, cma: coordinate_max_area, cmp: coordinate_max_precision)
Rantly(cnd) { range(-cma, cma) + float.round(range(0, cmp)) }
end

def coordinate_to_vector(coordinate)
Vector.elements(coordinate)
end

end
end
35 changes: 18 additions & 17 deletions lib/multilateration/solver.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
module Multilateration
class Solver
attr_reader :receivers, :wave_speed, :time_of_arrival_strategy
private :receivers, :wave_speed, :time_of_arrival_strategy
attr_reader :receivers, :signal_propagation_speed
private :receivers, :signal_propagation_speed

def initialize(unsorted_receivers, time_of_arrival_strategy)
@receivers = unsorted_receivers.sort_by { |r| time_of_arrival_strategy.toa(r) }
@wave_speed = time_of_arrival_strategy.wave_speed
@time_of_arrival_strategy = time_of_arrival_strategy
def initialize(receiver_events:, signal_propagation_speed:)
@receivers = receiver_events.map {|e| SignalEvent.new(e) }.sort
@signal_propagation_speed = signal_propagation_speed
end

def solved_vector
Vector.elements (ai_matrix * bi_matrix).flat_map.to_a
def solved_signal_event_emitter
time_between = first_receiver.distance_between(solved_coordinate) / signal_propagation_speed
solved_time = (first_receiver.time_of_arrival + time_between)
SignalEvent.new(coordinate: solved_coordinate, time: solved_time).to_h
end

private

def solved_coordinate
(ai_matrix * bi_matrix).flat_map.to_a
end

def ai_matrix
Matrix.rows(middle_receivers.map { |i| ai(i) }).inverse
end
Expand All @@ -29,9 +34,9 @@ def ai(i)
end

def bi(i)
( distance(tdoa_between_receivers_first_and(i)) * ( distance_sq(tdoa_between_receivers_first_and_last) - inner_product_sq(last_receiver) )) \
+ ( inner_product_sq(first_receiver) * ( distance(tdoa_between_receivers_first_and(i)) - distance(tdoa_between_receivers_first_and_last) )) \
+ ( distance(tdoa_between_receivers_first_and_last) * ( inner_product_sq(i) - distance_sq(tdoa_between_receivers_first_and(i)) ))
( distance(tdoa_between_receivers_first_and(i)) * ( distance_sq(tdoa_between_receivers_first_and_last) - last_receiver.inner_product_sq )) \
+ ( first_receiver.inner_product_sq * ( distance(tdoa_between_receivers_first_and(i)) - distance(tdoa_between_receivers_first_and_last) )) \
+ ( distance(tdoa_between_receivers_first_and_last) * ( i.inner_product_sq - distance_sq(tdoa_between_receivers_first_and(i)) ))
end

def middle_receivers
Expand All @@ -51,20 +56,16 @@ def tdoa_between_receivers_first_and_last
end

def tdoa_between_receivers_first_and(other_receiver)
time_of_arrival_strategy.tdoa(other_receiver, first_receiver)
first_receiver.time_difference_of_arrival(other_receiver)
end

def distance(time, exp=1)
(wave_speed**exp) * (time**exp)
(signal_propagation_speed**exp) * (time**exp)
end

def distance_sq(time)
distance(time, 2)
end

def inner_product_sq(vector)
vector.inner_product(vector)
end

end
end

This file was deleted.

5 changes: 5 additions & 0 deletions multilateration.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,10 @@ Gem::Specification.new do |spec|

spec.add_development_dependency "bundler", "~> 1.3"
spec.add_development_dependency "rake"
spec.add_development_dependency "pry"
spec.add_development_dependency "rspec", "~> 3.0"
spec.add_development_dependency "generative", "~> 0.2"

spec.add_dependency "rantly", "~> 0.3"

end
21 changes: 21 additions & 0 deletions spec/multilateration/signal_event_data_generator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require 'spec_helper'

describe Multilateration::SignalEventDataGenerator do
describe '.generate' do
subject(:generate) { described_class.generate }

let(:signal_event_matcher) do
{
coordinate: [be_a(Numeric), be_a(Numeric), be_a(Numeric)], # [368, -454, 978.9]
# timestamp: match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{9}(\+|\-)\d{4}$/) # "1945-12-22T18:13:16.144102423+0000"
time: be_a(Time),
}
end

specify { expect(generate).to match(
signal_propagation_speed: be_a(Numeric),
emitter_event: signal_event_matcher,
receiver_events: [signal_event_matcher,signal_event_matcher,signal_event_matcher,signal_event_matcher,signal_event_matcher],
)}
end
end
31 changes: 20 additions & 11 deletions spec/multilateration/solver_spec.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
require 'spec_helper'

describe Multilateration::Solver do
describe "#solved_vector" do
let(:source_vector) { Vector[-20,5,120.2] }
let(:reciver_vector_0) { Vector[0,25,1] }
let(:reciver_vector_1) { Vector[50,20.34,2] }
let(:reciver_vector_2) { Vector[50,50,33] }
let(:reciver_vector_3) { Vector[23,75,4] }
let(:reciver_vector_4) { Vector[100,-100,-5.5] }
let(:time_of_arrival_strategy) { Multilateration::TimeOfArrivalStrategies::SourceVectorDistance.new(source_vector) }
let(:recivers) { [reciver_vector_0, reciver_vector_1, reciver_vector_2, reciver_vector_3, reciver_vector_4] }
describe "#solved_signal_event_emitter" do
generative do
let(:tolerance) { 0.0000001 }

subject(:solved_vector) { described_class.new(recivers, time_of_arrival_strategy).solved_vector }
let(:signal_event_data) { Multilateration::SignalEventDataGenerator.generate }
let(:signal_event_emiter) { signal_event_data.fetch(:emitter_event) }
let(:signal_event_emiter_time) { signal_event_emiter[:time] }
let(:signal_event_emiter_coordinate) { signal_event_emiter[:coordinate] }
let(:signal_event_data_without_emiter) { signal_event_data.reject { |k,v| v == signal_event_emiter } }

specify { expect( solved_vector.magnitude ).to be_within(0.0000000000001).of(source_vector.magnitude) }
subject(:solved_signal_event_emitter) { described_class.new(signal_event_data_without_emiter).solved_signal_event_emitter }

specify { expect(solved_signal_event_emitter).to match({
coordinate: [
be_within(tolerance).of(signal_event_emiter_coordinate[0]),
be_within(tolerance).of(signal_event_emiter_coordinate[1]),
be_within(tolerance).of(signal_event_emiter_coordinate[2]),
],
time: be_within(tolerance).of(signal_event_emiter_time),
})}
end
end

end

This file was deleted.

3 changes: 3 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
require 'pry'
require 'generative'

require 'multilateration'

# This file was generated by the `rspec --init` command. Conventionally, all
Expand Down