├── .ruby-version ├── img ├── avans-map-cell.jpg └── avans-map-cell_4APs.jpg ├── README.md ├── Gemfile ├── .gitignore ├── img.rb ├── data.rb ├── probe.rb ├── Gemfile.lock ├── run.rb ├── run4.rb ├── run3t.rb ├── image_funcs.rb ├── spec └── trian_funcs_spec.rb ├── run2.rb ├── processor.rb ├── run5.rb ├── data ├── phone2.csv ├── laptop.csv └── phone1.csv ├── run3.rb └── trian_funcs.rb /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-1.8.7-p374 2 | -------------------------------------------------------------------------------- /img/avans-map-cell.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bob/blutrian/master/img/avans-map-cell.jpg -------------------------------------------------------------------------------- /img/avans-map-cell_4APs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bob/blutrian/master/img/avans-map-cell_4APs.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # blutrian 2 | An attempt to get the number of people in rooms based on WiFi sygnals from their smartphones 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gem "configuration", "1.3.4" 4 | gem "rmagick" 5 | gem "rspec" 6 | gem "kdtree" 7 | gem "mysql2" 8 | gem "activerecord", "2.3.18" 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | config.xml 3 | config.xml.bak 4 | wifi_config 5 | wifi_config.bak 6 | identity/client.* 7 | identity/hotspotname 8 | identity/temp.crt 9 | image.version 10 | log/* 11 | perm_log/* 12 | statistics/* 13 | weblogs/* 14 | results/ 15 | *.swp 16 | -------------------------------------------------------------------------------- /img.rb: -------------------------------------------------------------------------------- 1 | require "rubygems" 2 | require File.expand_path('../image_funcs', __FILE__) 3 | 4 | @ap1 = [10,8]; @ap2 = [9,68]; @ap3 = [74,26] 5 | 6 | canvas = ImageFuncs.new("./img/avans-map-cell.jpg", [@ap1, @ap2, @ap3]) 7 | 8 | canvas.display 9 | 10 | 11 | exit 12 | -------------------------------------------------------------------------------- /data.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | def process(stack, hotspot_id, rssi) 4 | stack[hotspot_id] = rssi 5 | 6 | stack 7 | end 8 | 9 | aps = { 10 | 8000157 => [74, 26], 11 | 8000393 => [10, 8], 12 | 8000394 => [9, 68] 13 | } 14 | stack = {} 15 | 16 | file = File.open('data/laptop.csv').read 17 | cnt = 0 18 | file.each_line do |l| 19 | line = l.chop.split("\t") 20 | hotspot_id = line[3] 21 | next unless aps.keys.include? hotspot_id.to_i 22 | 23 | rssi = line[2] 24 | 25 | #stack = process(stack, hotspot_id, rssi) 26 | stack[hotspot_id] = rssi 27 | next if stack.keys.count < 3 28 | 29 | p stack.inspect 30 | 31 | 32 | 33 | cnt += 1 34 | break if cnt > 10 35 | end 36 | -------------------------------------------------------------------------------- /probe.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require File.expand_path('../trian_funcs', __FILE__) 3 | require File.expand_path('../image_funcs', __FILE__) 4 | 5 | AP1 = 8000157 6 | AP2 = 8000393 7 | AP3 = 8000394 8 | AP4 = 8000395 9 | 10 | # APs coordinates 11 | aps = { 12 | AP1 => [74, 26], 13 | AP2 => [10, 8], 14 | AP4 => [66, 91] 15 | } 16 | aps_coords = [aps[AP1], aps[AP2], aps[AP4]] 17 | stack = {} 18 | 19 | canvas = ImageFuncs.new("./img/avans-map-cell.jpg", aps_coords) 20 | trian = TrianFuncs.new(aps_coords) 21 | 22 | a = 48; b = 61; c = 61 23 | p "a: #{a}, b: #{b}, c: #{c}" 24 | ox, oy = trian.process(a, b, c) 25 | p "Ox: #{ox}, Oy: #{oy}" 26 | canvas.draw_point(ox, oy, :green) 27 | 28 | #res.each { |i| i.each { |j| canvas.draw_point(j[0], j[1], :yellow) } } 29 | 30 | canvas.display 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | activerecord (2.3.18) 5 | activesupport (= 2.3.18) 6 | activesupport (2.3.18) 7 | configuration (1.3.4) 8 | diff-lcs (1.2.5) 9 | kdtree (0.3) 10 | mysql2 (0.2.6) 11 | rmagick (2.13.3) 12 | rspec (3.1.0) 13 | rspec-core (~> 3.1.0) 14 | rspec-expectations (~> 3.1.0) 15 | rspec-mocks (~> 3.1.0) 16 | rspec-core (3.1.7) 17 | rspec-support (~> 3.1.0) 18 | rspec-expectations (3.1.2) 19 | diff-lcs (>= 1.2.0, < 2.0) 20 | rspec-support (~> 3.1.0) 21 | rspec-mocks (3.1.3) 22 | rspec-support (~> 3.1.0) 23 | rspec-support (3.1.2) 24 | 25 | PLATFORMS 26 | ruby 27 | 28 | DEPENDENCIES 29 | activerecord (= 2.3.18) 30 | configuration (= 1.3.4) 31 | kdtree 32 | mysql2 33 | rmagick 34 | rspec 35 | -------------------------------------------------------------------------------- /run.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require File.expand_path('../trian_funcs', __FILE__) 3 | require File.expand_path('../image_funcs', __FILE__) 4 | 5 | AP1 = 8000157 6 | AP2 = 8000393 7 | AP3 = 8000394 8 | 9 | # APs coordinates 10 | aps = { 11 | AP1 => [74, 26], 12 | AP2 => [10, 8], 13 | AP3 => [9, 68], 14 | AP4 => [66, 91] 15 | } 16 | aps_coords = [aps[AP1], aps[AP2], aps[AP3]] 17 | stack = {} 18 | 19 | canvas = ImageFuncs.new("./img/avans-map-cell.jpg", aps_coords) 20 | trian = TrianFuncs.new(aps_coords) 21 | file = File.open('data/laptop.csv').read 22 | 23 | cnt = 0 24 | file.each_line do |l| 25 | line = l.chop.split("\t") 26 | hotspot_id = line[3].to_i 27 | next unless aps.keys.include? hotspot_id.to_i 28 | 29 | rssi = line[2] 30 | 31 | #stack = process(stack, hotspot_id, rssi) 32 | stack[hotspot_id] = rssi.to_i.abs 33 | next if stack.keys.count < 3 34 | 35 | p "#{line[0]} - a: #{stack[AP1]}, b: #{stack[AP2]}, c: #{stack[AP3]}" 36 | ox, oy = trian.process(stack[AP1], stack[AP2], stack[AP3]) 37 | p "Ox: #{ox}, Oy: #{oy}" 38 | canvas.draw_point(ox, oy, :green) 39 | trian.results = [] 40 | 41 | cnt += 1 42 | break if cnt > 30 43 | end 44 | 45 | p "Processed #{cnt} points" 46 | 47 | #res.each { |i| i.each { |j| canvas.draw_point(j[0], j[1], :yellow) } } 48 | 49 | canvas.display 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /run4.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require File.expand_path('../processor', __FILE__) 3 | 4 | AP1 = 8000157 5 | AP2 = 8000393 6 | AP3 = 8000394 7 | AP4 = 8000395 8 | 9 | # APs coordinates 10 | aps = { 11 | AP1 => [74, 26], 12 | AP2 => [10, 8], 13 | AP3 => [9, 68], 14 | AP4 => [66, 91] 15 | } 16 | 17 | processor = Processor.new(aps) 18 | 19 | device_name = "phone2" 20 | 21 | # cleanup results directory 22 | results_path = "./results/#{device_name}" 23 | Dir.foreach(results_path) {|f| fn = File.join(results_path, f); File.delete(fn) if f != '.' && f != '..'} 24 | 25 | file = File.open("data/#{device_name}.csv").read 26 | 27 | stack = {}; vertexes = [] 28 | cnt = 0 29 | file.each_line do |l| 30 | line = l.chop.split("\t") 31 | p line 32 | 33 | hotspot_id = line[3].to_i 34 | rssi = line[2].to_i 35 | 36 | rssi = rssi * 1.05 37 | 38 | stack[hotspot_id] = rssi.abs 39 | vertexes.push hotspot_id unless vertexes.include? hotspot_id 40 | 41 | next if stack.keys.count < 3 42 | 43 | if stack.keys.count > 3 44 | to_delete = vertexes.shift 45 | stack.delete(to_delete) 46 | end 47 | 48 | a = stack[vertexes[0]] 49 | b = stack[vertexes[1]] 50 | c = stack[vertexes[2]] 51 | 52 | teststr = "#{line[0]} - a: #{a}, b: #{b}, c: #{c}" 53 | p teststr 54 | 55 | processor.vertexes = vertexes 56 | processor.line = line 57 | 58 | processor.run a, b, c 59 | 60 | processor.canvas.scale(0.5) 61 | processor.canvas.write "#{results_path}/#{"%03d" % cnt}.jpg" 62 | 63 | cnt += 1 64 | end 65 | 66 | p "Processed #{cnt} points" 67 | 68 | #`cd results; convert *.jpg -set delay 50 laptop.gif` 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /run3t.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require File.expand_path('../processor', __FILE__) 3 | 4 | AP1 = 8000157 5 | AP2 = 8000393 6 | AP3 = 8000394 7 | AP4 = 8000395 8 | 9 | # APs coordinates 10 | aps = { 11 | AP1 => [74, 26], 12 | AP2 => [10, 8], 13 | AP3 => [9, 68], 14 | AP4 => [66, 91] 15 | } 16 | 17 | processor = Processor.new(aps) 18 | 19 | device_name = "laptop" 20 | 21 | # cleanup results directory 22 | results_path = "./results/#{device_name}" 23 | Dir.foreach(results_path) {|f| fn = File.join(results_path, f); File.delete(fn) if f != '.' && f != '..'} 24 | 25 | file = File.open("data/#{device_name}.csv").read 26 | 27 | stack = {}; vertexes = [] 28 | cnt = 0 29 | file.each_line do |l| 30 | line = l.chop.split("\t") 31 | p line 32 | 33 | hotspot_id = line[3].to_i 34 | rssi = line[2].to_i 35 | 36 | #rssi = rssi * 1.05 37 | 38 | stack[hotspot_id] = rssi.abs 39 | vertexes.push hotspot_id unless vertexes.include? hotspot_id 40 | 41 | next if stack.keys.count < 3 42 | 43 | if stack.keys.count > 3 44 | to_delete = vertexes.shift 45 | stack.delete(to_delete) 46 | end 47 | 48 | a = stack[vertexes[0]] 49 | b = stack[vertexes[1]] 50 | c = stack[vertexes[2]] 51 | 52 | teststr = "#{line[0]} - a: #{a}, b: #{b}, c: #{c}" 53 | p teststr 54 | 55 | next if teststr != "2014-10-17 12:09:30 - a: 57, b: 31, c: 58" 56 | #next if teststr != "2014-10-17 12:05:18 - a: 80, b: 72, c: 44" 57 | 58 | processor.vertexes = vertexes 59 | processor.line = line 60 | 61 | processor.run a, b, c 62 | 63 | 64 | 65 | cnt += 1 66 | break 67 | #break if cnt > 0 68 | end 69 | 70 | p "Processed #{cnt} points" 71 | 72 | #`cd results; convert *.jpg -set delay 50 laptop.gif` 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /image_funcs.rb: -------------------------------------------------------------------------------- 1 | require 'RMagick' 2 | 3 | class ImageFuncs 4 | include Magick 5 | attr_accessor :canvas, :aps 6 | 7 | def initialize(filename, aps) 8 | canvas = ImageList.new(filename) 9 | @canvas = canvas.flip 10 | @aps = aps 11 | 12 | mark_aps aps 13 | end 14 | 15 | def draw_point(x, y, color) 16 | x, y = scale_coords(x, y) 17 | 18 | circle = Magick::Draw.new 19 | circle.stroke(color.to_s) 20 | 21 | circle.fill_opacity(0) 22 | circle.stroke_opacity(0.75) 23 | circle.stroke_width(4) 24 | #circle.stroke_linecap('round') 25 | #circle.stroke_linejoin('round') 26 | circle.ellipse(x, y, 3, 3, 0, 360) 27 | 28 | circle.draw(@canvas) 29 | end 30 | 31 | def draw_line(pa, pb, color) 32 | return unless pa.class.name == "Array" 33 | 34 | x1, y1 = scale_coords(pa[0], pa[1]) 35 | x2, y2 = scale_coords(pb[0], pb[1]) 36 | 37 | draw = Magick::Draw.new 38 | draw.stroke(color.to_s) 39 | 40 | draw.line(x1, y1, x2, y2) 41 | draw.draw(@canvas) 42 | end 43 | 44 | def draw_signal(ap, signal) 45 | x, y = scale_coords(ap[0] + 5, (100 - ap[1] + 2)) 46 | annotate(signal.to_s, x, y) 47 | end 48 | 49 | def annotate(str, x, y) 50 | 51 | @canvas = @canvas.flip 52 | draw = Magick::Draw.new 53 | 54 | draw.annotate(@canvas, 0,0, x, y, str) { 55 | self.font_family = 'Helvetica' 56 | self.fill = 'black' 57 | self.stroke = 'transparent' 58 | self.pointsize = 20 59 | self.font_weight = BoldWeight 60 | self.gravity = NorthWestGravity 61 | } 62 | 63 | @canvas = @canvas.flip 64 | end 65 | 66 | def display 67 | @canvas.flip.display 68 | end 69 | 70 | def write path 71 | @canvas.flip.write path 72 | end 73 | 74 | def scale val 75 | @canvas = @canvas.scale val 76 | end 77 | 78 | private 79 | def mark_aps(aps) 80 | aps.each do |ap| 81 | draw_point(ap[0], ap[1], :red) 82 | end 83 | end 84 | 85 | def scale_coords(a, b) 86 | # image width should be correct 87 | sidesize = @canvas.columns 88 | 89 | x = sidesize * a.to_f / 100.0 90 | y = sidesize * b.to_f / 100.0 91 | 92 | [x, y] 93 | end 94 | 95 | 96 | 97 | end 98 | -------------------------------------------------------------------------------- /spec/trian_funcs_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../trian_funcs', __FILE__) 2 | #require_relative '../trian_funcs' 3 | 4 | describe TrianFuncs do 5 | before(:each) do 6 | @ap1 = [74,26]; @ap2 = [10,8]; @ap3 = [9,68] 7 | aps = [@ap1, @ap2, @ap3] 8 | @trian = TrianFuncs.new(aps) 9 | end 10 | 11 | context "#process" do 12 | it "for inner point" do 13 | a = 44; b = 59; c = 62 14 | ox, oy = @trian.process(a, b, c) 15 | 16 | expect(ox.to_s).to eq '44.4931575980041' 17 | expect(oy.to_s).to eq '39.3319026723307' 18 | end 19 | 20 | end 21 | 22 | context "#discover_angles" do 23 | 24 | it "process inner point" do 25 | fls1 = 107.609436725129; fls2 = 97.129417480387; fls3 = 125.261145794484 26 | a = 44; b = 59; c = 62 27 | ang1, ang2, ang3 = @trian.discover_angles([a, b, c], [fls1, fls2, fls3]) 28 | 29 | expect(ang1.to_s).to eq '115.575986467115' 30 | expect(ang2.to_s).to eq '79.0395047947747' 31 | expect(ang3.to_s).to eq '165.386521571667' 32 | end 33 | 34 | context "process outer point" do 35 | it "First side" do 36 | fls2 = 114.783399173471; fls3 = 103.604711979079; fls1 = 133.61188884745 37 | a = 64; b = 44; c = 68 38 | 39 | ang1, ang2, ang3 = @trian.discover_angles([a, b, c], [fls1, fls2, fls3]) 40 | expect(ang2.to_s).to eq '103.311751023685' 41 | expect(ang3.to_s).to eq '72.280388485431' 42 | expect(ang1.to_s).to eq '184.406779258533' 43 | end 44 | 45 | it "Second side" do 46 | fls3 = 114.783399173471; fls1 = 103.604711979079; fls2 = 133.61188884745 47 | a = 68; b = 64; c = 44 48 | 49 | ang1, ang2, ang3 = @trian.discover_angles([a, b, c], [fls1, fls2, fls3]) 50 | expect(ang3.to_s).to eq '103.311751023685' 51 | expect(ang1.to_s).to eq '72.280388485431' 52 | expect(ang2.to_s).to eq '184.406779258533' 53 | end 54 | 55 | it "Third side" do 56 | fls1 = 114.783399173471; fls2 = 103.604711979079; fls3 = 133.61188884745 57 | a = 44; b = 68; c = 64 58 | 59 | ang1, ang2, ang3 = @trian.discover_angles([a, b, c], [fls1, fls2, fls3]) 60 | expect(ang1.to_s).to eq '103.311751023685' 61 | expect(ang2.to_s).to eq '72.280388485431' 62 | expect(ang3.to_s).to eq '184.406779258533' 63 | end 64 | 65 | it "Error 1" do 66 | fls1 = 114.783399173471; fls2 = 103.604711979079; fls3 = 133.61188884745 67 | a = 45; b = 74; c = 57 68 | 69 | ang1, ang2, ang3 = @trian.discover_angles([a, b, c], [fls1, fls2, fls3]) 70 | end 71 | 72 | end 73 | 74 | 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /run2.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require File.expand_path('../trian_funcs', __FILE__) 3 | require File.expand_path('../image_funcs', __FILE__) 4 | 5 | AP1 = 8000157 6 | AP2 = 8000393 7 | AP3 = 8000394 8 | AP4 = 8000395 9 | 10 | # APs coordinates 11 | aps = { 12 | AP1 => [74, 26], 13 | AP2 => [10, 8], 14 | AP3 => [9, 68], 15 | AP4 => [66, 91] 16 | } 17 | 18 | #vertexes = [AP1, AP2, AP3] 19 | #vertexes = [AP2, AP3, AP4] 20 | #vertexes = [AP3, AP4, AP1] 21 | vertexes = [AP4, AP1, AP2] 22 | 23 | 24 | 25 | 26 | aps_coords = [aps[vertexes[0]], aps[vertexes[1]], aps[vertexes[2]]] 27 | stack = {} 28 | 29 | canvas = ImageFuncs.new("./img/avans-map-cell.jpg", aps_coords) 30 | trian = TrianFuncs.new(aps_coords) 31 | file = File.open('data/laptop.csv').read 32 | 33 | cnt = 0 34 | file.each_line do |l| 35 | line = l.chop.split("\t") 36 | hotspot_id = line[3].to_i 37 | next unless vertexes.include? hotspot_id.to_i 38 | 39 | #if cnt < 15 40 | #cnt += 1 41 | #next 42 | #end 43 | 44 | rssi = line[2] 45 | 46 | #stack = process(stack, hotspot_id, rssi) 47 | stack[hotspot_id] = rssi.to_i.abs 48 | next if stack.keys.count < 3 49 | 50 | a = stack[vertexes[0]] 51 | b = stack[vertexes[1]] 52 | c = stack[vertexes[2]] 53 | p "#{line[0]} - a: #{a}, b: #{b}, c: #{c}" 54 | 55 | res_points = [] 56 | 57 | p3, p4 = trian.intersection_2circles(a, c, aps[vertexes[0]], aps[vertexes[2]]) 58 | #canvas.draw_point(p3[0], p3[1], :orange) 59 | res_points << p3 60 | if p4 61 | #canvas.draw_point(p4[0], p4[1], :orange) 62 | res_points << p4 63 | end 64 | 65 | p3, p4 = trian.intersection_2circles(a, b, aps[vertexes[0]], aps[vertexes[1]]) 66 | #canvas.draw_point(p3[0], p3[1], :magenta) 67 | res_points << p3 68 | if p4 69 | #canvas.draw_point(p4[0], p4[1], :magenta) 70 | res_points << p4 71 | end 72 | 73 | p3, p4 = trian.intersection_2circles(b, c, aps[vertexes[1]], aps[vertexes[2]]) 74 | #canvas.draw_point(p3[0], p3[1], :tomato) 75 | res_points << p3 76 | if p4 77 | #canvas.draw_point(p4[0], p4[1], :tomato) 78 | res_points << p4 79 | end 80 | 81 | #p res_points 82 | points = trian.get_3nearest(res_points) 83 | #p points 84 | 85 | #canvas.draw_line(points[0], points[1], :green) 86 | #canvas.draw_line(points[1], points[2], :green) 87 | #canvas.draw_line(points[2], points[0], :green) 88 | 89 | cd = trian.get_centroid(points) 90 | canvas.draw_point(cd[0], cd[1], :green) 91 | 92 | cnt += 1 93 | #break if cnt > 1 94 | end 95 | 96 | p "Processed #{cnt} points" 97 | 98 | #res.each { |i| i.each { |j| canvas.draw_point(j[0], j[1], :yellow) } } 99 | 100 | canvas.display 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /processor.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../trian_funcs', __FILE__) 2 | require File.expand_path('../image_funcs', __FILE__) 3 | 4 | class Processor 5 | attr_accessor :aps, :vertexes, :line 6 | attr_accessor :circles_not_intersect, :lines_not_intersect 7 | attr_accessor :signals 8 | attr_accessor :canvas, :trian 9 | 10 | AP1 = 4010142 #8000157 11 | AP2 = 4010143 #8000393 12 | AP3 = 4010144 #8000394 13 | AP4 = 4010145 #8000395 14 | AP9 = 4010146 #8000438 15 | 16 | # APs coordinates 17 | COORDS = { 18 | AP1 => [56, 44], 19 | AP2 => [8, 30], 20 | AP3 => [7, 76], 21 | AP4 => [49, 93], 22 | AP9 => [25, 5] 23 | } 24 | 25 | def initialize(aps=nil) 26 | @aps = aps || COORDS 27 | @vertexes = [] 28 | @circles_not_intersect = false 29 | @lines_not_intersect = false 30 | @signals = [] 31 | end 32 | 33 | def run(a, b, c, recurs = false) 34 | unless recurs 35 | # @canvas = ImageFuncs.new("./img/avans-map-cell.jpg", @aps.values) 36 | @trian = TrianFuncs.new(aps_coords) 37 | end 38 | 39 | @signals = [a, b, c] 40 | p @signals.inspect 41 | 42 | res1 = circles_intersect(0, 1) 43 | res2 = circles_intersect(0, 2) 44 | res3 = circles_intersect(1, 2) 45 | 46 | if @circles_not_intersect 47 | @circles_not_intersect = false 48 | ts = tuned_signals 49 | return run(ts[0], ts[1], ts[2], true) 50 | end 51 | 52 | li = lines_intersect_any(res1, res2, res3) 53 | 54 | if @lines_not_intersect 55 | @lines_not_intersect = false 56 | ts = tuned_signals 57 | return run(ts[0], ts[1], ts[2], true) 58 | end 59 | 60 | #@canvas.draw_signal(aps_coords[0], @signals[0]) 61 | #@canvas.draw_signal(aps_coords[1], @signals[1]) 62 | #@canvas.draw_signal(aps_coords[2], @signals[2]) 63 | #@canvas.annotate(@line[0], 20, 20) 64 | 65 | #points = trian.get_3nearest(res_points) 66 | 67 | #canvas.draw_line(points[0], points[1], :green) 68 | #canvas.draw_line(points[1], points[2], :green) 69 | #canvas.draw_line(points[2], points[0], :green) 70 | 71 | #cd = trian.get_centroid(points) 72 | #canvas.draw_point(cd[0], cd[1], :magenta) 73 | 74 | #@canvas.display 75 | end 76 | 77 | def tuned_signals 78 | min = @signals.min 79 | res = [] 80 | res[0] = @signals[0] == min ? @signals[0] * 1.1 : @signals[0] * 1.01 81 | res[1] = @signals[1] == min ? @signals[1] * 1.1 : @signals[1] * 1.01 82 | res[2] = @signals[2] == min ? @signals[2] * 1.1 : @signals[2] * 1.01 83 | res 84 | end 85 | 86 | def circles_intersect(ind1, ind2) 87 | res_points = [] 88 | p3, p4 = @trian.intersection_2circles(@signals[ind1], @signals[ind2], @aps[@vertexes[ind1]], @aps[@vertexes[ind2]]) 89 | 90 | #@canvas.draw_point(p3[0], p3[1], :orange) 91 | res_points << p3 92 | if p4 93 | #@canvas.draw_point(p4[0], p4[1], :orange) 94 | res_points << p4 95 | else 96 | @circles_not_intersect = true 97 | end 98 | 99 | res_points 100 | end 101 | 102 | def lines_intersect_any(p1, p2, p3) 103 | res = @trian.get_lines_intersection(p1[0], p1[1], p2[0], p2[1]) 104 | res = @trian.get_lines_intersection(p2[0], p2[1], p3[0], p3[1]) if res == -1 105 | res = @trian.get_lines_intersection(p1[0], p1[1], p3[0], p3[1]) if res == -1 106 | if res == -1 107 | @lines_not_intersect = true 108 | else 109 | @canvas.draw_point(res[0], res[1], :blue) 110 | end 111 | res 112 | end 113 | 114 | def aps_coords 115 | [@aps[@vertexes[0]], @aps[@vertexes[1]], @aps[@vertexes[2]]] 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /run5.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'mysql2' 3 | require File.expand_path('../processor', __FILE__) 4 | require File.expand_path('../../blubase/app/models/api/avans', __FILE__) 5 | 6 | @points = ['AP1', 'AP2', 'AP3', 'AP4', 'AP9'] 7 | 8 | def results_period(date, start, finish) 9 | dt1 = Time.parse("#{date} #{start}").strftime("%Y-%m-%d %H:%M:%S") 10 | dt2 = Time.parse("#{date} #{finish}").strftime("%Y-%m-%d %H:%M:%S") 11 | 12 | sql = " 13 | SELECT 14 | DISTINCT drl.device_address," 15 | 16 | concats = [] 17 | @points.each_with_index do |ap, i| 18 | concats << "CONCAT(t#{i}.rssi, ' (', t#{i}.cnt, ')') AS '#{ap}'" 19 | end 20 | sql += concats.join(", ") 21 | sql += "FROM discovery_raw_logs AS drl " 22 | 23 | @points.each_with_index do |ap, i| 24 | sql += " 25 | LEFT JOIN ( 26 | SELECT DISTINCT device_address, MAX(rssi) as rssi, hotspot_id, COUNT(rssi) AS cnt 27 | FROM discovery_raw_logs WHERE discovered_at >= '#{dt1}' AND discovered_at <= '#{dt2}' AND hotspot_id = #{eval("Processor::#{ap}")} GROUP BY device_address 28 | ) AS t#{i} ON drl.device_address = t#{i}.device_address AND t#{i}.cnt > 3 29 | " 30 | end 31 | 32 | sql += " 33 | WHERE drl.discovered_at >= '#{dt1}' AND drl.discovered_at <= '#{dt2}' 34 | AND drl.device_address NOT IN(#{inner_devices_sql(date)})" 35 | 36 | #p sql.gsub(/\n/, "") 37 | 38 | results = @client.query(sql) 39 | end 40 | 41 | def inner_devices_sql(day) 42 | " 43 | SELECT device_address 44 | FROM ( 45 | SELECT device_address, (MAX(UNIX_TIMESTAMP(discovered_at)) - MIN(UNIX_TIMESTAMP(discovered_at))) AS dur 46 | FROM discovery_raw_logs 47 | WHERE day = '#{day}' 48 | GROUP BY device_address 49 | ) t2 50 | WHERE dur >= 36000 51 | " 52 | end 53 | 54 | def process_period(start, finish) 55 | # get macs matched on defined five units 56 | results = results_period('2014-11-27', start, finish) 57 | 58 | cnt = 0 59 | results.each(:as => :array) do |row| 60 | mac = row.shift 61 | # handle only macs matched on 4 or 5 units simultaneously 62 | next unless row.compact.count > 3 63 | 64 | values = {} 65 | row.each_with_index do |r, i| 66 | next unless r 67 | 68 | rssi, c = r.scan(/(\d+)\s\((\d+)\)/)[0] 69 | #p "#{i}. rssi: #{rssi}, count: #{c}" 70 | 71 | values[i] = {:rssi => rssi.to_i, :count => c.to_i} 72 | end 73 | 74 | # get 3 units with most number of matches 75 | top3 = values.sort_by{ |k, v| v[:count] }.last(3) 76 | p top3 77 | 78 | vertexes = [] 79 | vertexes[0] = eval("Processor::#{@points[top3[0][0]]}"); a = top3[0][1][:rssi] 80 | vertexes[1] = eval("Processor::#{@points[top3[1][0]]}"); b = top3[1][1][:rssi] 81 | vertexes[2] = eval("Processor::#{@points[top3[2][0]]}"); c = top3[2][1][:rssi] 82 | 83 | p "#{vertexes.join('/')}, a: #{a}, b: #{b}, c: #{c}" 84 | 85 | @processor.vertexes = vertexes 86 | @processor.run a, b, c 87 | 88 | #break if cnt == 10 89 | cnt += 1 90 | end 91 | 92 | 93 | 94 | #processor.canvas.display 95 | 96 | p "All: #{results.count}, Calculate: #{cnt - 1}" 97 | end 98 | 99 | @client = Mysql2::Client.new( 100 | :database => 'blubase', 101 | :username => 'root', 102 | :password => '', 103 | :host => 'localhost') 104 | 105 | @processor = Processor.new 106 | results_path = "./results/2014-11-27" 107 | Dir.foreach(results_path) {|f| fn = File.join(results_path, f); File.delete(fn) if f != '.' && f != '..'} 108 | 109 | Api::Avans::PERIODS.each do |period| 110 | start, finish = period 111 | 112 | @processor.canvas = ImageFuncs.new("./img/avans-map-cell.jpg", @processor.aps.values) 113 | process_period(start, finish) 114 | 115 | #processor.canvas.scale(0.5) 116 | @processor.canvas.write "#{results_path}/#{start}-#{finish}.jpg" 117 | 118 | break 119 | end 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /data/phone2.csv: -------------------------------------------------------------------------------- 1 | 2014-10-17 12:01:57 3C:25:D7:DC:9E:9C -70 8000393 2 | 2014-10-17 12:02:03 3C:25:D7:DC:9E:9C -64 8000395 3 | 2014-10-17 12:02:09 3C:25:D7:DC:9E:9C -67 8000394 4 | 2014-10-17 12:02:14 3C:25:D7:DC:9E:9C -56 8000395 5 | 2014-10-17 12:02:19 3C:25:D7:DC:9E:9C -67 8000393 6 | 2014-10-17 12:02:27 3C:25:D7:DC:9E:9C -62 8000393 7 | 2014-10-17 12:02:33 3C:25:D7:DC:9E:9C -70 8000394 8 | 2014-10-17 12:02:46 3C:25:D7:DC:9E:9C -62 8000393 9 | 2014-10-17 12:02:51 3C:25:D7:DC:9E:9C -65 8000394 10 | 2014-10-17 12:07:02 3C:25:D7:DC:9E:9C -57 8000394 11 | 2014-10-17 12:08:48 3C:25:D7:DC:9E:9C -76 8000393 12 | 2014-10-17 12:08:52 3C:25:D7:DC:9E:9C -73 8000393 13 | 2014-10-17 12:09:23 3C:25:D7:DC:9E:9C -78 8000393 14 | 2014-10-17 12:09:27 3C:25:D7:DC:9E:9C -69 8000394 15 | 2014-10-17 12:09:40 3C:25:D7:DC:9E:9C -74 8000394 16 | 2014-10-17 12:09:51 3C:25:D7:DC:9E:9C -59 8000395 17 | 2014-10-17 12:10:01 3C:25:D7:DC:9E:9C -66 8000395 18 | 2014-10-17 12:10:05 3C:25:D7:DC:9E:9C -42 8000395 19 | 2014-10-17 12:10:10 3C:25:D7:DC:9E:9C -71 8000393 20 | 2014-10-17 12:10:16 3C:25:D7:DC:9E:9C -67 8000393 21 | 2014-10-17 12:12:49 3C:25:D7:DC:9E:9C -64 8000394 22 | 2014-10-17 12:14:24 3C:25:D7:DC:9E:9C -48 8000395 23 | 2014-10-17 12:15:04 3C:25:D7:DC:9E:9C -49 8000395 24 | 2014-10-17 12:16:00 3C:25:D7:DC:9E:9C -44 8000395 25 | 2014-10-17 12:16:07 3C:25:D7:DC:9E:9C -77 8000393 26 | 2014-10-17 12:18:19 3C:25:D7:DC:9E:9C -63 8000395 27 | 2014-10-17 12:18:21 3C:25:D7:DC:9E:9C -73 8000157 28 | 2014-10-17 12:19:20 3C:25:D7:DC:9E:9C -76 8000394 29 | 2014-10-17 12:19:35 3C:25:D7:DC:9E:9C -58 8000395 30 | 2014-10-17 12:20:38 3C:25:D7:DC:9E:9C -63 8000395 31 | 2014-10-17 12:21:29 3C:25:D7:DC:9E:9C -67 8000394 32 | 2014-10-17 12:21:38 3C:25:D7:DC:9E:9C -54 8000395 33 | 2014-10-17 12:21:50 3C:25:D7:DC:9E:9C -55 8000395 34 | 2014-10-17 12:21:57 3C:25:D7:DC:9E:9C -44 8000395 35 | 2014-10-17 12:23:43 3C:25:D7:DC:9E:9C -57 8000394 36 | 2014-10-17 12:24:16 3C:25:D7:DC:9E:9C -46 8000394 37 | 2014-10-17 12:25:14 3C:25:D7:DC:9E:9C -73 8000393 38 | 2014-10-17 12:26:16 3C:25:D7:DC:9E:9C -63 8000394 39 | 2014-10-17 12:27:44 3C:25:D7:DC:9E:9C -73 8000393 40 | 2014-10-17 12:28:51 3C:25:D7:DC:9E:9C -60 8000157 41 | 2014-10-17 12:28:57 3C:25:D7:DC:9E:9C -44 8000394 42 | 2014-10-17 12:30:13 3C:25:D7:DC:9E:9C -68 8000394 43 | 2014-10-17 12:30:14 3C:25:D7:DC:9E:9C -82 8000393 44 | 2014-10-17 12:31:51 3C:25:D7:DC:9E:9C -77 8000395 45 | 2014-10-17 12:32:55 3C:25:D7:DC:9E:9C -68 8000393 46 | 2014-10-17 12:34:00 3C:25:D7:DC:9E:9C -75 8000395 47 | 2014-10-17 12:34:39 3C:25:D7:DC:9E:9C -64 8000393 48 | 2014-10-17 12:35:27 3C:25:D7:DC:9E:9C -66 8000394 49 | 2014-10-17 12:35:40 3C:25:D7:DC:9E:9C -58 8000393 50 | 2014-10-17 12:35:48 3C:25:D7:DC:9E:9C -70 8000394 51 | 2014-10-17 12:37:28 3C:25:D7:DC:9E:9C -62 8000394 52 | 2014-10-17 12:37:35 3C:25:D7:DC:9E:9C -59 8000393 53 | 2014-10-17 12:38:00 3C:25:D7:DC:9E:9C -79 8000395 54 | 2014-10-17 12:39:57 3C:25:D7:DC:9E:9C -44 8000393 55 | 2014-10-17 12:42:43 3C:25:D7:DC:9E:9C -50 8000393 56 | 2014-10-17 12:43:06 3C:25:D7:DC:9E:9C -73 8000394 57 | 2014-10-17 12:43:18 3C:25:D7:DC:9E:9C -55 8000395 58 | 2014-10-17 12:43:24 3C:25:D7:DC:9E:9C -73 8000394 59 | 2014-10-17 12:43:29 3C:25:D7:DC:9E:9C -63 8000395 60 | 2014-10-17 12:44:50 3C:25:D7:DC:9E:9C -60 8000393 61 | 2014-10-17 12:50:34 3C:25:D7:DC:9E:9C -40 8000157 62 | 2014-10-17 12:52:14 3C:25:D7:DC:9E:9C -72 8000394 63 | 2014-10-17 12:52:19 3C:25:D7:DC:9E:9C -66 8000393 64 | 2014-10-17 12:55:38 3C:25:D7:DC:9E:9C -71 8000157 65 | 2014-10-17 12:55:55 3C:25:D7:DC:9E:9C -68 8000157 66 | 2014-10-17 12:56:08 3C:25:D7:DC:9E:9C -61 8000394 67 | 2014-10-17 12:59:28 3C:25:D7:DC:9E:9C -46 8000394 68 | 2014-10-17 13:00:29 3C:25:D7:DC:9E:9C -59 8000395 69 | 2014-10-17 13:01:29 3C:25:D7:DC:9E:9C -60 8000393 70 | 2014-10-17 13:01:45 3C:25:D7:DC:9E:9C -63 8000395 71 | 2014-10-17 13:01:48 3C:25:D7:DC:9E:9C -53 8000394 72 | 2014-10-17 13:02:14 3C:25:D7:DC:9E:9C -64 8000157 73 | 2014-10-17 13:11:17 3C:25:D7:DC:9E:9C -63 8000395 74 | 2014-10-17 13:15:42 3C:25:D7:DC:9E:9C -72 8000393 75 | 2014-10-17 13:21:26 3C:25:D7:DC:9E:9C -60 8000394 76 | 2014-10-17 13:21:50 3C:25:D7:DC:9E:9C -64 8000394 77 | -------------------------------------------------------------------------------- /run3.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require File.expand_path('../trian_funcs', __FILE__) 3 | require File.expand_path('../image_funcs', __FILE__) 4 | 5 | AP1 = 8000157 6 | AP2 = 8000393 7 | AP3 = 8000394 8 | AP4 = 8000395 9 | 10 | # APs coordinates 11 | aps = { 12 | AP1 => [74, 26], 13 | AP2 => [10, 8], 14 | AP3 => [9, 68], 15 | AP4 => [66, 91] 16 | } 17 | 18 | # define color step from start/end times 19 | #f = File.open('data/laptop.csv') 20 | #file = f.read 21 | #cnt = 0 22 | #file.each_line do |l| 23 | #line = l.chop.split("\t") 24 | #t = line[0] 25 | #@ts = Time.parse(t).to_i 26 | 27 | #@first_ts = @ts if cnt == 0 28 | #cnt += 1 29 | #end 30 | #@last_ts = @ts 31 | #f.close 32 | 33 | p "First: #{@first_ts}, Last: #{@last_ts}" 34 | 35 | device_name = "laptop" 36 | 37 | # cleanup results directory 38 | results_path = "./results/#{device_name}" 39 | Dir.foreach(results_path) {|f| fn = File.join(results_path, f); File.delete(fn) if f != '.' && f != '..'} 40 | 41 | #canvas = ImageFuncs.new("./img/avans-map-cell.jpg", aps.values) 42 | file = File.open("data/#{device_name}.csv").read 43 | 44 | stack = {}; vertexes = [] 45 | cnt = 0 46 | file.each_line do |l| 47 | canvas = ImageFuncs.new("./img/avans-map-cell.jpg", aps.values) 48 | 49 | line = l.chop.split("\t") 50 | p line 51 | 52 | #ts = Time.parse(line[0]).to_i 53 | #color = 255 - (255 * (@last_ts - ts) / (@last_ts - @first_ts)) 54 | 55 | hotspot_id = line[3].to_i 56 | rssi = line[2].to_i 57 | 58 | #rssi = rssi * 1.05 59 | 60 | stack[hotspot_id] = rssi.abs 61 | vertexes.push hotspot_id unless vertexes.include? hotspot_id 62 | 63 | next if stack.keys.count < 3 64 | 65 | if stack.keys.count > 3 66 | to_delete = vertexes.shift 67 | stack.delete(to_delete) 68 | end 69 | 70 | aps_coords = [aps[vertexes[0]], aps[vertexes[1]], aps[vertexes[2]]] 71 | trian = TrianFuncs.new(aps_coords) 72 | 73 | a = stack[vertexes[0]] 74 | b = stack[vertexes[1]] 75 | c = stack[vertexes[2]] 76 | p "#{line[0]} - a: #{a}, b: #{b}, c: #{c}" 77 | 78 | canvas.draw_signal(aps[vertexes[0]], a) 79 | canvas.draw_signal(aps[vertexes[1]], b) 80 | canvas.draw_signal(aps[vertexes[2]], c) 81 | 82 | res_points = [] 83 | 84 | p3, p4 = trian.intersection_2circles(a, c, aps[vertexes[0]], aps[vertexes[2]]) 85 | canvas.draw_point(p3[0], p3[1], :orange) 86 | res_points << p3 87 | if p4 88 | canvas.draw_point(p4[0], p4[1], :orange) 89 | res_points << p4 90 | end 91 | 92 | p5, p6 = trian.intersection_2circles(a, b, aps[vertexes[0]], aps[vertexes[1]]) 93 | canvas.draw_point(p5[0], p5[1], :orange) 94 | res_points << p5 95 | if p6 96 | canvas.draw_point(p6[0], p6[1], :orange) 97 | res_points << p6 98 | end 99 | 100 | i1 = trian.get_lines_intersection(p3, p4, p5, p6) 101 | canvas.draw_point(i1[0], i1[1], :blue) 102 | 103 | p7, p8 = trian.intersection_2circles(b, c, aps[vertexes[1]], aps[vertexes[2]]) 104 | canvas.draw_point(p7[0], p8[1], :orange) 105 | res_points << p7 106 | if p4 107 | canvas.draw_point(p8[0], p8[1], :orange) 108 | res_points << p8 109 | end 110 | 111 | #i2 = trian.get_lines_intersection(p3, p4, p7, p8) 112 | #canvas.draw_point(i2[0], i2[1], :yellow) 113 | #i3 = trian.get_lines_intersection(p5, p6, p7, p8) 114 | #canvas.draw_point(i3[0], i3[1], :green) 115 | 116 | points = trian.get_3nearest(res_points) 117 | 118 | canvas.draw_line(points[0], points[1], :green) 119 | canvas.draw_line(points[1], points[2], :green) 120 | canvas.draw_line(points[2], points[0], :green) 121 | 122 | cd = trian.get_centroid(points) 123 | #canvas.draw_point(cd[0], cd[1], "#00#{"%02x" % color}00") 124 | canvas.draw_point(cd[0], cd[1], :magenta) 125 | 126 | canvas.annotate(line[0], 20, 20) 127 | 128 | canvas.scale(0.5) 129 | canvas.write "#{results_path}/#{"%03d" % cnt}.jpg" 130 | #canvas.display 131 | 132 | cnt += 1 133 | #break if cnt > 0 134 | end 135 | 136 | p "Processed #{cnt} points" 137 | 138 | #`cd results; convert *.jpg -set delay 50 laptop.gif` 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /data/laptop.csv: -------------------------------------------------------------------------------- 1 | 2014-10-17 11:53:39 28:E3:47:CB:29:1B -59 8000393 2 | 2014-10-17 11:54:53 28:E3:47:CB:29:1B -44 8000157 3 | 2014-10-17 11:56:20 28:E3:47:CB:29:1B -62 8000394 4 | 2014-10-17 11:57:32 28:E3:47:CB:29:1B -46 8000395 5 | 2014-10-17 11:57:33 28:E3:47:CB:29:1B -32 8000395 6 | 2014-10-17 11:58:18 28:E3:47:CB:29:1B -60 8000395 7 | 2014-10-17 11:59:31 28:E3:47:CB:29:1B -32 8000395 8 | 2014-10-17 11:59:34 28:E3:47:CB:29:1B -52 8000395 9 | 2014-10-17 12:00:34 28:E3:47:CB:29:1B -44 8000395 10 | 2014-10-17 12:01:47 28:E3:47:CB:29:1B -54 8000395 11 | 2014-10-17 12:01:50 28:E3:47:CB:29:1B -68 8000393 12 | 2014-10-17 12:03:08 28:E3:47:CB:29:1B -57 8000394 13 | 2014-10-17 12:03:32 28:E3:47:CB:29:1B -52 8000395 14 | 2014-10-17 12:04:05 28:E3:47:CB:29:1B -64 8000394 15 | 2014-10-17 12:04:18 28:E3:47:CB:29:1B -44 8000157 16 | 2014-10-17 12:04:22 28:E3:47:CB:29:1B -72 8000393 17 | 2014-10-17 12:05:18 28:E3:47:CB:29:1B -80 8000395 18 | 2014-10-17 12:06:19 28:E3:47:CB:29:1B -67 8000394 19 | 2014-10-17 12:06:36 28:E3:47:CB:29:1B -31 8000395 20 | 2014-10-17 12:06:38 28:E3:47:CB:29:1B -70 8000393 21 | 2014-10-17 12:07:38 28:E3:47:CB:29:1B -57 8000394 22 | 2014-10-17 12:08:33 28:E3:47:CB:29:1B -31 8000395 23 | 2014-10-17 12:09:30 28:E3:47:CB:29:1B -58 8000393 24 | 2014-10-17 12:09:46 28:E3:47:CB:29:1B -69 8000393 25 | 2014-10-17 12:09:55 28:E3:47:CB:29:1B -66 8000395 26 | 2014-10-17 12:10:18 28:E3:47:CB:29:1B -55 8000395 27 | 2014-10-17 12:10:58 28:E3:47:CB:29:1B -54 8000395 28 | 2014-10-17 12:11:26 28:E3:47:CB:29:1B -51 8000395 29 | 2014-10-17 12:11:34 28:E3:47:CB:29:1B -31 8000395 30 | 2014-10-17 12:11:41 28:E3:47:CB:29:1B -59 8000395 31 | 2014-10-17 12:12:39 28:E3:47:CB:29:1B -32 8000395 32 | 2014-10-17 12:13:49 28:E3:47:CB:29:1B -31 8000395 33 | 2014-10-17 12:13:53 28:E3:47:CB:29:1B -70 8000393 34 | 2014-10-17 12:15:05 28:E3:47:CB:29:1B -31 8000395 35 | 2014-10-17 12:17:26 28:E3:47:CB:29:1B -41 8000395 36 | 2014-10-17 12:19:54 28:E3:47:CB:29:1B -46 8000395 37 | 2014-10-17 12:19:57 28:E3:47:CB:29:1B -74 8000393 38 | 2014-10-17 12:21:07 28:E3:47:CB:29:1B -45 8000157 39 | 2014-10-17 12:21:09 28:E3:47:CB:29:1B -57 8000157 40 | 2014-10-17 12:22:22 28:E3:47:CB:29:1B -32 8000395 41 | 2014-10-17 12:22:25 28:E3:47:CB:29:1B -52 8000395 42 | 2014-10-17 12:23:32 28:E3:47:CB:29:1B -64 8000394 43 | 2014-10-17 12:23:34 28:E3:47:CB:29:1B -58 8000157 44 | 2014-10-17 12:24:47 28:E3:47:CB:29:1B -68 8000157 45 | 2014-10-17 12:26:01 28:E3:47:CB:29:1B -70 8000393 46 | 2014-10-17 12:27:16 28:E3:47:CB:29:1B -59 8000394 47 | 2014-10-17 12:27:18 28:E3:47:CB:29:1B -45 8000394 48 | 2014-10-17 12:28:32 28:E3:47:CB:29:1B -56 8000394 49 | 2014-10-17 12:29:45 28:E3:47:CB:29:1B -75 8000393 50 | 2014-10-17 12:31:15 28:E3:47:CB:29:1B -69 8000393 51 | 2014-10-17 12:31:21 28:E3:47:CB:29:1B -55 8000393 52 | 2014-10-17 12:31:28 28:E3:47:CB:29:1B -39 8000393 53 | 2014-10-17 12:31:37 28:E3:47:CB:29:1B -68 8000394 54 | 2014-10-17 12:33:26 28:E3:47:CB:29:1B -49 8000394 55 | 2014-10-17 12:34:41 28:E3:47:CB:29:1B -69 8000395 56 | 2014-10-17 12:34:42 28:E3:47:CB:29:1B -54 8000394 57 | 2014-10-17 12:35:52 28:E3:47:CB:29:1B -45 8000394 58 | 2014-10-17 12:35:53 28:E3:47:CB:29:1B -60 8000157 59 | 2014-10-17 12:37:09 28:E3:47:CB:29:1B -49 8000394 60 | 2014-10-17 12:38:20 28:E3:47:CB:29:1B -66 8000157 61 | 2014-10-17 12:39:35 28:E3:47:CB:29:1B -63 8000395 62 | 2014-10-17 12:39:42 28:E3:47:CB:29:1B -56 8000394 63 | 2014-10-17 12:41:13 28:E3:47:CB:29:1B -61 8000394 64 | 2014-10-17 12:41:18 28:E3:47:CB:29:1B -55 8000157 65 | 2014-10-17 12:42:16 28:E3:47:CB:29:1B -56 8000395 66 | 2014-10-17 12:42:18 28:E3:47:CB:29:1B -60 8000393 67 | 2014-10-17 12:43:28 28:E3:47:CB:29:1B -54 8000394 68 | 2014-10-17 12:45:57 28:E3:47:CB:29:1B -67 8000395 69 | 2014-10-17 12:47:08 28:E3:47:CB:29:1B -48 8000157 70 | 2014-10-17 12:47:10 28:E3:47:CB:29:1B -47 8000395 71 | 2014-10-17 12:48:25 28:E3:47:CB:29:1B -45 8000157 72 | 2014-10-17 12:49:39 28:E3:47:CB:29:1B -39 8000157 73 | 2014-10-17 12:50:52 28:E3:47:CB:29:1B -57 8000393 74 | 2014-10-17 12:52:03 28:E3:47:CB:29:1B -54 8000394 75 | 2014-10-17 12:52:08 28:E3:47:CB:29:1B -50 8000393 76 | 2014-10-17 12:54:40 28:E3:47:CB:29:1B -56 8000157 77 | 2014-10-17 12:54:47 28:E3:47:CB:29:1B -54 8000394 78 | 2014-10-17 12:54:52 28:E3:47:CB:29:1B -47 8000394 79 | 2014-10-17 12:56:02 28:E3:47:CB:29:1B -62 8000393 80 | 2014-10-17 12:56:05 28:E3:47:CB:29:1B -56 8000393 81 | 2014-10-17 12:57:19 28:E3:47:CB:29:1B -67 8000393 82 | 2014-10-17 12:58:29 28:E3:47:CB:29:1B -73 8000393 83 | 2014-10-17 12:58:32 28:E3:47:CB:29:1B -53 8000393 84 | 2014-10-17 13:02:08 28:E3:47:CB:29:1B -62 8000395 85 | 2014-10-17 13:04:35 28:E3:47:CB:29:1B -65 8000393 86 | 2014-10-17 13:05:45 28:E3:47:CB:29:1B -63 8000157 87 | 2014-10-17 13:05:46 28:E3:47:CB:29:1B -61 8000395 88 | 2014-10-17 13:07:01 28:E3:47:CB:29:1B -51 8000393 89 | 2014-10-17 13:08:13 28:E3:47:CB:29:1B -62 8000157 90 | 2014-10-17 13:08:17 28:E3:47:CB:29:1B -65 8000393 91 | 2014-10-17 13:09:24 28:E3:47:CB:29:1B -52 8000157 92 | 2014-10-17 13:10:15 28:E3:47:CB:29:1B -54 8000157 93 | 2014-10-17 13:18:07 28:E3:47:CB:29:1B -52 8000394 94 | 2014-10-17 13:18:10 28:E3:47:CB:29:1B -60 8000393 95 | 2014-10-17 13:18:25 28:E3:47:CB:29:1B -60 8000393 96 | 2014-10-17 13:19:24 28:E3:47:CB:29:1B -43 8000157 97 | 2014-10-17 13:21:05 28:E3:47:CB:29:1B -61 8000394 98 | 2014-10-17 13:22:19 28:E3:47:CB:29:1B -56 8000393 99 | 2014-10-17 13:23:30 28:E3:47:CB:29:1B -61 8000395 100 | 2014-10-17 13:24:42 28:E3:47:CB:29:1B -48 8000157 101 | 2014-10-17 13:24:44 28:E3:47:CB:29:1B -61 8000393 102 | 2014-10-17 13:25:57 28:E3:47:CB:29:1B -62 8000395 103 | 2014-10-17 13:27:09 28:E3:47:CB:29:1B -62 8000395 104 | 2014-10-17 13:28:24 28:E3:47:CB:29:1B -43 8000157 105 | 2014-10-17 13:28:26 28:E3:47:CB:29:1B -73 8000393 106 | 2014-10-17 13:34:04 28:E3:47:CB:29:1B -81 8000393 107 | 2014-10-17 13:34:18 28:E3:47:CB:29:1B -80 8000395 108 | 2014-10-17 13:34:22 28:E3:47:CB:29:1B -62 8000395 109 | 2014-10-17 13:34:29 28:E3:47:CB:29:1B -38 8000395 110 | 2014-10-17 13:35:39 28:E3:47:CB:29:1B -67 8000395 111 | 2014-10-17 13:35:40 28:E3:47:CB:29:1B -52 8000394 112 | 2014-10-17 13:36:59 28:E3:47:CB:29:1B -55 8000395 113 | 2014-10-17 13:38:10 28:E3:47:CB:29:1B -51 8000394 114 | 2014-10-17 13:38:11 28:E3:47:CB:29:1B -53 8000395 115 | 2014-10-17 13:39:27 28:E3:47:CB:29:1B -69 8000393 116 | 2014-10-17 13:40:39 28:E3:47:CB:29:1B -69 8000395 117 | 2014-10-17 13:41:34 28:E3:47:CB:29:1B -64 8000395 118 | -------------------------------------------------------------------------------- /data/phone1.csv: -------------------------------------------------------------------------------- 1 | 2014-10-17 12:01:06 18:87:96:64:E6:B9 -68 8000395 2 | 2014-10-17 12:01:12 18:87:96:64:E6:B9 -54 8000394 3 | 2014-10-17 12:01:17 18:87:96:64:E6:B9 -77 8000394 4 | 2014-10-17 12:01:23 18:87:96:64:E6:B9 -62 8000395 5 | 2014-10-17 12:01:30 18:87:96:64:E6:B9 -66 8000395 6 | 2014-10-17 12:01:37 18:87:96:64:E6:B9 -69 8000393 7 | 2014-10-17 12:01:54 18:87:96:64:E6:B9 -64 8000394 8 | 2014-10-17 12:02:00 18:87:96:64:E6:B9 -61 8000394 9 | 2014-10-17 12:02:06 18:87:96:64:E6:B9 -64 8000394 10 | 2014-10-17 12:02:12 18:87:96:64:E6:B9 -67 8000393 11 | 2014-10-17 12:02:24 18:87:96:64:E6:B9 -60 8000157 12 | 2014-10-17 12:02:31 18:87:96:64:E6:B9 -67 8000393 13 | 2014-10-17 12:02:38 18:87:96:64:E6:B9 -62 8000395 14 | 2014-10-17 12:02:43 18:87:96:64:E6:B9 -71 8000393 15 | 2014-10-17 12:07:49 18:87:96:64:E6:B9 -47 8000395 16 | 2014-10-17 12:08:31 18:87:96:64:E6:B9 -76 8000393 17 | 2014-10-17 12:09:22 18:87:96:64:E6:B9 -63 8000395 18 | 2014-10-17 12:09:27 18:87:96:64:E6:B9 -84 8000393 19 | 2014-10-17 12:09:32 18:87:96:64:E6:B9 -79 8000393 20 | 2014-10-17 12:09:38 18:87:96:64:E6:B9 -67 8000394 21 | 2014-10-17 12:09:46 18:87:96:64:E6:B9 -60 8000157 22 | 2014-10-17 12:09:58 18:87:96:64:E6:B9 -73 8000394 23 | 2014-10-17 12:10:00 18:87:96:64:E6:B9 -45 8000395 24 | 2014-10-17 12:10:08 18:87:96:64:E6:B9 -77 8000394 25 | 2014-10-17 12:10:15 18:87:96:64:E6:B9 -69 8000393 26 | 2014-10-17 12:16:19 18:87:96:64:E6:B9 -73 8000394 27 | 2014-10-17 12:16:19 18:87:96:64:E6:B9 -79 8000393 28 | 2014-10-17 12:16:33 18:87:96:64:E6:B9 -80 8000393 29 | 2014-10-17 12:17:13 18:87:96:64:E6:B9 -71 8000395 30 | 2014-10-17 12:17:27 18:87:96:64:E6:B9 -68 8000394 31 | 2014-10-17 12:17:33 18:87:96:64:E6:B9 -55 8000394 32 | 2014-10-17 12:17:43 18:87:96:64:E6:B9 -45 8000395 33 | 2014-10-17 12:18:03 18:87:96:64:E6:B9 -44 8000395 34 | 2014-10-17 12:18:15 18:87:96:64:E6:B9 -52 8000395 35 | 2014-10-17 12:18:16 18:87:96:64:E6:B9 -64 8000395 36 | 2014-10-17 12:18:32 18:87:96:64:E6:B9 -54 8000395 37 | 2014-10-17 12:19:28 18:87:96:64:E6:B9 -69 8000394 38 | 2014-10-17 12:19:45 18:87:96:64:E6:B9 -61 8000157 39 | 2014-10-17 12:21:39 18:87:96:64:E6:B9 -68 8000393 40 | 2014-10-17 12:22:01 18:87:96:64:E6:B9 -55 8000395 41 | 2014-10-17 12:22:19 18:87:96:64:E6:B9 -54 8000395 42 | 2014-10-17 12:22:26 18:87:96:64:E6:B9 -66 8000395 43 | 2014-10-17 12:22:30 18:87:96:64:E6:B9 -47 8000395 44 | 2014-10-17 12:22:34 18:87:96:64:E6:B9 -59 8000395 45 | 2014-10-17 12:22:51 18:87:96:64:E6:B9 -56 8000395 46 | 2014-10-17 12:22:55 18:87:96:64:E6:B9 -70 8000393 47 | 2014-10-17 12:22:59 18:87:96:64:E6:B9 -61 8000393 48 | 2014-10-17 12:23:04 18:87:96:64:E6:B9 -65 8000394 49 | 2014-10-17 12:23:10 18:87:96:64:E6:B9 -63 8000393 50 | 2014-10-17 12:25:39 18:87:96:64:E6:B9 -65 8000395 51 | 2014-10-17 12:25:43 18:87:96:64:E6:B9 -65 8000394 52 | 2014-10-17 12:25:46 18:87:96:64:E6:B9 -54 8000393 53 | 2014-10-17 12:25:50 18:87:96:64:E6:B9 -59 8000395 54 | 2014-10-17 12:26:03 18:87:96:64:E6:B9 -78 8000393 55 | 2014-10-17 12:26:06 18:87:96:64:E6:B9 -76 8000393 56 | 2014-10-17 12:26:23 18:87:96:64:E6:B9 -59 8000395 57 | 2014-10-17 12:26:29 18:87:96:64:E6:B9 -42 8000395 58 | 2014-10-17 12:26:34 18:87:96:64:E6:B9 -62 8000395 59 | 2014-10-17 12:26:47 18:87:96:64:E6:B9 -42 8000395 60 | 2014-10-17 12:26:53 18:87:96:64:E6:B9 -55 8000395 61 | 2014-10-17 12:27:00 18:87:96:64:E6:B9 -69 8000157 62 | 2014-10-17 12:27:06 18:87:96:64:E6:B9 -64 8000157 63 | 2014-10-17 12:27:06 18:87:96:64:E6:B9 -81 8000393 64 | 2014-10-17 12:27:28 18:87:96:64:E6:B9 -71 8000395 65 | 2014-10-17 12:27:47 18:87:96:64:E6:B9 -50 8000395 66 | 2014-10-17 12:27:58 18:87:96:64:E6:B9 -61 8000395 67 | 2014-10-17 12:28:13 18:87:96:64:E6:B9 -66 8000157 68 | 2014-10-17 12:28:22 18:87:96:64:E6:B9 -74 8000393 69 | 2014-10-17 12:32:42 18:87:96:64:E6:B9 -64 8000393 70 | 2014-10-17 12:32:47 18:87:96:64:E6:B9 -52 8000393 71 | 2014-10-17 12:32:56 18:87:96:64:E6:B9 -53 8000394 72 | 2014-10-17 12:33:08 18:87:96:64:E6:B9 -59 8000394 73 | 2014-10-17 12:33:16 18:87:96:64:E6:B9 -59 8000395 74 | 2014-10-17 12:33:21 18:87:96:64:E6:B9 -68 8000395 75 | 2014-10-17 12:33:28 18:87:96:64:E6:B9 -61 8000395 76 | 2014-10-17 12:33:35 18:87:96:64:E6:B9 -70 8000394 77 | 2014-10-17 12:33:41 18:87:96:64:E6:B9 -72 8000394 78 | 2014-10-17 12:33:48 18:87:96:64:E6:B9 -71 8000394 79 | 2014-10-17 12:39:46 18:87:96:64:E6:B9 -58 8000395 80 | 2014-10-17 12:42:07 18:87:96:64:E6:B9 -60 8000395 81 | 2014-10-17 12:42:11 18:87:96:64:E6:B9 -58 8000395 82 | 2014-10-17 12:42:18 18:87:96:64:E6:B9 -68 8000394 83 | 2014-10-17 12:42:27 18:87:96:64:E6:B9 -73 8000394 84 | 2014-10-17 12:42:33 18:87:96:64:E6:B9 -38 8000157 85 | 2014-10-17 12:42:39 18:87:96:64:E6:B9 -51 8000157 86 | 2014-10-17 12:42:56 18:87:96:64:E6:B9 -60 8000395 87 | 2014-10-17 12:42:56 18:87:96:64:E6:B9 -38 8000157 88 | 2014-10-17 12:43:03 18:87:96:64:E6:B9 -57 8000393 89 | 2014-10-17 12:43:10 18:87:96:64:E6:B9 -39 8000157 90 | 2014-10-17 12:43:16 18:87:96:64:E6:B9 -65 8000394 91 | 2014-10-17 12:49:01 18:87:96:64:E6:B9 -78 8000393 92 | 2014-10-17 12:49:07 18:87:96:64:E6:B9 -63 8000394 93 | 2014-10-17 12:49:10 18:87:96:64:E6:B9 -75 8000393 94 | 2014-10-17 12:49:15 18:87:96:64:E6:B9 -64 8000395 95 | 2014-10-17 12:49:23 18:87:96:64:E6:B9 -68 8000393 96 | 2014-10-17 12:49:35 18:87:96:64:E6:B9 -65 8000394 97 | 2014-10-17 12:49:37 18:87:96:64:E6:B9 -67 8000394 98 | 2014-10-17 12:49:48 18:87:96:64:E6:B9 -47 8000157 99 | 2014-10-17 12:49:55 18:87:96:64:E6:B9 -61 8000393 100 | 2014-10-17 12:49:56 18:87:96:64:E6:B9 -47 8000157 101 | 2014-10-17 12:50:05 18:87:96:64:E6:B9 -65 8000394 102 | 2014-10-17 12:51:34 18:87:96:64:E6:B9 -61 8000395 103 | 2014-10-17 12:54:45 18:87:96:64:E6:B9 -72 8000395 104 | 2014-10-17 12:54:53 18:87:96:64:E6:B9 -61 8000393 105 | 2014-10-17 12:54:55 18:87:96:64:E6:B9 -69 8000393 106 | 2014-10-17 12:55:03 18:87:96:64:E6:B9 -67 8000395 107 | 2014-10-17 12:55:08 18:87:96:64:E6:B9 -53 8000394 108 | 2014-10-17 12:55:22 18:87:96:64:E6:B9 -57 8000394 109 | 2014-10-17 12:55:34 18:87:96:64:E6:B9 -70 8000157 110 | 2014-10-17 12:55:39 18:87:96:64:E6:B9 -63 8000394 111 | 2014-10-17 12:56:04 18:87:96:64:E6:B9 -67 8000395 112 | 2014-10-17 12:56:28 18:87:96:64:E6:B9 -59 8000395 113 | 2014-10-17 13:10:54 18:87:96:64:E6:B9 -77 8000393 114 | 2014-10-17 13:21:30 18:87:96:64:E6:B9 -71 8000395 115 | 2014-10-17 13:21:42 18:87:96:64:E6:B9 -72 8000394 116 | 2014-10-17 13:21:52 18:87:96:64:E6:B9 -70 8000393 117 | 2014-10-17 13:21:57 18:87:96:64:E6:B9 -57 8000393 118 | 2014-10-17 13:22:08 18:87:96:64:E6:B9 -75 8000395 119 | 2014-10-17 13:22:21 18:87:96:64:E6:B9 -75 8000395 120 | 2014-10-17 13:22:26 18:87:96:64:E6:B9 -67 8000157 121 | 2014-10-17 13:22:32 18:87:96:64:E6:B9 -70 8000394 122 | 2014-10-17 13:22:39 18:87:96:64:E6:B9 -59 8000157 123 | 2014-10-17 13:23:02 18:87:96:64:E6:B9 -68 8000393 124 | 2014-10-17 13:23:07 18:87:96:64:E6:B9 -59 8000395 125 | 2014-10-17 13:23:20 18:87:96:64:E6:B9 -67 8000157 126 | 2014-10-17 13:23:27 18:87:96:64:E6:B9 -64 8000395 127 | 2014-10-17 13:23:37 18:87:96:64:E6:B9 -65 8000157 128 | 2014-10-17 13:23:49 18:87:96:64:E6:B9 -68 8000393 129 | 2014-10-17 13:23:53 18:87:96:64:E6:B9 -60 8000157 130 | 2014-10-17 13:24:00 18:87:96:64:E6:B9 -72 8000395 131 | 2014-10-17 13:24:08 18:87:96:64:E6:B9 -66 8000393 132 | 2014-10-17 13:24:18 18:87:96:64:E6:B9 -67 8000393 133 | 2014-10-17 13:24:28 18:87:96:64:E6:B9 -67 8000393 134 | 2014-10-17 13:24:34 18:87:96:64:E6:B9 -49 8000157 135 | 2014-10-17 13:24:38 18:87:96:64:E6:B9 -77 8000395 136 | 2014-10-17 13:24:54 18:87:96:64:E6:B9 -59 8000157 137 | 2014-10-17 13:25:02 18:87:96:64:E6:B9 -49 8000157 138 | 2014-10-17 13:25:03 18:87:96:64:E6:B9 -71 8000393 139 | 2014-10-17 13:25:10 18:87:96:64:E6:B9 -46 8000157 140 | 2014-10-17 13:25:17 18:87:96:64:E6:B9 -74 8000393 141 | 2014-10-17 13:25:19 18:87:96:64:E6:B9 -74 8000395 142 | 2014-10-17 13:25:24 18:87:96:64:E6:B9 -65 8000157 143 | 2014-10-17 13:25:32 18:87:96:64:E6:B9 -68 8000393 144 | 2014-10-17 13:25:35 18:87:96:64:E6:B9 -67 8000395 145 | 2014-10-17 13:25:39 18:87:96:64:E6:B9 -72 8000395 146 | 2014-10-17 13:25:56 18:87:96:64:E6:B9 -64 8000395 147 | 2014-10-17 13:26:02 18:87:96:64:E6:B9 -65 8000393 148 | 2014-10-17 13:26:06 18:87:96:64:E6:B9 -74 8000395 149 | 2014-10-17 13:26:21 18:87:96:64:E6:B9 -71 8000157 150 | 2014-10-17 13:26:27 18:87:96:64:E6:B9 -76 8000394 151 | 2014-10-17 13:26:32 18:87:96:64:E6:B9 -78 8000395 152 | 2014-10-17 13:26:37 18:87:96:64:E6:B9 -76 8000394 153 | 2014-10-17 13:26:51 18:87:96:64:E6:B9 -68 8000395 154 | 2014-10-17 13:26:54 18:87:96:64:E6:B9 -62 8000395 155 | 2014-10-17 13:27:00 18:87:96:64:E6:B9 -65 8000157 156 | 2014-10-17 13:27:07 18:87:96:64:E6:B9 -76 8000394 157 | 2014-10-17 13:27:19 18:87:96:64:E6:B9 -46 8000157 158 | 2014-10-17 13:27:33 18:87:96:64:E6:B9 -77 8000395 159 | 2014-10-17 13:27:56 18:87:96:64:E6:B9 -79 8000395 160 | 2014-10-17 13:28:00 18:87:96:64:E6:B9 -63 8000394 161 | 2014-10-17 13:28:08 18:87:96:64:E6:B9 -66 8000157 162 | 2014-10-17 13:28:22 18:87:96:64:E6:B9 -61 8000157 163 | 2014-10-17 13:28:27 18:87:96:64:E6:B9 -55 8000157 164 | 2014-10-17 13:28:33 18:87:96:64:E6:B9 -56 8000395 165 | 2014-10-17 13:28:37 18:87:96:64:E6:B9 -55 8000393 166 | 2014-10-17 13:28:49 18:87:96:64:E6:B9 -58 8000157 167 | 2014-10-17 13:28:55 18:87:96:64:E6:B9 -62 8000394 168 | 2014-10-17 13:28:56 18:87:96:64:E6:B9 -68 8000395 169 | 2014-10-17 13:29:00 18:87:96:64:E6:B9 -59 8000394 170 | 2014-10-17 13:29:06 18:87:96:64:E6:B9 -64 8000157 171 | 2014-10-17 13:29:17 18:87:96:64:E6:B9 -66 8000395 172 | 2014-10-17 13:29:24 18:87:96:64:E6:B9 -62 8000157 173 | 2014-10-17 13:29:31 18:87:96:64:E6:B9 -64 8000394 174 | 2014-10-17 13:29:38 18:87:96:64:E6:B9 -59 8000393 175 | 2014-10-17 13:29:51 18:87:96:64:E6:B9 -63 8000395 176 | 2014-10-17 13:29:56 18:87:96:64:E6:B9 -65 8000157 177 | 2014-10-17 13:30:02 18:87:96:64:E6:B9 -58 8000393 178 | 2014-10-17 13:30:07 18:87:96:64:E6:B9 -59 8000157 179 | 2014-10-17 13:30:24 18:87:96:64:E6:B9 -56 8000157 180 | 2014-10-17 13:30:32 18:87:96:64:E6:B9 -62 8000394 181 | 2014-10-17 13:30:39 18:87:96:64:E6:B9 -57 8000393 182 | 2014-10-17 13:30:58 18:87:96:64:E6:B9 -67 8000394 183 | 2014-10-17 13:31:04 18:87:96:64:E6:B9 -66 8000395 184 | 2014-10-17 13:31:09 18:87:96:64:E6:B9 -59 8000393 185 | 2014-10-17 13:31:16 18:87:96:64:E6:B9 -68 8000395 186 | 2014-10-17 13:31:21 18:87:96:64:E6:B9 -71 8000395 187 | 2014-10-17 13:31:26 18:87:96:64:E6:B9 -57 8000393 188 | 2014-10-17 13:31:32 18:87:96:64:E6:B9 -63 8000393 189 | 2014-10-17 13:31:51 18:87:96:64:E6:B9 -60 8000157 190 | 2014-10-17 13:31:55 18:87:96:64:E6:B9 -55 8000157 191 | 2014-10-17 13:32:01 18:87:96:64:E6:B9 -60 8000393 192 | 2014-10-17 13:32:09 18:87:96:64:E6:B9 -53 8000157 193 | 2014-10-17 13:32:21 18:87:96:64:E6:B9 -78 8000393 194 | 2014-10-17 13:33:09 18:87:96:64:E6:B9 -64 8000393 195 | 2014-10-17 13:33:23 18:87:96:64:E6:B9 -49 8000395 196 | 2014-10-17 13:36:11 18:87:96:64:E6:B9 -44 8000395 197 | 2014-10-17 13:36:31 18:87:96:64:E6:B9 -37 8000395 198 | -------------------------------------------------------------------------------- /trian_funcs.rb: -------------------------------------------------------------------------------- 1 | require "kdtree" 2 | 3 | # for ruby 1.8.7 4 | class Float 5 | alias oldround:round 6 | def round(precision = nil) 7 | if precision.nil? 8 | return self 9 | else 10 | return ((self * 10**precision).oldround.to_f) / (10**precision) 11 | end 12 | end 13 | end 14 | 15 | class Numeric 16 | def percent_of(n) 17 | self.to_f / n.to_f * 100.0 18 | end 19 | 20 | def portion_of(n) 21 | self.to_f * n.to_f / 100.0 22 | end 23 | 24 | def to_radians 25 | self * Math::PI / 180 26 | end 27 | 28 | def to_degrees 29 | self * 180 / Math::PI 30 | end 31 | end 32 | 33 | class TrianFuncs 34 | attr_accessor :debug 35 | attr_accessor :ap1, :ap2, :ap3, :a, :b, :c, :l1, :l2, :l3 36 | attr_accessor :result_is_inner 37 | attr_accessor :results 38 | 39 | def initialize(aps) 40 | @ap1 = aps[0] 41 | @ap2 = aps[1] 42 | @ap3 = aps[2] 43 | @debug = true 44 | @result_is_inner = true 45 | @results = [] 46 | end 47 | 48 | # Returns coordinates of the needed point 49 | # Point of the a,b,c intersection 50 | def process(a, b, c) 51 | # 2. Calc sides real lengths. ('Real' means plan metrics (mm)). Get l1, l2, l3 52 | @l1 = get_side_real_length(@ap1, @ap2) 53 | @l2 = get_side_real_length(@ap2, @ap3) 54 | @l3 = get_side_real_length(@ap3, @ap1) 55 | p "Length: #{@l1}, #{@l2}, #{@l3}" if @debug 56 | 57 | # 3. Get percents of sides real lengths 58 | l1_p, l2_p, l3_p = get_percentage_for(@l1, @l2, @l3) 59 | p "Length percents: #{l1_p}, #{l2_p}, #{l3_p}, #{l1_p + l2_p + l3_p}" if @debug 60 | 61 | # 4. Get start-values for angles discovering. 62 | fls1, fls2, fls3 = get_discovering_starts([a, b, c], [l1_p, l2_p, l3_p]) 63 | p "Discovering start values: #{fls1}, #{fls2}, #{fls3}, S: #{fls1 + fls2 + fls3}" if @debug 64 | 65 | # 5. Get angles. ab_angle, bc_angle, ca_angle 66 | ang1, ang2, ang3 = discover_angles([a, b, c], [fls1, fls2, fls3]) 67 | p "Discover angles: #{ang1}, #{ang2}, #{ang3}" if @debug 68 | return [0,0] if ang1 == 0 or !ang2 == 0 or !ang3 == 0 69 | 70 | # --- Below are actions for each pair of sides 71 | @results << discover_coords(a, b, ang1, @l1, @ap1, @ap2) 72 | @results << discover_coords(b, c, ang2, @l2, @ap2, @ap3) 73 | @results << discover_coords(c, a, ang3, @l3, @ap3, @ap1) 74 | 75 | p "Results: inner?: #{@result_is_inner}, #{@results.inspect}" if @debug 76 | 77 | @results.last[0] 78 | end 79 | 80 | def discover_coords(ga, gb, gang, gl, gap1, gap2) 81 | res = [] 82 | 83 | # 6. Get triangle side fake length via formula for 2 sides and angle 84 | fd = get_side_by_2sides_and_angle(ga, gb, gang) 85 | #p "Side fake length: #{fd}" 86 | 87 | # 7. Get fake-b-side-part from formula for circles intersection points 88 | fb = (gb**2 - ga**2 + fd**2) / (2 * fd) 89 | #p "Side fake b part: #{fb}" 90 | 91 | # 8. Get real-b-side-part via proportion formula. d = l1. 92 | rb = (fb * gl) / fd 93 | #p "Side real b part: #{rb}" 94 | 95 | # 9. Get real-a-side-part 96 | ra = gl - rb 97 | #p "Side real a part: #{ra}" 98 | 99 | # 10. Get fake-h via Pythagor formula 100 | fh = Math.sqrt(gb**2 - fb**2) 101 | #p "Fake h: #{fh}" 102 | 103 | # 11. Get real-h via proportion formula 104 | rh = fh * gl / fd 105 | #p "Real h: #{rh}" 106 | 107 | # 12. Get real side-point coordinates 108 | rcx = gap1[0] + (ra * (gap2[0] - gap1[0]) / gl) 109 | rcy = gap1[1] + (ra * (gap2[1] - gap1[1]) / gl) 110 | #p "C coords: [#{rcx}, #{rcy}]" 111 | 112 | # 13. Get ox, oy coordinates 113 | ox = rcx + ((gap2[1] - gap1[1]) * rh / gl) 114 | oy = rcy - ((gap2[0] - gap1[0]) * rh / gl) 115 | res << [ox, oy] if @result_is_inner == is_inner?(ox, oy) 116 | p "Coords 1: [#{ox.round(2)}, #{oy.round(2)}] inner: #{is_inner?(ox, oy)}" if @debug 117 | 118 | ox2 = rcx - ((gap2[1] - gap1[1]) * rh / gl) 119 | oy2 = rcy + ((gap2[0] - gap1[0]) * rh / gl) 120 | res << [ox2, oy2] if @result_is_inner == is_inner?(ox2, oy2) 121 | p "Coords 2: [#{ox2.round(2)}, #{oy2.round(2)}] inner: #{is_inner?(ox2, oy2)}" if @debug 122 | 123 | res 124 | end 125 | 126 | def intersection_2circles(r2, r1, p2, p1) 127 | # get d 128 | d = get_side_real_length(p1, p2) 129 | #p "d: #{d}" if @debug 130 | 131 | # circles are not intersecting 132 | if (r1 + r2) < d 133 | p "Circles NOT intersecting" if @debug 134 | 135 | o = r1 + (d - (r1 + r2)) / 2 136 | p0x = p1[0] + (o * (p2[0] - p1[0]) / d) 137 | p0y = p1[1] + (o * (p2[1] - p1[1]) / d) 138 | 139 | return [[p0x, p0y]] 140 | end 141 | 142 | # get b part 143 | b = (r2**2 - r1**2 + d**2) / (2 * d) 144 | #p "b: #{b}" if @debug 145 | 146 | # get a part 147 | a = d - b 148 | 149 | # get h 150 | h = Math.sqrt(r2**2 - b**2) 151 | #p "h: #{h}" if @debug 152 | 153 | # get p0 154 | p0x = p1[0] + (a * (p2[0] - p1[0]) / d) 155 | p0y = p1[1] + (a * (p2[1] - p1[1]) / d) 156 | 157 | # get p3 158 | p3x = p0x + ((p2[1] - p1[1]) * h / d) 159 | p3y = p0y - ((p2[0] - p1[0]) * h / d) 160 | #p "p3: [#{p3x},#{p3y}]" 161 | 162 | # get p4 163 | p4x = p0x - ((p2[1] - p1[1]) * h / d) 164 | p4y = p0y + ((p2[0] - p1[0]) * h / d) 165 | #p "p4: [#{p4x},#{p4y}]" 166 | 167 | [[p3x, p3y],[p4x, p4y]] 168 | end 169 | 170 | def get_side_real_length(p1, p2) 171 | Math.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2) 172 | end 173 | 174 | def get_percentage_for(i1, i2, i3) 175 | sum = i1 + i2 + i3 176 | [i1.percent_of(sum), i2.percent_of(sum), i3.percent_of(sum)] 177 | end 178 | 179 | def get_discovering_starts(signals, percents) 180 | s1 = signals[0] + signals[1] 181 | s2 = signals[1] + signals[2] 182 | s3 = signals[2] + signals[0] 183 | s = s1 + s2 + s3 184 | 185 | #s = signals[0] + signals[1] + signals[2] 186 | 187 | [percents[0].portion_of(s), percents[1].portion_of(s), percents[2].portion_of(s)] 188 | end 189 | 190 | def get_angle_by_3sides(a, b, c) 191 | v1 = a.to_f**2 + b.to_f**2 - c.to_f**2 192 | val = v1 / (2 * a * b) 193 | Math.acos(val).to_degrees rescue 0.0 194 | end 195 | 196 | def get_side_by_2sides_and_angle(a, b, angle) 197 | v1 = a.to_f**2 + b.to_f**2 198 | val = v1 - 2 * a * b * Math.cos(angle.to_radians) 199 | Math.sqrt(val) 200 | end 201 | 202 | def discover_angles(sides, starts) 203 | 204 | format = "%5s\t| %15s\t| %15s\t| %15s\t| %5s\n" 205 | printf format, "", "1", "2", "3", "sum" if @debug 206 | 207 | ang_sum = 0.0 208 | fls1, fls2, fls3 = starts 209 | 210 | shift = 0 211 | shift_step = 10 212 | pereval = true 213 | cnt = 0 214 | ang1_outer = false; ang2_outer = false; ang3_outer = false 215 | #while cnt < 40 do 216 | while ang_sum.round(2) != 360.0 do 217 | fls1 += shift; fls2 += shift; fls3 += shift 218 | 219 | ang1 = get_angle_by_3sides(sides[0], sides[1], fls1) 220 | ang2 = get_angle_by_3sides(sides[1], sides[2], fls2) 221 | ang3 = get_angle_by_3sides(sides[2], sides[0], fls3) 222 | 223 | ang1_val = (ang1_outer ? (360 - ang1) : ang1) 224 | ang2_val = (ang2_outer ? (360 - ang2) : ang2) 225 | ang3_val = (ang3_outer ? (360 - ang3) : ang3) 226 | ang_sum = ang1_val + ang2_val + ang3_val 227 | 228 | printf format, "#{shift}", "#{"*" if ang1_outer}#{fls1.round(2)} - #{ang1_val.round(2)}", "#{"*" if ang2_outer}#{fls2.round(2)} - #{ang2_val.round(2)}", "#{"*" if ang3_outer}#{fls3.round(2)} - #{ang3_val.round(2)}", "#{ang_sum}" if @debug 229 | 230 | if ang1.zero? or ang2.zero? or ang3.zero? 231 | if pereval # in this case we think that this are first rough reduces 232 | shift = -(shift_step) 233 | else # this case for outer point 234 | ang1_outer = true if ang1.zero? 235 | ang2_outer = true if ang2.zero? 236 | ang3_outer = true if ang3.zero? 237 | 238 | pereval = !pereval 239 | shift = -(shift_step) 240 | end 241 | cnt += 1 242 | next 243 | end 244 | 245 | if ang1_outer or ang2_outer or ang3_outer 246 | conds = "ang_sum.round(2) >= 360.0" 247 | else 248 | conds = "ang_sum.round(2) < 360.0" 249 | end 250 | 251 | if eval(conds) 252 | if pereval 253 | shift_step = (shift_step.to_f / 10) 254 | pereval = false 255 | end 256 | shift = shift_step.to_f 257 | else 258 | if !pereval 259 | shift_step = (shift_step.to_f / 10) 260 | pereval = true 261 | end 262 | shift = -(shift_step) 263 | end 264 | 265 | if cnt > 40 266 | return [0,0,0] 267 | end 268 | cnt += 1 269 | end 270 | 271 | @result_is_inner = false if ang1_outer or ang2_outer or ang3_outer 272 | 273 | [ang1_val, ang2_val, ang3_val] 274 | end 275 | 276 | def is_inner?(px, py) 277 | pp = [px, py] 278 | s = square(@ap1, @ap2, pp) + square(@ap2, @ap3, pp) + square(@ap3, @ap1, pp) 279 | (square(@ap1, @ap2, @ap3) - s).abs <= 0.01 280 | end 281 | 282 | def square(a, b, c) 283 | #p "Ax: #{a[0]}, Ay: #{a[1]}" 284 | #p "Bx: #{b[0]}, By: #{b[1]}" 285 | #p "Cx: #{c[0]}, Cy: #{c[1]}" 286 | 287 | res = b[0]*c[1] - c[0]*b[1] - a[0]*c[1] + c[0]*a[1] + a[0]*b[1] - b[0]*a[1] 288 | res.abs / 2.0 289 | end 290 | 291 | def get_3nearest(res_points) 292 | points = []; res = [] 293 | res_points.each_with_index do |p, index| 294 | points << [p[0], p[1], index] 295 | end 296 | 297 | kd = Kdtree.new(points) 298 | 299 | nearests = [] 300 | res_points.each do |p| 301 | nearests << kd.nearestk(p[0], p[1], 3) 302 | end 303 | 304 | #p res_points 305 | #p nearests 306 | indices = dup_indices(nearests) 307 | #p indices 308 | 309 | nearest3 = indices.values.select{|v| v.count == 3}.first 310 | nearest3 = nearests[indices.values.first[0]] unless nearest3 311 | #p nearest3 312 | 313 | nearest3.each do |n| 314 | res << res_points[n] 315 | end if nearest3 316 | 317 | res 318 | end 319 | 320 | # Segments intersection ------ 321 | def get_lines_intersection(a1, a2, b1, b2) 322 | s1 = subtract_points(a2, a1) 323 | s2 = subtract_points(b2, b1) 324 | 325 | s = (-s1[1] * (a1[0] - b1[0]) + s1[0] * (a1[1] - b1[1])) / (-s2[0] * s1[1] + s1[0] * s2[1]) 326 | t = (s2[0] * (a1[1] - b1[1]) - s2[1] * (a1[0] - b1[0])) / (-s2[0] * s1[1] + s1[0] * s2[1]) 327 | 328 | if s >= 0 && s <= 1 && t >= 0 && t <= 1 329 | x = a1[0] + (t * s1[0]) 330 | y = a1[1] + (t * s1[1]) 331 | 332 | return [x, y] 333 | else 334 | p "Lines NOT intersect" 335 | return -1 336 | end 337 | end 338 | 339 | def cross_product(p1, p2) 340 | p1[0] * p2[1] - p1[1] * p2[0] 341 | end 342 | 343 | def subtract_points(p1, p2) 344 | x = p1[0] - p2[0] 345 | y = p1[1] - p2[1] 346 | [x, y] 347 | end 348 | # segements intersection ----------- 349 | 350 | def get_centroid(p) 351 | rx = (p[0][0] + p[1][0] + p[2][0]) / 3 352 | ry = (p[0][1] + p[1][1] + p[2][1]) / 3 353 | 354 | [rx, ry] 355 | end 356 | 357 | def dup_indices(arr) 358 | a = arr.dup 359 | a.map{|i| i.sort!} 360 | 361 | dup_indices = Hash.new {|h,k| h[k]=[]} 362 | a.each_index {|i| dup_indices[a[i]] << i unless 1 == a.count(a[i])} 363 | dup_indices 364 | end 365 | end 366 | --------------------------------------------------------------------------------