rfbeam 0.3.5 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 139acc0d00964189f2eadbba6ed9c7b0e3f4b6c5104a757b2c1ab335d0e30c22
4
- data.tar.gz: 86370312525afb17e493aa4b561a68b0f57dc48122f3ecd5f389c421c512cbe8
3
+ metadata.gz: 7edface1b941c801c5842d40d396f8343461271f10a7c381346338e415b074e9
4
+ data.tar.gz: 02bf778dc66bb874da0323867beeb350d5a69e8bb8aa3986c140a88370ae4d94
5
5
  SHA512:
6
- metadata.gz: 76e49986de83640754506d935a5a46b0c74548fe96ac7fec03bff66efe048937eaf1dd1175ea25658e3cbdc3705f6f4744df56c969f584e9f3763629d37fbe0e
7
- data.tar.gz: 0d032f953bca0658fb4edec3544925bd1fe675b759b1a9759a8cfbffea0abd76f7e1690bb9ae87df5d700a102d4c713f5bb858ca7a8b855d039c3ea448e94428
6
+ metadata.gz: 0d4cac38cff0bcc30df6501bc42c2cfcc054df8df5d94b486489d1b123d44afce4d5fc49f42e14efbb66813caa12aca4e6d7c67dcdd4c6c9438dfaf698591a51
7
+ data.tar.gz: ee253b692da85a7504a0de62353fca8d060d1618d9b265f0afd28b32416892fc5754eb57db09af996d8b2fe766a96b84fa6b49d8d7cd4f3675d8ec338cc1e9d0
data/.rubocop.yml CHANGED
@@ -1,17 +1,18 @@
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
12
10
 
13
11
  Metrics/MethodLength:
14
- Max: 15
12
+ Max: 20
13
+
14
+ Metrics/ClassLength:
15
+ Enabled: false
15
16
 
16
17
  Documentation:
17
18
  Enabled: false
data/.streerc ADDED
@@ -0,0 +1,2 @@
1
+ --print-width=120
2
+ --plugins=plugin/single_quotes
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## [0.4.0] - 2023-4-4
2
+
3
+ - rough CLI implementation
4
+ - Streaming rfft data, with plot output
5
+
1
6
  ## [0.3.5] - 2023-4-1
2
7
 
3
8
  - Updated Radar parameter accessors
data/Gemfile CHANGED
@@ -10,3 +10,17 @@ gem "rake", "~> 13.0"
10
10
  gem "minitest", "~> 5.0"
11
11
 
12
12
  gem "rubocop", "~> 1.21"
13
+
14
+ gem "tty-table", "~> 0.12.0"
15
+
16
+ gem "tty-spinner", "~> 0.9.3"
17
+
18
+ gem "unicode_plot", "~> 0.0.5"
19
+
20
+ gem "tty-logger", "~> 0.6.0"
21
+
22
+ gem "tty-screen", "~> 0.8.1"
23
+
24
+ gem "tty-option", "~> 0.2.0"
25
+
26
+ gem "tty-command", "~> 0.10.1"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rfbeam (0.3.5)
4
+ rfbeam (0.4.1)
5
5
  activesupport (~> 6.1.0)
6
6
  rubyserial (~> 0.6.0)
7
7
  thor (~> 1.2.1)
@@ -17,6 +17,7 @@ GEM
17
17
  zeitwerk (~> 2.3)
18
18
  ast (2.4.2)
19
19
  concurrent-ruby (1.1.10)
20
+ enumerable-statistics (2.0.7)
20
21
  ffi (1.15.5)
21
22
  i18n (1.12.0)
22
23
  concurrent-ruby (~> 1.0)
@@ -25,6 +26,8 @@ GEM
25
26
  parallel (1.22.1)
26
27
  parser (3.1.3.0)
27
28
  ast (~> 2.4.1)
29
+ pastel (0.8.0)
30
+ tty-color (~> 0.5)
28
31
  rainbow (3.1.1)
29
32
  rake (13.0.6)
30
33
  regexp_parser (2.6.1)
@@ -44,10 +47,32 @@ GEM
44
47
  ruby-progressbar (1.11.0)
