rfbeam 0.4.10 → 0.5.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.
@@ -1,6 +1,4 @@
1
- # frozen_string_literal: true
2
-
3
- require 'csv'
1
+ require "csv"
4
2
 
5
3
  module RfBeam
6
4
  module Kld7
@@ -12,26 +10,26 @@ module RfBeam
12
10
  def rfft
13
11
  request_frame_data(:rfft)
14
12
  sleep MEASUREMENT_DELAY
15
- data = read(1032).unpack('a4LS256S256')
13
+ data = read(1032).unpack("a4LS256S256")
16
14
  header, length = data.shift(2)
17
- raise Error, "RFFT header response, header=#{header}" unless header == 'RFFT'
15
+ unless header == "RFFT"
16
+ raise Error, "RFFT header response, header=#{header}"
17
+ end
18
18
  raise Error, "RFFT payload length, length=#{length}" unless length == 1024
19
19
 
20
20
  data
21
21
  end
22
22
 
23
23
  def reset
24
- command = ['RFSE', 0]
25
- write command.pack('a4L')
24
+ command = ["RFSE", 0]
25
+ write command.pack("a4L")
26
26
  check_response
27
27
  end
28
28
  alias rfse reset
29
29
 
30
30
  def pdat(formatted: false)
31
31
  request_frame_data(:pdat)
32
- resp = read(102).unpack('a4LSssSSssSSssSSssSSssSSssSSssSSssSSssSSssS')
33
- raise Error, "PDAT response = #{resp[0]}" unless resp[0] == 'PDAT'
34
-
32
+ resp = read(102).unpack("a4LSssSSssSSssSSssSSssSSssSSssSSssSSssSSssS")
35
33
  return resp unless formatted
36
34
 
37
35
  target_count = resp[1].to_i / 8
@@ -40,7 +38,9 @@ module RfBeam
40
38
  resp.shift 2
41
39
  resp.compact
42
40
  detected_raw_targets = []
43
- target_count.times { detected_raw_targets << format_raw_target_data(resp.shift(4)) }
41
+ target_count.times do
42
+ detected_raw_targets << format_raw_target_data(resp.shift(4))
43
+ end
44
44
  detected_raw_targets
45
45
  end
46
46
 
@@ -48,34 +48,46 @@ module RfBeam
48
48
  request_frame_data(:tdat)
49
49
  sleep MEASUREMENT_DELAY
50
50
 
51
- resp = read(16).unpack('a4LSssS')
52
- raise Error, "TDAT response = #{resp[0]}" unless resp[0] == 'TDAT'
53
-
54
- resp
51
+ sleep 0.1
52
+ resp = read(16).unpack("a4LSssS")
53
+ unless resp[1].zero?
54
+ return { dist: resp[2], speed: resp[3], angle: resp[4], mag: resp[5] }
55
+ end
55
56
  end
56
57
 
57
58
  def ddat
58
59
  request_frame_data(:ddat)
59
- sleep MEASUREMENT_DELAY
60
-
61
- resp = read(14).unpack('a4LC6')
62
- raise Error, "DDAT response = #{resp[0]}" unless resp[0] == 'DDAT'
63
-
64
- resp
60
+ flags = %w[Low High]
61
+ array = read(14).unpack("a4LC6")
62
+ {
63
+ label: array[0],
64
+ detection: array[2] == 1,
65
+ detection_str: DETECTION_FLAGS[:detection][array[2]],
66
+ micro_detection: DETECTION_FLAGS[:micro_detection][array[3]],
67
+ angle: DETECTION_FLAGS[:angle][array[4]],
68
+ direction: DETECTION_FLAGS[:direction][array[5]],
69
+ range: DETECTION_FLAGS[:range][array[6]],
70
+ speed: DETECTION_FLAGS[:speed][array[7]]
71
+ }
65
72
  end
66
73
 
67
74
  # Get the radar parameter structure
68
75
  def grps
69
- command = ['GRPS', 0]
70
- write command.pack('a4L')
76
+ command = ["GRPS", 0]
77
+ write command.pack("a4L")
71
78
  check_response
72
- read(50).unpack('a4LA19C8c2C4cCCCCSCC')
79
+ read(50).unpack("a4LA19C8c2C4cCCCCSCC")
73
80
  end
74
81
 
75
82
  def config
76
83
  data = grps
