rfbeam 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -3
- data/.streerc +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +6 -1
- data/lib/rfbeam/cli.rb +35 -80
- data/lib/rfbeam/kld7/cli_formatter.rb +41 -0
- data/lib/rfbeam/kld7/cli_output.rb +127 -0
- data/lib/rfbeam/kld7/constants.rb +155 -31
- data/lib/rfbeam/kld7/radar_messages.rb +30 -23
- data/lib/rfbeam/kld7/serial_connection.rb +1 -1
- data/lib/rfbeam/version.rb +1 -1
- data/lib/rfbeam.rb +2 -2
- metadata +5 -3
- data/lib/rfbeam/kld7/streamer.rb +0 -75
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7edface1b941c801c5842d40d396f8343461271f10a7c381346338e415b074e9
|
4
|
+
data.tar.gz: 02bf778dc66bb874da0323867beeb350d5a69e8bb8aa3986c140a88370ae4d94
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d4cac38cff0bcc30df6501bc42c2cfcc054df8df5d94b486489d1b123d44afce4d5fc49f42e14efbb66813caa12aca4e6d7c67dcdd4c6c9438dfaf698591a51
|
7
|
+
data.tar.gz: ee253b692da85a7504a0de62353fca8d060d1618d9b265f0afd28b32416892fc5754eb57db09af996d8b2fe766a96b84fa6b49d8d7cd4f3675d8ec338cc1e9d0
|
data/.rubocop.yml
CHANGED
@@ -1,11 +1,9 @@
|
|
1
|
-
inherit_from:
|
2
|
-
- node_modules/@prettier/plugin-ruby/rubocop.yml
|
3
|
-
|
4
1
|
AllCops:
|
5
2
|
TargetRubyVersion: 3.2
|
6
3
|
|
7
4
|
Layout/LineLength:
|
8
5
|
Max: 120
|
6
|
+
AutoCorrect: true
|
9
7
|
|
10
8
|
Metrics/ModuleLength:
|
11
9
|
Enabled: false
|
@@ -13,5 +11,8 @@ Metrics/ModuleLength:
|
|
13
11
|
Metrics/MethodLength:
|
14
12
|
Max: 20
|
15
13
|
|
14
|
+
Metrics/ClassLength:
|
15
|
+
Enabled: false
|
16
|
+
|
16
17
|
Documentation:
|
17
18
|
Enabled: false
|
data/.streerc
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rfbeam (0.4.
|
4
|
+
rfbeam (0.4.1)
|
5
5
|
activesupport (~> 6.1.0)
|
6
6
|
rubyserial (~> 0.6.0)
|
7
7
|
thor (~> 1.2.1)
|
@@ -54,9 +54,12 @@ GEM
|
|
54
54
|
strings-ansi (0.2.0)
|
55
55
|
thor (1.2.1)
|
56
56
|
tty-color (0.6.0)
|
57
|
+
tty-command (0.10.1)
|
58
|
+
pastel (~> 0.8)
|
57
59
|
tty-cursor (0.7.1)
|
58
60
|
tty-logger (0.6.0)
|
59
61
|
pastel (~> 0.8)
|
62
|
+
tty-option (0.2.0)
|
60
63
|
tty-screen (0.8.1)
|
61
64
|
tty-spinner (0.9.3)
|
62
65
|
tty-cursor (~> 0.7)
|
@@ -81,7 +84,9 @@ DEPENDENCIES
|
|
81
84
|
rake (~> 13.0)
|
82
85
|
rfbeam!
|
83
86
|
rubocop (~> 1.21)
|
87
|
+
tty-command (~> 0.10.1)
|
84
88
|
tty-logger (~> 0.6.0)
|
89
|
+
tty-option (~> 0.2.0)
|
85
90
|
tty-screen (~> 0.8.1)
|
86
91
|
tty-spinner (~> 0.9.3)
|
87
92
|
tty-table (~> 0.12.0)
|
data/lib/rfbeam/cli.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# rubocop:disable all
|
2
1
|
require 'thor'
|
3
2
|
require 'rfbeam'
|
4
3
|
require 'tty-table'
|
@@ -9,122 +8,78 @@ require 'unicode_plot'
|
|
9
8
|
|
10
9
|
module RfBeam
|
11
10
|
class CLI < Thor
|
12
|
-
|
13
11
|
attr_accessor :radar, :logger
|
14
12
|
|
15
13
|
desc 'list', 'List available radar modules'
|
16
14
|
def list
|
15
|
+
logger = TTY::Logger.new
|
17
16
|
devices = RfBeam.connected
|
18
|
-
|
17
|
+
logger.warning 'No Radar modules found.' unless devices.count.positive?
|
19
18
|
|
20
|
-
table = TTY::Table.new(
|
19
|
+
table = TTY::Table.new(header: %w[id Path Version])
|
21
20
|
|
22
|
-
devices.each.with_index
|
23
|
-
table << ["R#{index}", path, @radar.sw_version]
|
24
|
-
end
|
21
|
+
devices.each.with_index { |path, index| table << ["#{index}", path, radar(index).sw_version] }
|
25
22
|
puts table.render(:ascii)
|
26
23
|
end
|
27
24
|
|
28
25
|
desc 'config <radar_id>', 'Shows the parameter setting for the Radar module'
|
29
26
|
def config(radar_id)
|
30
|
-
|
31
|
-
|
27
|
+
puts radar(radar_id).config
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'reset <radar_id>', 'Shows the parameter setting for the Radar module'
|
31
|
+
def reset(radar_id)
|
32
|
+
@logger.success 'Radar reset to factory defaults' if radar(radar_id).reset
|
32
33
|
end
|
33
34
|
|
34
35
|
desc 'set_param <radar_id> <key> <value>', 'Set radar parameters, see readme for keys'
|
35
36
|
def set_param(radar_id, param, value)
|
36
|
-
return logger.
|
37
|
+
return @logger.warn("Invalid param: '#{param}'") unless RfBeam::K_ld7::RADAR_PARAMETERS.include?(param.to_sym)
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
logger.success
|
39
|
+
r = radar(radar_id)
|
40
|
+
r.send("#{param}=", value.to_i)
|
41
|
+
@logger.success r.formatted_parameter(param.to_sym)
|
41
42
|
end
|
42
43
|
|
43
44
|
desc 'ddat <radar_id>', 'stream any valid detections, stop stream with q and enter'
|
44
|
-
option :stream, type: :boolean, aliases: '-s', desc:
|
45
|
+
option :stream, type: :boolean, aliases: '-s', desc: 'Stream the data from the device'
|
45
46
|
def ddat(radar_id)
|
46
|
-
|
47
|
-
|
48
|
-
if options[:stream]
|
49
|
-
Thread.new { monitor_keypress }
|
50
|
-
spinner = TTY::Spinner.new("[:spinner] :title ", format: :bouncing_ball)
|
51
|
-
loop do
|
52
|
-
break if @stop_streaming
|
53
|
-
spinner.spin
|
54
|
-
data = @radar.ddat
|
55
|
-
spinner.update title: "Searching... #{data}"
|
56
|
-
spinner.success @radar.tdat if data[2] == 1
|
57
|
-
end
|
58
|
-
spinner.stop
|
59
|
-
puts "\nTask Quit."
|
60
|
-
else
|
61
|
-
puts "\n#{@radar.ddat}"
|
62
|
-
end
|
63
|
-
|
47
|
+
cli = RfBeam::KLD7::CliOutput.new(radar_id)
|
48
|
+
cli.display(:ddat, stream: options[:stream])
|
64
49
|
end
|
65
50
|
|
66
51
|
desc 'pdat <radar_id>', 'Display Tracked Targets'
|
67
52
|
def pdat(radar_id)
|
68
|
-
|
69
|
-
|
53
|
+
cli = RfBeam::KLD7::CliOutput.new(radar_id)
|
54
|
+
cli.display(:pdat, stream: options[:stream])
|
70
55
|
end
|
71
56
|
|
72
|
-
desc
|
73
|
-
option :stream, type: :boolean, aliases: '-s', desc:
|
74
|
-
option :
|
57
|
+
desc 'rfft <radar_id>', 'Display the dopplar radar data as a plot'
|
58
|
+
option :stream, type: :boolean, aliases: '-s', desc: 'Stream the data from the device'
|
59
|
+
option :raw, type: :boolean, aliases: '-r', desc: 'Display raw data'
|
75
60
|
def rfft(radar_id)
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
streamer = RfBeam::KLD7::Streamer.new(@radar)
|
80
|
-
streamer.rfft
|
61
|
+
plotter = RfBeam::KLD7::CliOutput.new(radar_id)
|
62
|
+
if options[:raw]
|
63
|
+
print radar(radar_id).rfft
|
81
64
|
else
|
82
|
-
plot
|
83
|
-
p plot.render
|
65
|
+
plotter.plot(:rfft, stream: options[:stream])
|
84
66
|
end
|
85
67
|
end
|
86
68
|
|
69
|
+
desc 'tdat <radar_id>', 'Display tracked target data'
|
70
|
+
def tdat(radar_id)
|
71
|
+
cli = RfBeam::KLD7::CliOutput.new(radar_id)
|
72
|
+
cli.display(:tdat, stream: options[:stream])
|
73
|
+
end
|
74
|
+
|
87
75
|
private
|
88
76
|
|
89
|
-
def
|
77
|
+
def radar(id)
|
90
78
|
devices = RfBeam.connected
|
91
79
|
@logger = TTY::Logger.new
|
92
80
|
return @logger.warning 'No Radar modules found.' unless devices.count.positive?
|
93
81
|
|
94
|
-
|
95
|
-
end
|
96
|
-
|
97
|
-
def plot_data(data)
|
98
|
-
{ x: Array(-128...128), series1: data.shift(256).map { |value| value / 100 }, series2: data.shift(256).map { |value| value.to_i / 100 } }
|
99
|
-
end
|
100
|
-
|
101
|
-
def monitor_keypress
|
102
|
-
loop do
|
103
|
-
key = STDIN.getch
|
104
|
-
if key.downcase == 'q'
|
105
|
-
@stop_streaming = true
|
106
|
-
break
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def rfft_plot(radar)
|
112
|
-
speed = radar.max_speed
|
113
|
-
speed_label = radar.formatted_parameter(:max_speed)
|
114
|
-
xlim = [speed - speed * 2, speed]
|
115
|
-
data = plot_data(radar.rfft)
|
116
|
-
plot = UnicodePlot.lineplot(
|
117
|
-
data[:x],
|
118
|
-
data[:series1],
|
119
|
-
name: 'IF1/2 Averaged',
|
120
|
-
title: 'Raw FFT',
|
121
|
-
height: 25,
|
122
|
-
width: 120,
|
123
|
-
xlabel: "Speed (km/h), #{speed_label}",
|
124
|
-
ylabel: 'Signal (db)', xlim: [-128, 128],
|
125
|
-
ylim: [0, 100])
|
126
|
-
UnicodePlot.lineplot!(plot, data[:x], data[:series2], name: "Threshold")
|
127
|
-
plot
|
82
|
+
RfBeam::K_ld7.new(devices[id.to_i])
|
128
83
|
end
|
129
84
|
end
|
130
|
-
end
|
85
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'tty-table'
|
2
|
+
|
3
|
+
module RfBeam
|
4
|
+
module KLD7
|
5
|
+
class CliFormatter
|
6
|
+
def tdat(data)
|
7
|
+
{ dist: data[2], speed: data[3], angle: data[4], mag: data[5] }
|
8
|
+
end
|
9
|
+
|
10
|
+
def pdat_table(data)
|
11
|
+
table = TTY::Table.new header: ['index', 'dist (M)', 'speed (Km/h)', 'angle (°)', 'mag (db)']
|
12
|
+
count = data[1] / 8
|
13
|
+
data.shift(2)
|
14
|
+
count.times.with_index do |index|
|
15
|
+
values = data.shift(4).map { |value| value.to_f / 100.0 }
|
16
|
+
table << [index, values].flatten
|
17
|
+
end
|
18
|
+
table
|
19
|
+
end
|
20
|
+
|
21
|
+
def ddat(data)
|
22
|
+
if data[2] == 1
|
23
|
+
labels = ['Detection', 'Micro Detection', 'Angle', 'Direction', 'Range', 'Speed']
|
24
|
+
labels
|
25
|
+
.map
|
26
|
+
.with_index { |label, index| "#{label}: #{DETECTION_FLAGS[to_symbol(label)][data[index + 2]]}" }
|
27
|
+
.join("\n")
|
28
|
+
else
|
29
|
+
'DDAT: No Detection'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def to_symbol(string)
|
36
|
+
modified_string = string.gsub(' ', '_').downcase
|
37
|
+
modified_string.to_sym
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'unicode_plot'
|
2
|
+
require 'io/console'
|
3
|
+
require 'stringio'
|
4
|
+
require 'tty-screen'
|
5
|
+
require 'tty-logger'
|
6
|
+
|
7
|
+
module RfBeam
|
8
|
+
module KLD7
|
9
|
+
class CliOutput
|
10
|
+
attr_reader :radar
|
11
|
+
|
12
|
+
def initialize(radar_id)
|
13
|
+
devices = RfBeam.connected
|
14
|
+
return TTY::Logger.new.warning 'No Radar modules found.' unless devices.count.positive?
|
15
|
+
|
16
|
+
@radar = RfBeam::K_ld7.new(devices[radar_id.to_i])
|
17
|
+
end
|
18
|
+
|
19
|
+
def display(type, stream: false)
|
20
|
+
send("display_#{type}", stream)
|
21
|
+
end
|
22
|
+
|
23
|
+
def plot(type, stream: false)
|
24
|
+
stream ? stream_plot(type) : display_plot(type)
|
25
|
+
end
|
26
|
+
|
27
|
+
def display_plot(type)
|
28
|
+
out = StringIO.new
|
29
|
+
def out.tty?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
out.truncate(0)
|
33
|
+
|
34
|
+
plot = send("#{type}_plot")
|
35
|
+
plot.render(out)
|
36
|
+
|
37
|
+
lines = out.string.lines
|
38
|
+
lines.each { |line| $stdout.print "\r#{line}" }
|
39
|
+
$stdout.print "\e[0J"
|
40
|
+
$stdout.flush
|
41
|
+
|
42
|
+
if @streaming
|
43
|
+
n = lines.count
|
44
|
+
$stdout.print "\e[#{n}F"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def stream_plot(type)
|
49
|
+
@streaming = true
|
50
|
+
Thread.new { monitor_keypress }
|
51
|
+
|
52
|
+
loop do
|
53
|
+
display_plot(type)
|
54
|
+
break unless @streaming
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def monitor_keypress
|
61
|
+
loop do
|
62
|
+
key = STDIN.getch
|
63
|
+
if key.downcase == 'q'
|
64
|
+
@streaming = false
|
65
|
+
break
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def rfft_plot_data(data)
|
71
|
+
{
|
72
|
+
x: Array(-128...128),
|
73
|
+
series1: data.shift(256).map { |value| value / 100 },
|
74
|
+
series2: data.shift(256).map { |value| value.to_i / 100 }
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def display_ddat(stream)
|
79
|
+
if stream
|
80
|
+
@streaming = true
|
81
|
+
Thread.new { monitor_keypress }
|
82
|
+
spinner = TTY::Spinner.new('[:spinner] :title ', format: :bouncing_ball)
|
83
|
+
logger = TTY::Logger.new
|
84
|
+
loop do
|
85
|
+
break unless @streaming
|
86
|
+
spinner.spin
|
87
|
+
data = @radar.ddat
|
88
|
+
spinner.update title: "Searching... #{data[:detection_str]}"
|
89
|
+
logger.success "#{@radar.tdat}" if data[:detection]
|
90
|
+
end
|
91
|
+
else
|
92
|
+
puts RfBeam::KLD7::CliFormatter.new.ddat(@radar.ddat)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def display_tdat(stream)
|
97
|
+
puts RfBeam::KLD7::CliFormatter.new.tdat(@radar.tdat)
|
98
|
+
end
|
99
|
+
|
100
|
+
def display_pdat(stream)
|
101
|
+
table = RfBeam::KLD7::CliFormatter.new.pdat_table(@radar.pdat)
|
102
|
+
puts "\n Detected Raw Targets"
|
103
|
+
puts table.render(:unicode, alignment: :center)
|
104
|
+
end
|
105
|
+
|
106
|
+
def rfft_plot
|
107
|
+
width = TTY::Screen.width * 0.65
|
108
|
+
data = rfft_plot_data(@radar.rfft)
|
109
|
+
plot =
|
110
|
+
UnicodePlot.lineplot(
|
111
|
+
data[:x],
|
112
|
+
data[:series1],
|
113
|
+
name: 'IF1/2 Averaged',
|
114
|
+
title: 'Raw FFT',
|
115
|
+
height: 25,
|
116
|
+
width: width,
|
117
|
+
xlabel: 'Speed (km/h)',
|
118
|
+
ylabel: 'Signal (db)',
|
119
|
+
xlim: [-128, 128],
|
120
|
+
ylim: [0, 100]
|
121
|
+
)
|
122
|
+
UnicodePlot.lineplot!(plot, data[:x], data[:series2], name: 'Threshold')
|
123
|
+
plot
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -14,52 +14,176 @@ module RfBeam
|
|
14
14
|
6 => 'Timeout error'
|
15
15
|
}.freeze
|
16
16
|
|
17
|
-
#
|
18
|
-
|
17
|
+
# Delays are determined empirically and may need adjusting with baude rate
|
18
|
+
RESPONSE_DELAY = 0.05
|
19
|
+
MEASUREMENT_DELAY = 0.15
|
19
20
|
|
20
21
|
# 'GNFD' command types
|
21
22
|
FRAME_DATA_TYPES = { disabled: 0x00, radc: 0x01, rfft: 0x02, pdat: 0x04, tdat: 0x08, ddat: 0x10, done: 0x20 }.freeze
|
22
23
|
|
23
24
|
# The angle, direction, range and speed flags are only valid if the detection flag is 1.
|
24
25
|
DETECTION_FLAGS = {
|
25
|
-
detection: ['No
|
26
|
-
micro_detection: ['No
|
26
|
+
detection: ['No', 'Yes'],
|
27
|
+
micro_detection: ['No', 'Yes'],
|
27
28
|
angle: %w[Left Right],
|
28
29
|
direction: %w[Receding Approaching],
|
29
30
|
range: %w[Far Near],
|
30
31
|
speed: %w[Low High]
|
31
32
|
}.freeze
|
32
33
|
|
33
|
-
Param =
|
34
|
-
|
35
|
-
|
34
|
+
Param =
|
35
|
+
Data.define(:name, :grps_index, :description, :default, :units, :values) do |_param|
|
36
|
+
def initialize(name:, grps_index:, description: nil, default: nil, units: nil, values: [])
|
37
|
+
super(name:, grps_index:, description:, default:, units:, values:)
|
38
|
+
end
|
36
39
|
end
|
37
|
-
end
|
38
40
|
|
39
41
|
RADAR_PARAMETERS = {
|
40
42
|
sw_version: Param.new(name: 'Software Version', grps_index: 2, default: 'K-LD7_APP-RFB-XXXX'),
|
41
|
-
base_frequency:
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
43
|
+
base_frequency:
|
44
|
+
Param.new(
|
45
|
+
name: 'Base Frequency',
|
46
|
+
grps_index: 3,
|
47
|
+
description: '0 = Low, 1 = Middle, 2 = High',
|
48
|
+
default: 1,
|
49
|
+
values: %w[Low Middle High]
|
50
|
+
),
|
51
|
+
max_speed:
|
52
|
+
Param.new(
|
53
|
+
name: 'Maximum Speed',
|
54
|
+
grps_index: 4,
|
55
|
+
description: '0 = 12km/h, 1 = 25km/h, 2 = 50km/h, 3 = 100km/h',
|
56
|
+
default: 1,
|
57
|
+
units: 'km/h',
|
58
|
+
values: %w[12.5 25 50 100]
|
59
|
+
),
|
60
|
+
max_range:
|
61
|
+
Param.new(
|
62
|
+
name: 'Maximum Range',
|
63
|
+
grps_index: 5,
|
64
|
+
description: '0 = 5m, 1 = 10m, 2 = 30m, 3 = 100m',
|
65
|
+
default: 1,
|
66
|
+
values: %w[5m 10m 30m 100m]
|
67
|
+
),
|
68
|
+
threshold_offset:
|
69
|
+
Param.new(name: 'Threshold Offset', grps_index: 6, description: '10db - 60db', default: 30, units: 'db'),
|
70
|
+
tracking_filter:
|
71
|
+
Param.new(
|
72
|
+
name: 'Tracking Filter Type',
|
73
|
+
grps_index: 7,
|
74
|
+
description: '0 = Standard, 2 = Fast Detection, 3 = Long Visibility',
|
75
|
+
default: 0,
|
76
|
+
values: ['standard', 'Fast Detection', 'Long Visibility']
|
77
|
+
),
|
78
|
+
vibration_suppression:
|
79
|
+
Param.new(
|
80
|
+
name: 'Vibration Suppression',
|
81
|
+
grps_index: 8,
|
82
|
+
description: '0-16, 0 = No Suppression, 16 = High Suppression',
|
83
|
+
default: 2
|
84
|
+
),
|
85
|
+
min_detection_distance:
|
86
|
+
Param.new(
|
87
|
+
name: 'Minimum Detection Distance',
|
88
|
+
grps_index: 9,
|
89
|
+
description: '0 - 100% of range setting',
|
90
|
+
default: 0,
|
91
|
+
units: '%'
|
92
|
+
),
|
93
|
+
max_detection_distance:
|
94
|
+
Param.new(
|
95
|
+
name: 'Maximum Detection Distance',
|
96
|
+
grps_index: 10,
|
97
|
+
description: '0 - 100% of range setting',
|
98
|
+
default: 50,
|
99
|
+
units: '%'
|
100
|
+
),
|
101
|
+
min_detection_angle:
|
102
|
+
Param.new(name: 'Minimum Detection Angle', grps_index: 11, description: '-90° - 90°', default: -90, units: '°'),
|
103
|
+
max_detection_angle:
|
104
|
+
Param.new(name: 'Maximum Detection Angle', grps_index: 12, description: '-90° - 90°', default: 90, units: '°'),
|
105
|
+
min_detection_speed:
|
106
|
+
Param.new(
|
107
|
+
name: 'Minimum Detection Speed',
|
108
|
+
grps_index: 13,
|
109
|
+
description: '0 - 100% of speed setting',
|
110
|
+
default: 0,
|
111
|
+
units: '%'
|
112
|
+
),
|
113
|
+
max_detection_speed:
|
114
|
+
Param.new(
|
115
|
+
name: 'Maximum Detection Speed',
|
116
|
+
grps_index: 14,
|
117
|
+
description: '0 - 100% of speed setting',
|
118
|
+
default: 100,
|
119
|
+
units: '%'
|
120
|
+
),
|
121
|
+
detection_direction:
|
122
|
+
Param.new(
|
123
|
+
name: 'Detection Direction',
|
124
|
+
grps_index: 15,
|
125
|
+
description: '0 = Receding, 1 = Approaching, 2 = Both',
|
126
|
+
default: 2,
|
127
|
+
values: %w[Receding Approaching Both]
|
128
|
+
),
|
129
|
+
range_threshold:
|
130
|
+
Param.new(
|
131
|
+
name: 'Range Threshold',
|
132
|
+
grps_index: 16,
|
133
|
+
description: '0 - 100% of range setting',
|
134
|
+
default: 10,
|
135
|
+
units: '%'
|
136
|
+
),
|
137
|
+
angle_threshold:
|
138
|
+
Param.new(name: 'Angle Threshold', grps_index: 17, description: '-90° - 90°', default: 0, units: '°'),
|
139
|
+
speed_threshold:
|
140
|
+
Param.new(
|
141
|
+
name: 'Speed Threshold',
|
142
|
+
grps_index: 18,
|
143
|
+
description: '0 - 100% of speed setting',
|
144
|
+
default: 50,
|
145
|
+
units: '%'
|
146
|
+
),
|
147
|
+
digital_output1:
|
148
|
+
Param.new(
|
149
|
+
name: 'Digital Output 1',
|
150
|
+
grps_index: 19,
|
151
|
+
description: '0 = Direction, 1 = Angle, 2 = Range, 3 = Speed, 4 = Micro Detection',
|
152
|
+
default: 0,
|
153
|
+
values: %w[Direction Angle Range Speed Micro]
|
154
|
+
),
|
155
|
+
digital_output2:
|
156
|
+
Param.new(
|
157
|
+
name: 'Digital Output 2',
|
158
|
+
grps_index: 20,
|
159
|
+
description: '0 = Direction, 1 = Angle, 2 = Range, 3 = Speed, 4 = Micro Detection',
|
160
|
+
default: 1,
|
161
|
+
values: %w[Direction Angle Range Speed Micro]
|
162
|
+
),
|
163
|
+
digital_output3:
|
164
|
+
Param.new(
|
165
|
+
name: 'Digital Output 3',
|
166
|
+
grps_index: 21,
|
167
|
+
description: '0 = Direction, 1 = Angle, 2 = Range, 3 = Speed, 4 = Micro Detection',
|
168
|
+
default: 2,
|
169
|
+
values: %w[Direction Angle Range Speed Micro]
|
170
|
+
),
|
171
|
+
hold_time: Param.new(name: 'Hold Time', grps_index: 22, description: '1 - 7200s', default: 1, units: 's'),
|
172
|
+
micro_detection_retrigger:
|
173
|
+
Param.new(
|
174
|
+
name: 'Micro Detection Trigger',
|
175
|
+
grps_index: 23,
|
176
|
+
description: '0 = Off, 1 = Retrigger',
|
177
|
+
default: 0,
|
178
|
+
values: %w[Off Retrigger]
|
179
|
+
),
|
180
|
+
micro_detection_sensativity:
|
181
|
+
Param.new(
|
182
|
+
name: 'Micro Detection Sensativity',
|
183
|
+
grps_index: 24,
|
184
|
+
description: '0 - 9, 0 = Min, 9 = Max',
|
185
|
+
default: 4
|
186
|
+
)
|
187
|
+
}.freeze
|
64
188
|
end
|
65
189
|
end
|
@@ -9,15 +9,27 @@ module RfBeam
|
|
9
9
|
|
10
10
|
def rfft
|
11
11
|
request_frame_data(:rfft)
|
12
|
+
sleep MEASUREMENT_DELAY
|
13
|
+
data = read(1032).unpack('a4LS256S256')
|
14
|
+
header, length = data.shift(2)
|
15
|
+
raise Error, "RFFT header response, header=#{header}" unless header == 'RFFT'
|
16
|
+
raise Error, "RFFT payload length, length=#{length}" unless length == 1024
|
12
17
|
|
13
|
-
|
14
|
-
|
15
|
-
|
18
|
+
data
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset
|
22
|
+
command = ['RFSE', 0]
|
23
|
+
write command.pack('a4L')
|
24
|
+
check_response
|
16
25
|
end
|
17
|
-
|
26
|
+
alias rfse reset
|
27
|
+
|
18
28
|
def pdat(formatted: false)
|
19
|
-
request_frame_data(:pdat)
|
29
|
+
request_frame_data(:pdat)
|
20
30
|
resp = read(102).unpack('a4LSssSSssSSssSSssSSssSSssSSssSSssSSssSSssS')
|
31
|
+
raise Error, "PDAT response = #{resp[0]}" unless resp[0] == 'PDAT'
|
32
|
+
|
21
33
|
return resp unless formatted
|
22
34
|
|
23
35
|
target_count = resp[1].to_i / 8
|
@@ -32,26 +44,23 @@ module RfBeam
|
|
32
44
|
|
33
45
|
def tdat
|
34
46
|
request_frame_data(:tdat)
|
47
|
+
sleep MEASUREMENT_DELAY
|
35
48
|
|
36
|
-
sleep 0.1
|
37
49
|
resp = read(16).unpack('a4LSssS')
|
38
|
-
|
50
|
+
raise Error, "TDAT response = #{resp[0]}" unless resp[0] == 'TDAT'
|
51
|
+
|
52
|
+
resp
|
39
53
|
end
|
40
54
|
|
41
55
|
def ddat
|
42
56
|
request_frame_data(:ddat)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
angle: DETECTION_FLAGS[:angle][array[4]],
|
49
|
-
direction: DETECTION_FLAGS[:direction][array[5]],
|
50
|
-
range: DETECTION_FLAGS[:range][array[6]],
|
51
|
-
speed: DETECTION_FLAGS[:speed][array[7]]
|
52
|
-
}
|
57
|
+
sleep MEASUREMENT_DELAY
|
58
|
+
|
59
|
+
resp = read(14).unpack('a4LC6')
|
60
|
+
raise Error, "DDAT response = #{resp[0]}" unless resp[0] == 'DDAT'
|
61
|
+
resp
|
53
62
|
end
|
54
|
-
|
63
|
+
|
55
64
|
# Get the radar parameter structure
|
56
65
|
def grps
|
57
66
|
command = ['GRPS', 0]
|
@@ -63,12 +72,10 @@ module RfBeam
|
|
63
72
|
def config
|
64
73
|
data = grps
|
65
74
|
output = "\n"
|
66
|
-
RADAR_PARAMETERS.keys.each
|
67
|
-
output << formatted_parameter(key, data[RADAR_PARAMETERS[key].grps_index])
|
68
|
-
end
|
75
|
+
RADAR_PARAMETERS.keys.each { |key| output << formatted_parameter(key, data[RADAR_PARAMETERS[key].grps_index]) }
|
69
76
|
output
|
70
77
|
end
|
71
|
-
|
78
|
+
|
72
79
|
def formatted_parameter(param, value = nil)
|
73
80
|
param = RADAR_PARAMETERS[param]
|
74
81
|
if value.nil?
|
@@ -82,7 +89,7 @@ module RfBeam
|
|
82
89
|
private
|
83
90
|
|
84
91
|
def format_raw_target_data(array)
|
85
|
-
|
92
|
+
{ dist: array.shift, speed: array.shift, angle: array.shift, mag: array.shift }
|
86
93
|
end
|
87
94
|
|
88
95
|
def request_frame_data(type)
|
data/lib/rfbeam/version.rb
CHANGED
data/lib/rfbeam.rb
CHANGED
@@ -4,11 +4,11 @@ require 'rfbeam/kld7/radar_parameters'
|
|
4
4
|
require 'rfbeam/kld7/radar_messages'
|
5
5
|
require 'rfbeam/kld7/serial_connection'
|
6
6
|
require 'rfbeam/kld7/constants'
|
7
|
-
require 'rfbeam/kld7/
|
7
|
+
require 'rfbeam/kld7/cli_output'
|
8
|
+
require 'rfbeam/kld7/cli_formatter'
|
8
9
|
require 'rfbeam/version'
|
9
10
|
require 'rfbeam/cli'
|
10
11
|
|
11
|
-
|
12
12
|
module RfBeam
|
13
13
|
class Error < StandardError
|
14
14
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rfbeam
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Carruthers
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -62,6 +62,7 @@ extra_rdoc_files: []
|
|
62
62
|
files:
|
63
63
|
- ".DS_Store"
|
64
64
|
- ".rubocop.yml"
|
65
|
+
- ".streerc"
|
65
66
|
- ".tool-versions"
|
66
67
|
- CHANGELOG.md
|
67
68
|
- Gemfile
|
@@ -72,11 +73,12 @@ files:
|
|
72
73
|
- exe/rfbeam
|
73
74
|
- lib/rfbeam.rb
|
74
75
|
- lib/rfbeam/cli.rb
|
76
|
+
- lib/rfbeam/kld7/cli_formatter.rb
|
77
|
+
- lib/rfbeam/kld7/cli_output.rb
|
75
78
|
- lib/rfbeam/kld7/constants.rb
|
76
79
|
- lib/rfbeam/kld7/radar_messages.rb
|
77
80
|
- lib/rfbeam/kld7/radar_parameters.rb
|
78
81
|
- lib/rfbeam/kld7/serial_connection.rb
|
79
|
-
- lib/rfbeam/kld7/streamer.rb
|
80
82
|
- lib/rfbeam/version.rb
|
81
83
|
- node_modules/.bin/prettier
|
82
84
|
- node_modules/.yarn-integrity
|
data/lib/rfbeam/kld7/streamer.rb
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
# rubocop:disable all
|
2
|
-
require 'unicode_plot'
|
3
|
-
require 'io/console'
|
4
|
-
require "stringio"
|
5
|
-
require 'tty-screen'
|
6
|
-
|
7
|
-
module RfBeam
|
8
|
-
module KLD7
|
9
|
-
class Streamer
|
10
|
-
attr_accessor :radar
|
11
|
-
|
12
|
-
def initialize(radar)
|
13
|
-
@radar = radar
|
14
|
-
end
|
15
|
-
|
16
|
-
def monitor_keypress
|
17
|
-
loop do
|
18
|
-
key = STDIN.getch
|
19
|
-
if key.downcase == "q"
|
20
|
-
@stop_streaming = true
|
21
|
-
break
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def rfft
|
27
|
-
out = StringIO.new
|
28
|
-
def out.tty?
|
29
|
-
true
|
30
|
-
end
|
31
|
-
|
32
|
-
Thread.new { monitor_keypress }
|
33
|
-
|
34
|
-
loop do
|
35
|
-
out.truncate(0)
|
36
|
-
|
37
|
-
plot = rfft_plot(@radar)
|
38
|
-
plot.render(out)
|
39
|
-
|
40
|
-
lines = out.string.lines
|
41
|
-
lines.each { |line| $stdout.print "\r#{line}" }
|
42
|
-
$stdout.print "\e[0J"
|
43
|
-
$stdout.flush
|
44
|
-
break if @stop_streaming
|
45
|
-
|
46
|
-
n = lines.count
|
47
|
-
$stdout.print "\e[#{n}F"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
def plot_data(data)
|
54
|
-
{ x: Array(-128...128), series1: data.shift(256).map { |value| value / 100 }, series2: data.shift(256).map { |value| value.to_i / 100 } }
|
55
|
-
end
|
56
|
-
|
57
|
-
def rfft_plot(radar)
|
58
|
-
width = TTY::Screen.width * 0.65
|
59
|
-
data = plot_data(radar.rfft)
|
60
|
-
plot = UnicodePlot.lineplot(
|
61
|
-
data[:x],
|
62
|
-
data[:series1],
|
63
|
-
name: 'IF1/2 Averaged',
|
64
|
-
title: 'Raw FFT',
|
65
|
-
height: 25,
|
66
|
-
width: width,
|
67
|
-
xlabel: "Speed (km/h)",
|
68
|
-
ylabel: 'Signal (db)', xlim: [-128, 128],
|
69
|
-
ylim: [0, 100])
|
70
|
-
UnicodePlot.lineplot!(plot, data[:x], data[:series2], name: "Threshold")
|
71
|
-
plot
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|