45
48
  rubyserial (0.6.0)
46
49
  ffi (~> 1.9, >= 1.9.3)
50
+ strings (0.2.1)
51
+ strings-ansi (~> 0.2)
52
+ unicode-display_width (>= 1.5, < 3.0)
53
+ unicode_utils (~> 1.4)
54
+ strings-ansi (0.2.0)
47
55
  thor (1.2.1)
56
+ tty-color (0.6.0)
57
+ tty-command (0.10.1)
58
+ pastel (~> 0.8)
59
+ tty-cursor (0.7.1)
60
+ tty-logger (0.6.0)
61
+ pastel (~> 0.8)
62
+ tty-option (0.2.0)
63
+ tty-screen (0.8.1)
64
+ tty-spinner (0.9.3)
65
+ tty-cursor (~> 0.7)
66
+ tty-table (0.12.0)
67
+ pastel (~> 0.8)
68
+ strings (~> 0.2.0)
69
+ tty-screen (~> 0.8)
48
70
  tzinfo (2.0.5)
49
71
  concurrent-ruby (~> 1.0)
50
72
  unicode-display_width (2.3.0)
73
+ unicode_plot (0.0.5)
74
+ enumerable-statistics (>= 2.0.1)
75
+ unicode_utils (1.4.0)
51
76
  zeitwerk (2.6.6)
52
77
 
53
78
  PLATFORMS
@@ -59,6 +84,13 @@ DEPENDENCIES
59
84
  rake (~> 13.0)
60
85
  rfbeam!
61
86
  rubocop (~> 1.21)
87
+ tty-command (~> 0.10.1)
88
+ tty-logger (~> 0.6.0)
89
+ tty-option (~> 0.2.0)
90
+ tty-screen (~> 0.8.1)
91
+ tty-spinner (~> 0.9.3)
92
+ tty-table (~> 0.12.0)
93
+ unicode_plot (~> 0.0.5)
62
94
 
63
95
  BUNDLED WITH
64
96
  2.4.10