77
- output = []
78
- RADAR_PARAMETERS.each_key { |key| output << formatted_parameter(key, data[RADAR_PARAMETERS[key].grps_index]) }
84
+ output = "\n"
85
+ RADAR_PARAMETERS.keys.each do |key|
86
+ output << formatted_parameter(
87
+ key,
88
+ data[RADAR_PARAMETERS[key].grps_index]
89
+ )
90
+ end
79
91
  output
80
92
  end
81
93
 
@@ -92,12 +104,17 @@ module RfBeam
92
104
  private
93
105
 
94
106
  def format_raw_target_data(array)
95
- { dist: array.shift, speed: array.shift, angle: array.shift, mag: array.shift }
107
+ {
108
+ dist: array.shift,
109
+ speed: array.shift,
110
+ angle: array.shift,
111
+ mag: array.shift
112
+ }
96
113
  end
97
114
 
98
115
  def request_frame_data(type)
99
- command = ['GNFD', 4, FRAME_DATA_TYPES[type]]
100
- write command.pack('a4LL')
116
+ command = ["GNFD", 4, FRAME_DATA_TYPES[type]]
117
+ write command.pack("a4LL")
101
118
  check_response
102
119
  end
103
120
  end
@@ -1,7 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
1
  module RfBeam
4
- module Kld7
2
+ module KLD7
5
3
  # -----------------
6
4
  # Software Version, 'K-LD7_APP-RFB-XXXX'
7
5
  # -----------------
@@ -20,11 +18,11 @@ module RfBeam
20
18
  def base_frequency=(frequency = 1)
21
19
  value =
22
20
  case frequency
23
- when 0, :low, 'low'
21
+ when 0, :low, "low"
24
22
  0
25
- when 1, :middle, 'middle'
23
+ when 1, :middle, "middle"
26
24
  1
27
- when 2, :high, 'high'
25
+ when 2, :high, "high"
28
26
  2
29
27
  else
30
28
  raise ArgumentError, "Invalid arg: '#{frequency}'"
@@ -43,8 +41,10 @@ module RfBeam
43
41
  end
44
42
 
45
43
  def max_speed=(speed = 1)
46
- raise ArgumentError, "Invalid arg: '#{speed}'" unless (0..3).include?(speed)
47
- raise ArgumentError, 'Expected an Integer' unless speed.is_a?(Integer)
44
+ unless (0..3).include?(speed)
45
+ raise ArgumentError, "Invalid arg: '#{speed}'"
46
+ end
47
+ raise ArgumentError, "Expected an Integer" unless speed.is_a?(Integer)
48
48
 
49
49
  set_parameter :rspi, speed, :uint32
50
50
  end
@@ -60,8 +60,10 @@ module RfBeam
60
60
  end
61
61
 
62
62
  def max_range=(range = 1)
63
- raise ArgumentError, "Invalid arg: '#{range}'" unless (0..3).include?(range)
64
- raise ArgumentError, 'Expected an Integer' unless range.is_a?(Integer)
63
+ unless (0..3).include?(range)
64
+ raise ArgumentError, "Invalid arg: '#{range}'"
65
+ end
66
+ raise ArgumentError, "Expected an Integer" unless range.is_a?(Integer)
65
67
 
66
68
  set_parameter :rrai, range, :uint32
67
69
  end
@@ -77,8 +79,10 @@ module RfBeam
77
79
  end
78
80
 
79
81
  def threshold_offset=(offset = 30)
80
- raise ArgumentError, "Invalid arg: '#{offset}'" unless (10..60).include?(offset)
81
- raise ArgumentError, 'Expected an Integer' unless offset.is_a?(Integer)
82
+ unless (10..60).include?(offset)
83
+ raise ArgumentError, "Invalid arg: '#{offset}'"
84
+ end
85
+ raise ArgumentError, "Expected an Integer" unless offset.is_a?(Integer)
82
86
 
83
87
  set_parameter :thof, offset, :uint32
84
88
  end
@@ -95,7 +99,7 @@ module RfBeam
95
99
 
96
100
  def tracking_filter=(type = 0)
97
101
  raise ArgumentError, "Invalid arg: '#{type}'" unless (0..2).include?(type)
98
- raise ArgumentError, 'Expected an Integer' unless type.is_a?(Integer)
102
+ raise ArgumentError, "Expected an Integer" unless type.is_a?(Integer)
99
103
 
100
104
  set_parameter :trft, type, :uint32
101
105
  end
@@ -110,8 +114,10 @@ module RfBeam
110
114
  end
111
115
 
112
116
  def vibration_suppression=(value = 2)
