rfbeam 0.4.0 → 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: 19f39170dee64099fe6806a345393beff434035df70fe525fa31b361468cac9e
4
- data.tar.gz: 622aba1c5ab9f85fdb8cadf90f10339344306db39ab9299b2ef8b437d228d82d
3
+ metadata.gz: 7edface1b941c801c5842d40d396f8343461271f10a7c381346338e415b074e9
4
+ data.tar.gz: 02bf778dc66bb874da0323867beeb350d5a69e8bb8aa3986c140a88370ae4d94
5
5
  SHA512:
6
- metadata.gz: 57bd136ef560a32dab06e08c841b44e6e1b192525d9d3d44db1a88885c0b30b9b9b28ab28b1f408d628e191195373a012d31405ae24372fa4efd76ecbb3af46c
7
- data.tar.gz: 5dc08c9c71c511e210a4c30599248bb5be53f1ccd7373af73edeb1ff35043dc76b90235bf46de93b9b755123af598225d6b96f06f6c0c5ab89bf4d1bc2e81d57
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
@@ -0,0 +1,2 @@
1
+ --print-width=120
2
+ --plugins=plugin/single_quotes
data/Gemfile CHANGED
@@ -20,3 +20,7 @@ gem "unicode_plot", "~> 0.0.5"
20
20
  gem "tty-logger", "~> 0.6.0"
21
21
 
22
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.4.0)
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
- @logger.warning 'No Radar modules found.' unless devices.count.positive?
17
+ logger.warning 'No Radar modules found.' unless devices.count.positive?
19
18
 
20
- table = TTY::Table.new( header: ['id', 'Path', 'Version'])
19
+ table = TTY::Table.new(header: %w[id Path Version])
21
20
 
22
- devices.each.with_index do |path, 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
- init_radar(radar_id)
31
- puts @radar.config
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.warning("Invalid param: '#{param}'") unless RfBeam::K_ld7::RADAR_PARAMETERS.include?(param.to_sym)
37
+ return @logger.warn("Invalid param: '#{param}'") unless RfBeam::K_ld7::RADAR_PARAMETERS.include?(param.to_sym)
37
38
 
38
- init_radar radar_id
39
- @radar.send("#{param}=", value.to_i)
40
- logger.success "Set #{@radar.formatted_parameter(param.to_sym)}"
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: "Stream the data from the device"
45
+ option :stream, type: :boolean, aliases: '-s', desc: 'Stream the data from the device'
45
46
  def ddat(radar_id)
46
- init_radar radar_id
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
- init_radar radar_id
69
- puts @radar.pdat
53
+ cli = RfBeam::KLD7::CliOutput.new(radar_id)
54
+ cli.display(:pdat, stream: options[:stream])
70
55
  end
71
56
 
72
- desc "rfft <radar_id>", "Display the dopplar radar data as a plot"
73
- option :stream, type: :boolean, aliases: '-s', desc: "Stream the data from the device"
74
- option :period, type: :numeric, aliases: '-p', default: 0.5, desc: "Update period (in seconds) for the streaming data"
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
- init_radar(radar_id)
77
-
78
- if options[:stream]
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 = rfft_plot(@radar)
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 init_radar(id)
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
- @radar = RfBeam::K_ld7.new(devices[id.to_i])
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
- # 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
 
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:)
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
- threshold_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
@@ -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
- resp = read(1032).unpack('a4LS256S256')
14
- resp.shift 2
15
- resp
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
- 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
39
53
  end
40
54
 
41
55
  def ddat
42
56
  request_frame_data(:ddat)
43
- flags = %w[Low High]
44
- array = read(14).unpack('a4LC6')
45
- { label: array[0],
46
- detection: DETECTION_FLAGS[:detection][array[2]],
47
- micro_detection: DETECTION_FLAGS[:micro_detection][array[3]],
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 do |key|
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
- { 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 }
86
93
  end
87
94
 
88
95
  def request_frame_data(type)
@@ -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.4.0'
4
+ VERSION = '0.4.1'
5
5
  end
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/streamer'
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.0
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-04 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
@@ -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
@@ -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