data/exe/rfbeam ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rfbeam'
4
+
5
+ RfBeam::CLI.start(ARGV)
data/lib/rfbeam/cli.rb ADDED
@@ -0,0 +1,85 @@
1
+ require 'thor'
2
+ require 'rfbeam'
3
+ require 'tty-table'
4
+ require 'tty-logger'
5
+ require 'tty-spinner'
6
+ require 'io/console'
7
+ require 'unicode_plot'
8
+
9
+ module RfBeam
10
+ class CLI < Thor
11
+ attr_accessor :radar, :logger
12
+
13
+ desc 'list', 'List available radar modules'
14
+ def list
15
+ logger = TTY::Logger.new
16
+ devices = RfBeam.connected
17
+ logger.warning 'No Radar modules found.' unless devices.count.positive?
18
+
19
+ table = TTY::Table.new(header: %w[id Path Version])
20
+
21
+ devices.each.with_index { |path, index| table << ["#{index}", path, radar(index).sw_version] }
22
+ puts table.render(:ascii)
23
+ end
24
+
25
+ desc 'config <radar_id>', 'Shows the parameter setting for the Radar module'
26
+ def config(radar_id)
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
33
+ end
34
+
35
+ desc 'set_param <radar_id> <key> <value>', 'Set radar parameters, see readme for keys'
36
+ def set_param(radar_id, param, value)
37
+ return @logger.warn("Invalid param: '#{param}'") unless RfBeam::K_ld7::RADAR_PARAMETERS.include?(param.to_sym)
38
+
39
+ r = radar(radar_id)
40
+ r.send("#{param}=", value.to_i)
41
+ @logger.success r.formatted_parameter(param.to_sym)
42
+ end
43
+
44
+ desc 'ddat <radar_id>', 'stream any valid detections, stop stream with q and enter'
45
+ option :stream, type: :boolean, aliases: '-s', desc: 'Stream the data from the device'
46
+ def ddat(radar_id)
47
+ cli = RfBeam::KLD7::CliOutput.new(radar_id)
48
+ cli.display(:ddat, stream: options[:stream])
49
+ end
50
+
51
+ desc 'pdat <radar_id>', 'Display Tracked Targets'
52
+ def pdat(radar_id)
53
+ cli = RfBeam::KLD7::CliOutput.new(radar_id)
54
+ cli.display(:pdat, stream: options[:stream])
55
+ end
56
+
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'
60
+ def rfft(radar_id)
61
+ plotter = RfBeam::KLD7::CliOutput.new(radar_id)
62
+ if options[:raw]
63
+ print radar(radar_id).rfft
64
+ else
65
+ plotter.plot(:rfft, stream: options[:stream])
66
+ end
67
+ end
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
+
75
+ private
76
+
77
+ def radar(id)
78
+ devices = RfBeam.connected
79
+ @logger = TTY::Logger.new
80
+ return @logger.warning 'No Radar modules found.' unless devices.count.positive?
81
+
82
+ RfBeam::K_ld7.new(devices[id.to_i])
83
+ end
84
+ 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
- # The response delay was determined empirically and may need adjusting with baude rate
18
- RESP_DELAY = 0.1
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 Detection', 'Detection'],
26
- micro_detection: ['No Detection', 'Detection'],
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
- Param = Data.define(:name, :grps_index, :description, :default, :units, :values) do |_param|
34
- def initialize(name:, grps_index:, description: nil, default: nil, units: nil, values: [])
35
- super(name:, grps_index:, description:, default:, units:, values:)
33
+
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: Param.new( name: 'Base Frequency', grps_index: 3, description: '0 = Low, 1 = Middle, 2 = High', default: 1, values: ['Low', 'Middle', 'High'] ),
42
- max_speed: Param.new( name: 'Maximum Speed', grps_index: 4, description: '0 = 12km/h, 1 = 25km/h, 2 = 50km/h, 3 = 100km/h', default: 1, units: 'km/h', values: ['12.5', '25', '50', '100'] ),
43
- max_range: Param.new( name: 'Maximum Range', grps_index: 5, description: '0 = 5m, 1 = 10m, 2 = 30m, 3 = 100m', default: 1, values: %w[5m 10m 30m 100m] ),
44
- threshhold_offset: Param.new( name: 'Threshold Offset', grps_index: 6, description: '10db - 60db', default: 30, units: 'db' ),
45
- tracking_filter: Param.new( name: 'Tracking Filter Type', grps_index: 7, description: '0 = Standard, 2 = Fast Detection, 3 = Long Visibility', default: 0, values: ['standard', 'Fast Detection', 'Long Visibility'] ),
46
- vibration_suppression: Param.new( name: 'Vibration Suppression', grps_index: 8, description: '0-16, 0 = No Suppression, 16 = High Suppression', default: 2 ),
47
- min_detection_distance: Param.new( name: 'Minimum Detection Distance', grps_index: 9, description: '0 - 100% of range setting', default: 0, units: '%' ),
48
- max_detection_distance: Param.new( name: 'Maximum Detection Distance', grps_index: 10, description: '0 - 100% of range setting', default: 50, units: '%' ),
49
- min_detection_angle: Param.new( name: 'Minimum Detection Angle', grps_index: 11, description: '-90° - 90°', default: -90, units: '°' ),
50
- max_detection_angle: Param.new( name: 'Maximum Detection Angle', grps_index: 12, description: '-90° - 90°', default: 90, units: '°' ),
51
- min_detection_speed: Param.new( name: 'Minimum Detection Speed', grps_index: 13, description: '0 - 100% of speed setting', default: 0, units: '%' ),
52
- max_detection_speed: Param.new( name: 'Maximum Detection Speed', grps_index: 14, description: '0 - 100% of speed setting', default: 100, units: '%' ),
53
- detection_direction: Param.new( name: 'Detection Direction', grps_index: 15, description: '0 = Receding, 1 = Approaching, 2 = Both', default: 2, values: %w[Receding Approaching Both] ),
54
- range_threshold: Param.new( name: 'Range Threshold', grps_index: 16, description: '0 - 100% of range setting', default: 10, units: '%' ),
55
- angle_threshold: Param.new( name: 'Angle Threshold', grps_index: 17, description: '-90° - 90°', default: 0, units: '°' ),
56
- speed_threshold: Param.new( name: 'Speed Threshold', grps_index: 18, description: '0 - 100% of speed setting', default: 50, units: '%' ),
57
- digital_output1: Param.new( name: 'Digital Output 1', grps_index: 19, description: '0 = Direction, 1 = Angle, 2 = Range, 3 = Speed, 4 = Micro Detection', default: 0, values: %w[Direction Angle Range Speed Micro] ),
58
- digital_output2: Param.new( name: 'Digital Output 2', grps_index: 20, description: '0 = Direction, 1 = Angle, 2 = Range, 3 = Speed, 4 = Micro Detection', default: 1, values: %w[Direction Angle Range Speed Micro] ),
59
- digital_output3: Param.new( name: 'Digital Output 3', grps_index: 21, description: '0 = Direction, 1 = Angle, 2 = Range, 3 = Speed, 4 = Micro Detection', default: 2, values: %w[Direction Angle Range Speed Micro] ),
60
- hold_time: Param.new( name: 'Hold Time', grps_index: 22, description: '1 - 7200s', default: 1, units: 's' ),
61
- micro_detection_retrigger: Param.new( name: 'Micro Detection Trigger', grps_index: 23, description: '0 = Off, 1 = Retrigger', default: 0, values: %w[Off Retrigger] ),
62
- micro_detection_sensativity: Param.new( name: 'Micro Detection Sensativity', grps_index: 24, description: '0 - 9, 0 = Min, 9 = Max', default: 4 )
63
- }.freeze
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
@@ -1,13 +1,35 @@
1
+ require 'csv'
2
+
1
3
  module RfBeam