113
- raise ArgumentError, "Invalid arg: '#{value}'" unless (0..16).include?(value)
114
- raise ArgumentError, 'Expected an Integer' unless value.is_a?(Integer)
117
+ unless (0..16).include?(value)
118
+ raise ArgumentError, "Invalid arg: '#{value}'"
119
+ end
120
+ raise ArgumentError, "Expected an Integer" unless value.is_a?(Integer)
115
121
 
116
122
  set_parameter :visu, value, :uint32
117
123
  end
@@ -126,8 +132,10 @@ module RfBeam
126
132
  end
127
133
 
128
134
  def min_detection_distance=(value = 0)
129
- raise ArgumentError, "Invalid arg: '#{value}'" unless (0..100).include?(value)
130
- raise ArgumentError, 'Expected an Integer' unless value.is_a?(Integer)
135
+ unless (0..100).include?(value)
136
+ raise ArgumentError, "Invalid arg: '#{value}'"
137
+ end
138
+ raise ArgumentError, "Expected an Integer" unless value.is_a?(Integer)
131
139
 
132
140
  set_parameter :mira, value, :uint32
133
141
  end
@@ -142,8 +150,10 @@ module RfBeam
142
150
  end
143
151
 
144
152
  def max_detection_distance=(value = 50)
145
- raise ArgumentError, "Invalid arg: '#{value}'" unless (0..100).include?(value)
146
- raise ArgumentError, 'Expected an Integer' unless value.is_a?(Integer)
153
+ unless (0..100).include?(value)
154
+ raise ArgumentError, "Invalid arg: '#{value}'"
155
+ end
156
+ raise ArgumentError, "Expected an Integer" unless value.is_a?(Integer)
147
157
 
148
158
  set_parameter :mara, value, :uint32
149
159
  end
@@ -158,8 +168,10 @@ module RfBeam
158
168
  end
159
169
 
160
170
  def min_detection_angle=(angle = -90)
161
- raise ArgumentError, "Invalid arg: '#{angle}'" unless (-90..90).include?(angle)
162
- raise ArgumentError, 'Expected an Integer' unless angle.is_a?(Integer)
171
+ unless (-90..90).include?(angle)
172
+ raise ArgumentError, "Invalid arg: '#{angle}'"
173
+ end
174
+ raise ArgumentError, "Expected an Integer" unless angle.is_a?(Integer)
163
175
 
164
176
  set_parameter :mian, angle, :int32
165
177
  end
@@ -174,8 +186,10 @@ module RfBeam
174
186
  end
175
187
 
176
188
  def max_detection_angle=(angle = 90)
177
- raise ArgumentError, "Invalid arg: '#{angle}'" unless (-90..90).include?(angle)
178
- raise ArgumentError, 'Expected an Integer' unless angle.is_a?(Integer)
189
+ unless (-90..90).include?(angle)
190
+ raise ArgumentError, "Invalid arg: '#{angle}'"
191
+ end
192
+ raise ArgumentError, "Expected an Integer" unless angle.is_a?(Integer)
179
193
 
180
194
  set_parameter :maan, angle, :int32
181
195
  end
@@ -190,8 +204,10 @@ module RfBeam
190
204
  end
191
205
 
192
206
  def min_detection_speed=(speed = 0)
193
- raise ArgumentError, "Invalid arg: '#{speed}'" unless (0..100).include?(speed)
194
- raise ArgumentError, 'Expected an Integer' unless speed.is_a?(Integer)
207
+ unless (0..100).include?(speed)
208
+ raise ArgumentError, "Invalid arg: '#{speed}'"
209
+ end
210
+ raise ArgumentError, "Expected an Integer" unless speed.is_a?(Integer)
195
211
 
196
212
  set_parameter :misp, speed, :uint32
197
213
  end
@@ -206,8 +222,10 @@ module RfBeam
206
222
  end
207
223
 
208
224
  def max_detection_speed=(speed = 100)
209
- raise ArgumentError, "Invalid arg: '#{speed}'" unless (0..100).include?(speed)
210
- raise ArgumentError, 'Expected an Integer' unless speed.is_a?(Integer)
225
+ unless (0..100).include?(speed)
226
+ raise ArgumentError, "Invalid arg: '#{speed}'"
227
+ end
228
+ raise ArgumentError, "Expected an Integer" unless speed.is_a?(Integer)
211
229
 
212
230
  set_parameter :masp, speed, :uint32
213
231
  end
