rfbeam 0.4.10 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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