2
4
  module KLD7
3
5
  def detection?
4
6
  data = ddat
5
7
  (data[2] == 1)
6
8
  end
7
-
9
+
10
+ def rfft
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
17
+
18
+ data
19
+ end
20
+
21
+ def reset
22
+ command = ['RFSE', 0]
23
+ write command.pack('a4L')
24
+ check_response
25
+ end
26
+ alias rfse reset
27
+
8
28
  def pdat(formatted: false)
9
- request_frame_data(:pdat)
29
+ request_frame_data(:pdat)
10
30
  resp = read(102).unpack('a4LSssSSssSSssSSssSSssSSssSSssSSssSSssSSssS')
31
+ raise Error, "PDAT response = #{resp[0]}" unless resp[0] == 'PDAT'
32
+
11
33
  return resp unless formatted
12
34
 
13
35
  target_count = resp[1].to_i / 8
@@ -22,29 +44,21 @@ module RfBeam
22
44
 
23
45
  def tdat
24
46
  request_frame_data(:tdat)
47
+ sleep MEASUREMENT_DELAY
25
48
 
26
- sleep 0.1
27
49
  resp = read(16).unpack('a4LSssS')
28
- return { dist: resp[2], speed: resp[3], angle: resp[4], mag: resp[5] } unless resp[1].zero?
50
+ raise Error, "TDAT response = #{resp[0]}" unless resp[0] == 'TDAT'
51
+
52
+ resp
29
53
  end
30
54
 
31
55
  def ddat
32
56
  request_frame_data(:ddat)
57
+ sleep MEASUREMENT_DELAY
33
58
 
34
- read(14).unpack('a4LC6')
35
- end
36
-
37
- def config
38
- puts formatted_grps(grps)
39
- end
40
-
41
- def formatted_parameter(param)
42
- return unless PARAMETERS.include? param
43
-
44
- param_data = PARAMETERS[param]
45
- grps_data = grps
46
- index = grps_data[param_data[:grps_index]]
47
- param_data[:values][index]
59
+ resp = read(14).unpack('a4LC6')
60
+ raise Error, "DDAT response = #{resp[0]}" unless resp[0] == 'DDAT'
61
+ resp
48
62
  end
49
63
 
50
64
  # Get the radar parameter structure
@@ -54,12 +68,28 @@ module RfBeam
54
68
  check_response