@@ -222,8 +240,10 @@ module RfBeam
222
240
  end
223
241
 
224
242
  def detection_direction=(direction = 2)
225
- raise ArgumentError, "Invalid arg: '#{direction}'" unless (0..2).include?(direction)
226
- raise ArgumentError, 'Expected an Integer' unless direction.is_a?(Integer)
243
+ unless (0..2).include?(direction)
244
+ raise ArgumentError, "Invalid arg: '#{direction}'"
245
+ end
246
+ raise ArgumentError, "Expected an Integer" unless direction.is_a?(Integer)
227
247
 
228
248
  set_parameter :dedi, direction, :uint32
229
249
  end
@@ -238,8 +258,10 @@ module RfBeam
238
258
  end
239
259
 
240
260
  def range_threshold=(value = 10)
241
- raise ArgumentError, "Invalid arg: '#{value}'" unless (0..100).include?(value)
242
- raise ArgumentError, 'Expected an Integer' unless value.is_a?(Integer)
261
+ unless (0..100).include?(value)
262
+ raise ArgumentError, "Invalid arg: '#{value}'"
263
+ end
264
+ raise ArgumentError, "Expected an Integer" unless value.is_a?(Integer)
243
265
 
244
266
  set_parameter :rath, value, :uint32
245
267
  end
@@ -254,8 +276,10 @@ module RfBeam
254
276
  end
255
277
 
256
278
  def angle_threshold=(value = 0)
257
- raise ArgumentError, "Invalid arg: '#{value}'" unless (-90..90).include?(value)
258
- raise ArgumentError, 'Expected an Integer' unless value.is_a?(Integer)
279
+ unless (-90..90).include?(value)
280
+ raise ArgumentError, "Invalid arg: '#{value}'"
281
+ end
282
+ raise ArgumentError, "Expected an Integer" unless value.is_a?(Integer)
259
283
 
260
284
  set_parameter :anth, value, :int32
261
285
  end
@@ -270,8 +294,10 @@ module RfBeam
270
294
  end
271
295
 
272
296
  def speed_threshold=(value = 50)
273
- raise ArgumentError, "Invalid arg: '#{value}'" unless (0..100).include?(value)
274
- raise ArgumentError, 'Expected an Integer' unless value.is_a?(Integer)
297
+ unless (0..100).include?(value)
298
+ raise ArgumentError, "Invalid arg: '#{value}'"
299
+ end
300
+ raise ArgumentError, "Expected an Integer" unless value.is_a?(Integer)
275
301
 
276
302
  set_parameter :spth, value, :uint32
277
303
  end
@@ -286,8 +312,10 @@ module RfBeam
286
312
  end
287
313
 
288
314
  def digital_output1=(value = 0)
289
- raise ArgumentError, "Invalid arg: '#{value}'" unless (0..4).include?(value)
290
- raise ArgumentError, 'Expected an Integer' unless value.is_a?(Integer)
315
+ unless (0..4).include?(value)
316
+ raise ArgumentError, "Invalid arg: '#{value}'"
317
+ end
318
+ raise ArgumentError, "Expected an Integer" unless value.is_a?(Integer)
291
319
 
292
320
  set_parameter :dig1, value, :uint32
293
321
  end
@@ -302,8 +330,10 @@ module RfBeam
302
330
  end
303
331
 
304
332
  def digital_output2=(value = 1)
305
- raise ArgumentError, "Invalid arg: '#{value}'" unless (0..4).include?(value)
306
- raise ArgumentError, 'Expected an Integer' unless value.is_a?(Integer)
333
+ unless (0..4).include?(value)
334
+ raise ArgumentError, "Invalid arg: '#{value}'"
335
+ end
336
+ raise ArgumentError, "Expected an Integer" unless value.is_a?(Integer)
307
337
 
308
338
  set_parameter :dig2, value, :uint32
309
339
  end
@@ -318,8 +348,10 @@ module RfBeam
318
348
  end
319
349
 
320
350
  def digital_output3=(value = 2)
321
- raise ArgumentError, "Invalid arg: '#{value}'" unless (0..4).include?(value)
322
- raise ArgumentError, 'Expected an Integer' unless value.is_a?(Integer)
351
+ unless (0..4).include?(value)
352
+ raise ArgumentError, "Invalid arg: '#{value}'"
353
+ end
354
+ raise ArgumentError, "Expected an Integer" unless value.is_a?(Integer)
323
355
 
324
356
  set_parameter :dig3, value, :uint32
