rfbeam 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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