55
69
  read(50).unpack('a4LA19C8c2C4cCCCCSCC')
56
70
  end
57
-
71
+
72
+ def config
73
+ data = grps
74
+ output = "\n"
75
+ RADAR_PARAMETERS.keys.each { |key| output << formatted_parameter(key, data[RADAR_PARAMETERS[key].grps_index]) }
76
+ output
77
+ end
78
+
79
+ def formatted_parameter(param, value = nil)
80
+ param = RADAR_PARAMETERS[param]
81
+ if value.nil?
82
+ data = grps
83
+ value = data[param.grps_index]
84
+ end
85
+ param_str_value = param.values.empty? ? value.to_s : param.values[value]
86
+ "#{param.name}: #{param_str_value}#{param.units}\n"
87
+ end
58
88
 
59
89
  private
60
90
 
61
91
  def format_raw_target_data(array)
62
- { dist: array.shift, speed: array.shift, angle: array.shift, mag: array.shift }
92
+ { dist: array.shift, speed: array.shift, angle: array.shift, mag: array.shift }
63
93
  end
64
94
 
65
95
  def request_frame_data(type)
@@ -67,15 +97,5 @@ module RfBeam
67
97
  write command.pack('a4LL')
68
98
  check_response
69
99
  end
70
-
71
- def formatted_grps(data)
72
- output = "\n"
73
- RADAR_PARAMETERS.each do |param|
74
- param = param[1]
75
- value = param.values.empty? ? data[param.grps_index] : param.values[data[param.grps_index].to_i]
76
- output << "#{param.name}: #{value}#{param.units}\n"
77
- end
78
- output
79
- end
80
100
  end
81
101
  end
@@ -1,6 +1,13 @@
1
1
  module RfBeam
2
2
  module KLD7
3
3
 
4
+ # -----------------
5
+ # Software Version, 'K-LD7_APP-RFB-XXXX'
6
+ # -----------------
7
+ def sw_version
8
+ query_parameter RADAR_PARAMETERS[:sw_version].grps_index
9
+ end
10
+
4
11
  # -----------------
5
12
  # Base Frequency, 0 = low, 1 = middle (default), 2 = high
6
13
  # -----------------
@@ -52,6 +59,7 @@ module RfBeam
52
59
 
53
60
  def max_range=(range = 1)
54
61
  raise ArgumentError, "Invalid arg: '#{range}'" unless (0..3).include?(range)
62
+ raise ArgumentError, "Expected an Integer" unless range.is_a?(Integer)
55
63
 
56
64
  set_parameter :rrai, range, :uint32
57
65
  end
@@ -68,7 +76,7 @@ module RfBeam
68
76
 
69
77
  def threshold_offset=(offset = 30)
70
78
  raise ArgumentError, "Invalid arg: '#{offset}'" unless (10..60).include?(offset)
71
- raise ArgumentError, "Expected an Integer" unless range.is_a?(Integer)
79
+ raise ArgumentError, "Expected an Integer" unless offset.is_a?(Integer)
72
80
 
73
81
  set_parameter :thof, offset, :uint32
74
82
  end
@@ -83,13 +91,14 @@ module RfBeam
83
91
  query_parameter RADAR_PARAMETERS[:tracking_filter].grps_index
84
92
  end
85
93
 
86
- def set_tracking_filter(type = 0)
94
+ def tracking_filter=(type = 0)
87
95
  raise ArgumentError, "Invalid arg: '#{type}'" unless (0..2).include?(type)
88
96
  raise ArgumentError, "Expected an Integer" unless type.is_a?(Integer)
89
97
 
90
98
  set_parameter :trft, type, :uint32
91
99
  end
92
- alias trtf set_tracking_filter
100
+ alias trtf= tracking_filter=
101
+ alias set_tracking_filter tracking_filter=
93
102
 
94
103
  # -----------------
95
104
  # Vibration suppression, 0 - 16, 0 = No Suppression, 16 = High Suppression, default = 2
@@ -271,7 +280,7 @@ module RfBeam
271
280
  # Digital output 1, 0 = Direction, 1 = Angle, 2 = Range, 3 = Speed, 4 = Micro Detection, default = 0