325
357
  end
@@ -334,8 +366,10 @@ module RfBeam
334
366
  end
335
367
 
336
368
  def hold_time=(time = 1)
337
- raise ArgumentError, "Invalid arg: '#{time}'" unless (1..7200).include?(time)
338
- raise ArgumentError, 'Expected an Integer' unless time.is_a?(Integer)
369
+ unless (1..7200).include?(time)
370
+ raise ArgumentError, "Invalid arg: '#{time}'"
371
+ end
372
+ raise ArgumentError, "Expected an Integer" unless time.is_a?(Integer)
339
373
 
340
374
  set_parameter :hold, time, :uint32
341
375
  end
@@ -346,12 +380,16 @@ module RfBeam
346
380
  # Micro Detection retrigger, 0 = Off (default), 1 = Retrigger
347
381
  # -----------------
348
382
  def micro_detection_retrigger
349
- query_parameter RADAR_PARAMETERS[:set_micro_detection_retrigger].grps_index
383
+ query_parameter RADAR_PARAMETERS[
384
+ :set_micro_detection_retrigger
385
+ ].grps_index
350
386
  end
351
387
 
352
388
  def micro_detection_retrigger=(value = 0)
353
- raise ArgumentError, "Invalid arg: '#{value}'" unless (0..1).include?(value)
354
- raise ArgumentError, 'Expected an Integer' unless value.is_a?(Integer)
389
+ unless (0..1).include?(value)
390
+ raise ArgumentError, "Invalid arg: '#{value}'"
391
+ end
392
+ raise ArgumentError, "Expected an Integer" unless value.is_a?(Integer)
355
393
 
356
394
  set_parameter :mide, value, :uint32
357
395
  end
@@ -366,8 +404,10 @@ module RfBeam
366
404
  end
367
405
 
368
406
  def micro_detection_sensitivity=(value = 4)
369
- raise ArgumentError, "Invalid arg: '#{value}'" unless (0..9).include?(value)
370
- raise ArgumentError, 'Expected an Integer' unless value.is_a?(Integer)
407
+ unless (0..9).include?(value)
408
+ raise ArgumentError, "Invalid arg: '#{value}'"
409
+ end
410
+ raise ArgumentError, "Expected an Integer" unless value.is_a?(Integer)
371
411
 
372
412
  set_parameter :mids, value, :uint32
373
413
  end
@@ -384,10 +424,14 @@ module RfBeam
384
424
  def set_parameter(header, value, return_type = :uint32)
385
425
  return_type =
386
426
  case return_type
427
+ when :uint32
428
+ "L"
387
429
  when :int32
388
- 'l'
430
+ "l"
431
+ when :uint32
432
+ "S"
389
433
  else
390
- 'L'
434
+ "L"
391
435
  end
392
436
  command = [header.upcase.to_s, 4, value]
393
437
  write command.pack("a4L#{return_type}")
@@ -0,0 +1,82 @@
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
+ {
55
+ x: Array(-128...128),
56
+ series1: data.shift(256).map { |value| value / 100 },
57
+ series2: data.shift(256).map { |value| value.to_i / 100 }
58
+ }
59
+ end
60
+
61
+ def rfft_plot(radar)
62
+ width = TTY::Screen.width * 0.65
63
+ data = plot_data(radar.rfft)
64
+ plot =
65
+ UnicodePlot.lineplot(
66
+ data[:x],
67
+ data[:series1],
68
+ name: "IF1/2 Averaged",
69
+ title: "Raw FFT",
70
+ height: 25,
71
+ width: width,
72
+ xlabel: "Speed (km/h)",
73
+ ylabel: "Signal (db)",
74
+ xlim: [-128, 128],
75
+ ylim: [0, 100]
76
+ )
77
+ UnicodePlot.lineplot!(plot, data[:x], data[:series2], name: "Threshold")
78
+ plot
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RfBeam
4
- VERSION = '0.4.10'
4
+ VERSION = '0.5.1'
5
5
  end
data/rfbeam.gemspec CHANGED
@@ -10,15 +10,15 @@ Gem::Specification.new do |spec|
10
10
 
11
11
  spec.summary = 'Ruby API and CLI for RFBeam doplar radar modules'
12
12
  spec.description = 'Currently only tested with K-LD7 on MacOS & Raspian (bullseye)'
13
- spec.homepage = 'https://gitlab.com/robcarruthers/rfbeam'
13
+ spec.homepage = 'https://github.com/robcarruthers/rfbeam'
14
14
  spec.license = 'MIT'
