rfbeam 0.3.5 → 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 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