272
281
  # -----------------
273
282
  def digital_output1
274
- query_parameter RADAR_PARAMETERS[:digital_output1]grps_index
283
+ query_parameter RADAR_PARAMETERS[:digital_output1].grps_index
275
284
  end
276
285
 
277
286
  def digital_output1=(value = 0)
@@ -287,7 +296,7 @@ module RfBeam
287
296
  # Digital output 2, 0 = Direction, 1 = Angle, 2 = Range, 3 = Speed, 4 = Micro Detection, default = 1
288
297
  # -----------------
289
298
  def digital_output2
290
- query_parameter RADAR_PARAMETERS[:digital_output2]grps_index
299
+ query_parameter RADAR_PARAMETERS[:digital_output2].grps_index
291
300
  end
292
301
 
293
302
  def digital_output2=(value = 1)
@@ -296,7 +305,7 @@ module RfBeam
296
305
 
297
306
  set_parameter :dig2, value, :uint32
298
307
  end
299
- alias dig2= set_digital_output2=
308
+ alias dig2= digital_output2=
300
309
  alias set_digital_output2 digital_output2=
301
310
 
302
311
  # -----------------
@@ -360,8 +369,8 @@ module RfBeam
360
369
 
361
370
  set_parameter :mids, value, :uint32
362
371
  end
363
- alias mids= micro_detection_sensitivty=
364
- alias set_micro_detection_sensitivity micro_detection_sensitivty=
372
+ alias mids= micro_detection_sensitivity=
373
+ alias set_micro_detection_sensitivity micro_detection_sensitivity=
365
374
 
366
375
  private
367
376
 
@@ -75,7 +75,7 @@ module RfBeam
75
75
  end
76
76
 
77
77
  def check_response
78
- sleep RESP_DELAY
78
+ sleep RESPONSE_DELAY
79
79
  resp = @serial_port.read(9).unpack('a4LC') # 4 ASCII bytes, UINT32, UINT8
80
80
  raise Error, 'No valid response from Serial Port' if resp[2].nil?
81
81
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RfBeam
4
- VERSION = '0.3.5'
4
+ VERSION = '0.4.1'
5
5
  end
data/lib/rfbeam.rb CHANGED
@@ -4,7 +4,10 @@ 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_relative 'rfbeam/version'
7
+ require 'rfbeam/kld7/cli_output'
8
+ require 'rfbeam/kld7/cli_formatter'
9
+ require 'rfbeam/version'
10
+ require 'rfbeam/cli'
8
11
 
9
12
  module RfBeam
10
13
  class Error < StandardError