15
15
  spec.required_ruby_version = '>= 3.1.2'
16
16
 
17
17
  # spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
18
18
 
19
19
  spec.metadata['homepage_uri'] = spec.homepage
20
- spec.metadata['source_code_uri'] = 'https://gitlab.com/robcarruthers/rfbeam'
21
- spec.metadata['changelog_uri'] = 'https://gitlab.com/robcarruthers/rfbeam/-/blob/master/CHANGELOG.md'
20
+ spec.metadata['source_code_uri'] = 'https://github.com/robcarruthers/rfbeam'
21
+ spec.metadata['changelog_uri'] = 'https://github.com/robcarruthers/rfbeam/CHANGELOG.md'
22
22
 
23
23
  # Specify which files should be added to the gem when it is released.
24
24
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
data/streamer2.rb ADDED
@@ -0,0 +1,55 @@
1
+ require "unicode_plot"
2
+ require "stringio"
3
+ require "io/console"
4
+
5
+ N = 1000
6
+ M = 50
7
+
8
+ def generate_random_data(n)
9
+ Array.new(n) { rand(-10.0..10.0) }
10
+ end
11
+
12
+ def monitor_keypress
13
+ loop do
14
+ key = STDIN.getch
15
+ if key.downcase == "q"
16
+ @stop_streaming = true
17
+ break
18
+ end
19
+ end
20
+ end
21
+
22
+ out = StringIO.new
23
+ def out.tty?
24
+ true
25
+ end
26
+
27
+ Thread.new { monitor_keypress }
28
+
29
+ loop do
30
+ out.truncate(0)
31
+
32
+ plot =
33
+ UnicodePlot.lineplot(
34
+ generate_random_data(40),
35
+ name: "Series 0",
36
+ width: 120,
37
+ height: 30
38
+ )
39
+ UnicodePlot.lineplot!(
40
+ plot,
41
+ generate_random_data(40),
42
+ name: "Series 1",
43
+ color: :red
44
+ )
45
+ plot.render(out)
46
+
47
+ lines = out.string.lines
48
+ lines.each { |line| $stdout.print "\r#{line}" }
49
+ $stdout.print "\e[0J"
50
+ $stdout.flush
51
+ break if @stop_streaming
52
+
53
+ n = lines.count
54
+ $stdout.print "\e[#{n}F"
55
+ end
data/streaming.rb ADDED
@@ -0,0 +1,71 @@
1
+ # rubocop:disable all
2
+ require "unicode_plot"
3
+ require "io/console"
4
+
5
+ def generate_random_data(n)
6
+ Array.new(n) { rand(-10.0..10.0) }
7
+ end
8
+
9
+ def update_data(plot, series1, series2)
10
+ plot.series_list[0].data.y = series1
11
+ plot.series_list[1].data.y = series2
12
+ plot.auto_calc_ylim
13
+ end
14
+
15
+ def start
16
+ Thread.new { monitor_keypress }
17
+ loop do
18
+ break if @stop_streaming
19
+ display_plot
20
+ sleep period
21
+ end
22
+ end
23
+
24
+ def monitor_keypress
25
+ loop do
26
+ key = STDIN.getch
27
+ if key.downcase == "q"
28
+ @stop_streaming = true
29
+ break
30
+ end
31
+ end
32
+ end
33
+
34
+ def plot_demo
35
+ out = StringIO.new
36
+ out.truncate(0)
37
+ plot =
38
+ UnicodePlot.lineplot(
39
+ generate_random_data(40),
40
+ name: "Series 0",
41
+ width: 40,
42
+ height: 10
43
+ )
44
+
45
+ UnicodePlot.lineplot!(
46
+ plot,
47
+ generate_random_data(40),
48
+ name: "Series 1",
49
+ color: :red
50
+ )
51
+ UnicodePlot.lineplot!(
52
+ plot,
53
+ generate_random_data(40),
54
+ name: "Series 2",
55
+ color: :blue
56
+ )
57
+ puts plot.render(out)
58
+
59
+ Thread.new { monitor_keypress }
60
+
61
+ loop do
62
+ break if @stop_streaming
63
+
64
+ lines = out.string.lines
65
+ lines.each { |line| $stdout.print "\r#{line}" }
66
+ $stdout.print "\e[0J"
67
+ $stdout.flush
68
+ end
69
+ end
70
+
71
+ plot_demo