data/output.csv ADDED
@@ -0,0 +1,257 @@
1
+ 0,2105,2105
2
+ 1,3070,2915
3
+ 2,2915,805
4
+ 3,2325,2830
5
+ 4,805,3420
6
+ 5,1000,1340
7
+ 6,2830,1555
8
+ 7,3810,2645
9
+ 8,3420,1270
10
+ 9,1645,1400
11
+ 10,1340,1130
12
+ 11,1285,1350
13
+ 12,1555,1085
14
+ 13,1500,910
15
+ 14,2645,1400
16
+ 15,2785,1435
17
+ 16,1270,1150
18
+ 17,500,1545
19
+ 18,1400,1270
20
+ 19,615,1285
21
+ 20,1130,1435
22
+ 21,1000,785
23
+ 22,1350,710
24
+ 23,1565,955
25
+ 24,1085,1165
26
+ 25,1420,1645
27
+ 26,910,1595
28
+ 27,1265,455
29
+ 28,1400,805
30
+ 29,1015,1050
31
+ 30,1435,560
32
+ 31,1060,1530
33
+ 32,1150,3275
34
+ 33,305,3510
35
+ 34,1545,650
36
+ 35,765,1415
37
+ 36,1270,1760
38
+ 37,785,650
39
+ 38,1285,830
40
+ 39,980,560
41
+ 40,1435,850
42
+ 41,1085,0
43
+ 42,785,480
44
+ 43,1340,1150
45
+ 44,710,910
46
+ 45,1210,1050
47
+ 46,955,500
48
+ 47,1000,1230
49
+ 48,1165,1040
50
+ 49,1060,955
51
+ 50,1645,910
52
+ 51,2200,150
53
+ 52,1595,455
54
+ 53,1085,150
55
+ 54,455,830
56
+ 55,1400,455
57
+ 56,805,500
58
+ 57,1490,850
59
+ 58,1050,0
60
+ 59,560,980
61
+ 60,560,1040
62
+ 61,800,500
63
+ 62,1530,800
64
+ 63,0,1410
65
+ 64,3275,305
66
+ 65,3990,1570
67
+ 66,3510,1255
68
+ 67,1105,955
69
+ 68,650,150
70
+ 69,700,655
71
+ 70,1415,1245
72
+ 71,1295,455
73
+ 72,1760,710
74
+ 73,1235,560
75
+ 74,650,650
76
+ 75,500,1000
77
+ 76,830,760
78
+ 77,650,760
79
+ 78,560,350
80
+ 79,1240,305
81
+ 80,850,150
82
+ 81,1400,910
83
+ 82,0,650
84
+ 83,805,500
85
+ 84,480,1180
86
+ 85,150,1435
87
+ 86,1150,850
88
+ 87,1360,800
89
+ 88,910,1305
90
+ 89,1190,910
91
+ 90,1050,1260
92
+ 91,1115,455
93
+ 92,500,1000
94
+ 93,800,615
95
+ 94,1230,1265
96
+ 95,980,3955
97
+ 96,1040,3675
98
+ 97,500,615
99
+ 98,955,300
100
+ 99,1285,1825
101
+ 100,910,760
102
+ 101,980,1315
103
+ 102,150,1435
104
+ 103,1200,1260
105
+ 104,455,850
106
+ 105,480,1260
107
+ 106,150,1970
108
+ 107,500,1015
109
+ 108,830,350
110
+ 109,700,735
111
+ 110,455,1680
112
+ 111,150,350
113
+ 112,500,1135
114
+ 113,605,865
115
+ 114,850,1735
116
+ 115,805,850
117
+ 116,0,1165
118
+ 117,710,1965
119
+ 118,980,810
120
+ 119,305,1005
121
+ 120,1040,1200
122
+ 121,150,2885
123
+ 122,500,1095
124
+ 123,710,1060
125
+ 124,800,2455
126
+ 125,1265,2145
127
+ 126,1410,1665
128
+ 127,1175,3230
129
+ 128,305,4328
130
+ 129,1255,4228
131
+ 130,1570,4069
132
+ 131,980,3995
133
+ 132,1255,3946
134
+ 133,1480,3910
135
+ 134,955,3881
136
+ 135,455,3857
137
+ 136,150,3836
138
+ 137,910,3818
139
+ 138,655,3802
140
+ 139,350,3788
141
+ 140,1245,3774
142
+ 141,1150,3762
143
+ 142,455,3751
144
+ 143,560,3741
145
+ 144,710,3731
146
+ 145,150,3722
147
+ 146,560,3714
148
+ 147,830,3706
149
+ 148,650,3698
150
+ 149,655,3691
151
+ 150,1000,3684
152
+ 151,350,3677
153
+ 152,760,3671
154
+ 153,455,3665
155
+ 154,760,3659
156
+ 155,560,3654
157
+ 156,350,3648
158
+ 157,655,3643
159
+ 158,305,3638
160
+ 159,150,3633
161
+ 160,150,3629
162
+ 161,910,3624
163
+ 162,910,3620
164
+ 163,1190,3616
165
+ 164,650,3612
166
+ 165,455,3608
167
+ 166,500,3604
168
+ 167,500,3600
169
+ 168,1180,3596
170
+ 169,1500,3592
171
+ 170,1435,3589
172
+ 171,1265,3586
173
+ 172,850,3582
174
+ 173,650,3579
175
+ 174,800,3576
176
+ 175,1350,3573
177
+ 176,1305,3569
178
+ 177,1260,3566
179
+ 178,910,3563
180
+ 179,805,3561
181
+ 180,1260,3558
182
+ 181,1175,3555
183
+ 182,455,3552
184
+ 183,965,3550
185
+ 184,1000,3547
186
+ 185,755,3544
187
+ 186,615,3542
188
+ 187,350,3539
189
+ 188,1265,3537
190
+ 189,935,3534
191
+ 190,3955,3532
192
+ 191,4445,3530
193
+ 192,3675,3528
194
+ 193,1365,3530
195
+ 194,615,3532
196
+ 195,605,3534
197
+ 196,300,3537
198
+ 197,1135,3539
199
+ 198,1825,3542
200
+ 199,895,3544
201
+ 200,760,3547
202
+ 201,500,3550
203
+ 202,1315,3552
204
+ 203,1330,3555
205
+ 204,1435,3558
206
+ 205,1385,3561
207
+ 206,1260,3563
208
+ 207,920,3566
209
+ 208,850,3569
210
+ 209,615,3573
211
+ 210,1260,3576
212
+ 211,1545,3579
213
+ 212,1970,3582
214
+ 213,1605,3586
215
+ 214,1015,3589
216
+ 215,955,3592
217
+ 216,350,3596
218
+ 217,1270,3600
219
+ 218,735,3604
220
+ 219,1330,3608
221
+ 220,1680,3612
222
+ 221,630,3616
223
+ 222,350,3620
224
+ 223,1200,3624
225
+ 224,1135,3629
226
+ 225,850,3633
227
+ 226,865,3638
228
+ 227,1540,3643
229
+ 228,1735,3648
230
+ 229,500,3654
231
+ 230,850,3659
232
+ 231,150,3665
233
+ 232,1165,3671
234
+ 233,1345,3677
235
+ 234,1965,3684
236
+ 235,1630,3691
237
+ 236,810,3698
238
+ 237,1175,3706
239
+ 238,1005,3714
240
+ 239,500,3722
241
+ 240,1200,3731
242
+ 241,2830,3741
243
+ 242,2885,3751
244
+ 243,1630,3762
245
+ 244,1095,3774
246
+ 245,980,3788
247
+ 246,1060,3802
248
+ 247,1190,3818
249
+ 248,2455,3836
250
+ 249,3045,3857
251
+ 250,2145,3881
252
+ 251,1500,3910
253
+ 252,1665,3946
254
+ 253,1975,3995
255
+ 254,3230,
256
+ 255,3470,
257
+ 256,4328,
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.3.5
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-01 00:00:00.000000000 Z
11
+ date: 2023-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -55,12 +55,14 @@ dependencies:
55
55
  description: Currently only tested with K-LD7 on MacOS & Raspian (bullseye)
56
56
  email:
57
57
  - robc@hey.com
58
- executables: []
58
+ executables:
59
+ - rfbeam
59
60
  extensions: []
60
61
  extra_rdoc_files: []
61
62
  files:
62
63
  - ".DS_Store"
63
64
  - ".rubocop.yml"
65
+ - ".streerc"
64
66
  - ".tool-versions"
65
67
  - CHANGELOG.md
66
68
  - Gemfile
@@ -68,7 +70,11 @@ files:
68
70
  - LICENSE.txt
69
71
  - README.md
70
72
  - Rakefile
73
+ - exe/rfbeam
71
74
  - lib/rfbeam.rb
75
+ - lib/rfbeam/cli.rb
76
+ - lib/rfbeam/kld7/cli_formatter.rb
77
+ - lib/rfbeam/kld7/cli_output.rb
72
78
  - lib/rfbeam/kld7/constants.rb
73
79
  - lib/rfbeam/kld7/radar_messages.rb
74
80
  - lib/rfbeam/kld7/radar_parameters.rb
@@ -159,6 +165,7 @@ files:
159
165
  - node_modules/prettier/parser-yaml.js
160
166
  - node_modules/prettier/standalone.js
161
167
  - node_modules/prettier/third-party.js
168
+ - output.csv
162
169
  - package.json
163
170
  - rfbeam.gemspec
164
171
  - sig/rfbeam.rbs