Class: RTLSDR::TcpClient
- Inherits:
-
Object
- Object
- RTLSDR::TcpClient
- Includes:
- Enumerable
- Defined in:
- lib/rtlsdr/tcp_client.rb
Overview
TCP client for connecting to rtl_tcp servers
TcpClient provides a network interface to RTL-SDR devices that are shared over the network using the rtl_tcp utility. It implements the same interface as RTLSDR::Device, allowing seamless switching between local and remote devices.
rtl_tcp is a utility that makes RTL-SDR devices available over TCP/IP. Run it on a remote machine with: rtl_tcp -a 0.0.0.0
Constant Summary collapse
- CMD_SET_FREQUENCY =
rtl_tcp command codes
0x01- CMD_SET_SAMPLE_RATE =
0x02- CMD_SET_GAIN_MODE =
0x03- CMD_SET_GAIN =
0x04- CMD_SET_FREQ_CORRECTION =
0x05- CMD_SET_IF_GAIN =
0x06- CMD_SET_TEST_MODE =
0x07- CMD_SET_AGC_MODE =
0x08- CMD_SET_DIRECT_SAMPLING =
0x09- CMD_SET_OFFSET_TUNING =
0x0a- CMD_SET_RTL_XTAL =
0x0b- CMD_SET_TUNER_XTAL =
0x0c- CMD_SET_GAIN_BY_INDEX =
0x0d- CMD_SET_BIAS_TEE =
0x0e- DEFAULT_PORT =
Default rtl_tcp port
1234
Instance Attribute Summary collapse
-
#center_freq ⇒ Integer
(also: #frequency)
Get center frequency.
-
#direct_sampling ⇒ Integer
Get direct sampling mode.
-
#freq_correction ⇒ Integer
Get frequency correction.
-
#gain_count ⇒ Integer
readonly
Number of gain values supported.
-
#host ⇒ String
readonly
Remote host address.
-
#offset_tuning ⇒ Boolean
Get offset tuning mode.
-
#port ⇒ Integer
readonly
Remote port number.
-
#sample_rate ⇒ Integer
(also: #samp_rate)
Get current sample rate.
-
#tuner_gain ⇒ Integer
(also: #gain)
Get current tuner gain.
-
#tuner_type ⇒ Integer
readonly
Tuner type from server.
Instance Method Summary collapse
-
#agc_mode! ⇒ Boolean
Enable AGC mode.
-
#agc_mode=(enabled) ⇒ Object
Set AGC mode.
-
#auto_gain_mode! ⇒ Boolean
Enable automatic gain mode.
-
#bias_tee! ⇒ Boolean
Enable bias tee (alias).
-
#bias_tee=(enabled) ⇒ Object
Set bias tee state.
-
#cancel_async ⇒ void
Cancel asynchronous reading.
-
#close ⇒ void
Close the connection.
-
#closed? ⇒ Boolean
Check if connection is closed.
-
#configure(frequency: nil, sample_rate: nil, gain: nil, **options) ⇒ Object
Configuration shortcut.
-
#disable_bias_tee ⇒ Boolean
Disable bias tee.
-
#dump_eeprom ⇒ Object
Dump EEPROM (not supported via TCP).
-
#each(samples_per_read: 1024) ⇒ Object
Enumerable interface.
-
#enable_bias_tee ⇒ Boolean
Enable bias tee.
-
#info ⇒ Object
Device info as hash.
-
#initialize(host, port = DEFAULT_PORT, timeout: 10) ⇒ TcpClient
constructor
Create a new TCP client connection to an rtl_tcp server.
-
#inspect ⇒ Object
String representation.
-
#manual_gain_mode! ⇒ Boolean
Enable manual gain mode.
-
#name ⇒ Object
Device information.
-
#offset_tuning! ⇒ Boolean
Enable offset tuning.
-
#open? ⇒ Boolean
Check if connected to server.
-
#read_async(buffer_count: 15, buffer_length: 262_144) {|Array<Integer>| ... } ⇒ Thread
Read raw IQ data asynchronously.
-
#read_eeprom(_offset, _length) ⇒ Object
Read EEPROM (not supported via TCP).
-
#read_samples(count = 1024) ⇒ Array<Complex>
Read complex samples synchronously.
-
#read_samples_async(buffer_count: 15, buffer_length: 262_144) {|Array<Complex>| ... } ⇒ Thread
Read complex samples asynchronously.
-
#read_sync(length) ⇒ Array<Integer>
Read raw IQ data synchronously.
-
#reset_buffer ⇒ Object
Buffer reset (no-op for TCP).
-
#set_bias_tee_gpio(_gpio, enabled) ⇒ Object
Bias tee GPIO not supported via TCP.
-
#set_tuner_if_gain(stage, gain) ⇒ Integer
Set IF gain for specific stage.
-
#set_xtal_freq(rtl_freq, tuner_freq) ⇒ Object
Crystal oscillator frequencies (not fully supported via TCP).
-
#streaming? ⇒ Boolean
Check if streaming.
-
#test_mode! ⇒ Boolean
Enable test mode.
-
#test_mode=(enabled) ⇒ Object
Set test mode.
-
#tuner_bandwidth=(_bw) ⇒ Object
Set tuner bandwidth (not supported via rtl_tcp).
-
#tuner_gain_mode=(manual) ⇒ Object
Set gain mode (manual or automatic).
-
#tuner_gains ⇒ Array<Integer>
Get available tuner gains (not queryable via TCP, returns common values).
-
#tuner_name ⇒ String
Get human-readable tuner name.
-
#usb_strings ⇒ Hash
Get USB device strings (not available via TCP).
-
#write_eeprom(_data, _offset) ⇒ Object
Write EEPROM (not supported via TCP).
-
#xtal_freq ⇒ Array<Integer>
Get crystal frequencies (returns zeros - not readable via TCP).
Constructor Details
#initialize(host, port = DEFAULT_PORT, timeout: 10) ⇒ TcpClient
Create a new TCP client connection to an rtl_tcp server
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/rtlsdr/tcp_client.rb', line 71 def initialize(host, port = DEFAULT_PORT, timeout: 10) @host = host @port = port @timeout = timeout @socket = nil @streaming = false @mutex = Mutex.new # Cached configuration values (rtl_tcp doesn't support reading back) @center_freq = 0 @sample_rate = 0 @tuner_gain = 0 @freq_correction = 0 @direct_sampling = 0 @offset_tuning = false @agc_mode = false @test_mode = false @gain_mode_manual = false connect_to_server end |
Instance Attribute Details
#center_freq ⇒ Integer Also known as: frequency
Get center frequency
162 163 164 |
# File 'lib/rtlsdr/tcp_client.rb', line 162 def center_freq @center_freq end |
#direct_sampling ⇒ Integer
Get direct sampling mode
325 326 327 |
# File 'lib/rtlsdr/tcp_client.rb', line 325 def direct_sampling @direct_sampling end |
#freq_correction ⇒ Integer
Get frequency correction
178 179 180 |
# File 'lib/rtlsdr/tcp_client.rb', line 178 def freq_correction @freq_correction end |
#gain_count ⇒ Integer (readonly)
Returns Number of gain values supported.
62 63 64 |
# File 'lib/rtlsdr/tcp_client.rb', line 62 def gain_count @gain_count end |
#host ⇒ String (readonly)
Returns Remote host address.
56 57 58 |
# File 'lib/rtlsdr/tcp_client.rb', line 56 def host @host end |
#offset_tuning ⇒ Boolean
Get offset tuning mode
339 340 341 |
# File 'lib/rtlsdr/tcp_client.rb', line 339 def offset_tuning @offset_tuning end |
#port ⇒ Integer (readonly)
Returns Remote port number.
58 59 60 |
# File 'lib/rtlsdr/tcp_client.rb', line 58 def port @port end |
#sample_rate ⇒ Integer Also known as: samp_rate
Get current sample rate
275 276 277 |
# File 'lib/rtlsdr/tcp_client.rb', line 275 def sample_rate @sample_rate end |
#tuner_gain ⇒ Integer Also known as: gain
Get current tuner gain
216 217 218 |
# File 'lib/rtlsdr/tcp_client.rb', line 216 def tuner_gain @tuner_gain end |
#tuner_type ⇒ Integer (readonly)
Returns Tuner type from server.
60 61 62 |
# File 'lib/rtlsdr/tcp_client.rb', line 60 def tuner_type @tuner_type end |
Instance Method Details
#agc_mode! ⇒ Boolean
Enable AGC mode
310 311 312 |
# File 'lib/rtlsdr/tcp_client.rb', line 310 def agc_mode! self.agc_mode = true end |
#agc_mode=(enabled) ⇒ Object
Set AGC mode
301 302 303 304 305 |
# File 'lib/rtlsdr/tcp_client.rb', line 301 def agc_mode=(enabled) mode = enabled ? 1 : 0 send_command(CMD_SET_AGC_MODE, mode) @agc_mode = enabled end |
#auto_gain_mode! ⇒ Boolean
Enable automatic gain mode
240 241 242 |
# File 'lib/rtlsdr/tcp_client.rb', line 240 def auto_gain_mode! self.tuner_gain_mode = false end |
#bias_tee! ⇒ Boolean
Enable bias tee (alias)
375 376 377 |
# File 'lib/rtlsdr/tcp_client.rb', line 375 def bias_tee! enable_bias_tee end |
#bias_tee=(enabled) ⇒ Object
Set bias tee state
353 354 355 356 |
# File 'lib/rtlsdr/tcp_client.rb', line 353 def bias_tee=(enabled) mode = enabled ? 1 : 0 send_command(CMD_SET_BIAS_TEE, mode) end |
#cancel_async ⇒ void
This method returns an undefined value.
Cancel asynchronous reading
514 515 516 |
# File 'lib/rtlsdr/tcp_client.rb', line 514 def cancel_async @streaming = false end |
#close ⇒ void
This method returns an undefined value.
Close the connection
103 104 105 106 107 108 109 |
# File 'lib/rtlsdr/tcp_client.rb', line 103 def close return unless open? @streaming = false @socket.close @socket = nil end |
#closed? ⇒ Boolean
Check if connection is closed
114 115 116 |
# File 'lib/rtlsdr/tcp_client.rb', line 114 def closed? !open? end |
#configure(frequency: nil, sample_rate: nil, gain: nil, **options) ⇒ Object
Configuration shortcut
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 |
# File 'lib/rtlsdr/tcp_client.rb', line 533 def configure(frequency: nil, sample_rate: nil, gain: nil, **) self.center_freq = frequency if frequency self.sample_rate = sample_rate if sample_rate if gain manual_gain_mode! self.tuner_gain = gain end .each do |key, value| case key when :freq_correction then self.freq_correction = value when :agc_mode then self.agc_mode = value when :test_mode then self.test_mode = value when :bias_tee then self.bias_tee = value when :direct_sampling then self.direct_sampling = value when :offset_tuning then self.offset_tuning = value end end self end |
#disable_bias_tee ⇒ Boolean
Disable bias tee
368 369 370 |
# File 'lib/rtlsdr/tcp_client.rb', line 368 def disable_bias_tee self.bias_tee = false end |
#dump_eeprom ⇒ Object
Dump EEPROM (not supported via TCP)
403 404 405 |
# File 'lib/rtlsdr/tcp_client.rb', line 403 def dump_eeprom raise OperationFailedError, "EEPROM access not supported via TCP" end |
#each(samples_per_read: 1024) ⇒ Object
Enumerable interface
519 520 521 522 523 524 525 526 527 528 529 530 |
# File 'lib/rtlsdr/tcp_client.rb', line 519 def each(samples_per_read: 1024) return enum_for(:each, samples_per_read: samples_per_read) unless block_given? loop do samples = read_samples(samples_per_read) yield samples rescue StandardError => e break if e.is_a?(Interrupt) || e.is_a?(ConnectionError) raise end end |
#enable_bias_tee ⇒ Boolean
Enable bias tee
361 362 363 |
# File 'lib/rtlsdr/tcp_client.rb', line 361 def enable_bias_tee self.bias_tee = true end |
#info ⇒ Object
Device info as hash
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 |
# File 'lib/rtlsdr/tcp_client.rb', line 557 def info { host: @host, port: @port, name: name, usb_strings: usb_strings, tuner_type: @tuner_type, tuner_name: tuner_name, center_freq: center_freq, sample_rate: sample_rate, tuner_gain: tuner_gain, tuner_gains: tuner_gains, freq_correction: freq_correction, direct_sampling: direct_sampling, offset_tuning: offset_tuning } end |
#inspect ⇒ Object
String representation
576 577 578 579 580 581 582 583 |
# File 'lib/rtlsdr/tcp_client.rb', line 576 def inspect if open? "#<RTLSDR::TcpClient:#{object_id.to_s(16)} #{name} tuner=\"#{tuner_name}\" " \ "freq=#{center_freq}Hz rate=#{sample_rate}Hz>" else "#<RTLSDR::TcpClient:#{object_id.to_s(16)} #{name} closed>" end end |
#manual_gain_mode! ⇒ Boolean
Enable manual gain mode
233 234 235 |
# File 'lib/rtlsdr/tcp_client.rb', line 233 def manual_gain_mode! self.tuner_gain_mode = true end |
#name ⇒ Object
Device information
119 120 121 |
# File 'lib/rtlsdr/tcp_client.rb', line 119 def name "rtl_tcp://#{@host}:#{@port}" end |
#offset_tuning! ⇒ Boolean
Enable offset tuning
344 345 346 |
# File 'lib/rtlsdr/tcp_client.rb', line 344 def offset_tuning! self.offset_tuning = true end |
#open? ⇒ Boolean
Check if connected to server
96 97 98 |
# File 'lib/rtlsdr/tcp_client.rb', line 96 def open? !@socket.nil? && !@socket.closed? end |
#read_async(buffer_count: 15, buffer_length: 262_144) {|Array<Integer>| ... } ⇒ Thread
Read raw IQ data asynchronously
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 |
# File 'lib/rtlsdr/tcp_client.rb', line 467 def read_async(buffer_count: 15, buffer_length: 262_144, &block) raise ArgumentError, "Block required for async reading" unless block_given? raise OperationFailedError, "Already streaming" if streaming? _ = buffer_count # unused for TCP @streaming = true Thread.new do while @streaming && open? begin data = read_sync(buffer_length) block.call(data) rescue ConnectionError => e @streaming = false raise e unless e..include?("closed") rescue StandardError => e puts "Error in async callback: #{e.}" @streaming = false end end end end |
#read_eeprom(_offset, _length) ⇒ Object
Read EEPROM (not supported via TCP)
389 390 391 |
# File 'lib/rtlsdr/tcp_client.rb', line 389 def read_eeprom(_offset, _length) raise OperationFailedError, "EEPROM access not supported via TCP" end |
#read_samples(count = 1024) ⇒ Array<Complex>
Read complex samples synchronously
439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'lib/rtlsdr/tcp_client.rb', line 439 def read_samples(count = 1024) # RTL-SDR outputs 8-bit I/Q samples, so we need 2 bytes per complex sample data = read_sync(count * 2) # Convert to complex numbers (I + jQ) samples = [] (0...data.length).step(2) do |i| i_sample = (data[i] - 128) / 128.0 q_sample = (data[i + 1] - 128) / 128.0 samples << Complex(i_sample, q_sample) end samples end |
#read_samples_async(buffer_count: 15, buffer_length: 262_144) {|Array<Complex>| ... } ⇒ Thread
Read complex samples asynchronously
496 497 498 499 500 501 502 503 504 505 506 507 508 509 |
# File 'lib/rtlsdr/tcp_client.rb', line 496 def read_samples_async(buffer_count: 15, buffer_length: 262_144, &block) raise ArgumentError, "Block required for async reading" unless block_given? read_async(buffer_count: buffer_count, buffer_length: buffer_length) do |data| samples = [] (0...data.length).step(2) do |i| i_sample = (data[i] - 128) / 128.0 q_sample = (data[i + 1] - 128) / 128.0 samples << Complex(i_sample, q_sample) end block.call(samples) end end |
#read_sync(length) ⇒ Array<Integer>
Read raw IQ data synchronously
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/rtlsdr/tcp_client.rb', line 418 def read_sync(length) raise ConnectionError, "Not connected" unless open? data = +"" remaining = length while remaining.positive? chunk = @socket.read(remaining) raise ConnectionError, "Connection closed by server" if chunk.nil? || chunk.empty? data << chunk remaining -= chunk.bytesize end data.unpack("C*") end |
#reset_buffer ⇒ Object
Buffer reset (no-op for TCP)
408 409 410 411 |
# File 'lib/rtlsdr/tcp_client.rb', line 408 def reset_buffer # rtl_tcp doesn't have a reset buffer command # Data is continuously streamed end |
#set_bias_tee_gpio(_gpio, enabled) ⇒ Object
Bias tee GPIO not supported via TCP
380 381 382 |
# File 'lib/rtlsdr/tcp_client.rb', line 380 def set_bias_tee_gpio(_gpio, enabled) self.bias_tee = enabled end |
#set_tuner_if_gain(stage, gain) ⇒ Integer
Set IF gain for specific stage
249 250 251 252 253 |
# File 'lib/rtlsdr/tcp_client.rb', line 249 def set_tuner_if_gain(stage, gain) param = (stage << 16) | (gain & 0xFFFF) send_command(CMD_SET_IF_GAIN, param) gain end |
#set_xtal_freq(rtl_freq, tuner_freq) ⇒ Object
Crystal oscillator frequencies (not fully supported via TCP)
181 182 183 184 185 |
# File 'lib/rtlsdr/tcp_client.rb', line 181 def set_xtal_freq(rtl_freq, tuner_freq) send_command(CMD_SET_RTL_XTAL, rtl_freq) send_command(CMD_SET_TUNER_XTAL, tuner_freq) [rtl_freq, tuner_freq] end |
#streaming? ⇒ Boolean
Check if streaming
457 458 459 |
# File 'lib/rtlsdr/tcp_client.rb', line 457 def streaming? @streaming end |
#test_mode! ⇒ Boolean
Enable test mode
294 295 296 |
# File 'lib/rtlsdr/tcp_client.rb', line 294 def test_mode! self.test_mode = true end |
#test_mode=(enabled) ⇒ Object
Set test mode
285 286 287 288 289 |
# File 'lib/rtlsdr/tcp_client.rb', line 285 def test_mode=(enabled) mode = enabled ? 1 : 0 send_command(CMD_SET_TEST_MODE, mode) @test_mode = enabled end |
#tuner_bandwidth=(_bw) ⇒ Object
Set tuner bandwidth (not supported via rtl_tcp)
258 259 260 |
# File 'lib/rtlsdr/tcp_client.rb', line 258 def tuner_bandwidth=(_bw) # rtl_tcp doesn't support bandwidth command end |
#tuner_gain_mode=(manual) ⇒ Object
Set gain mode (manual or automatic)
224 225 226 227 228 |
# File 'lib/rtlsdr/tcp_client.rb', line 224 def tuner_gain_mode=(manual) mode = manual ? 1 : 0 send_command(CMD_SET_GAIN_MODE, mode) @gain_mode_manual = manual end |
#tuner_gains ⇒ Array<Integer>
Get available tuner gains (not queryable via TCP, returns common values)
199 200 201 202 203 |
# File 'lib/rtlsdr/tcp_client.rb', line 199 def tuner_gains # Return common R820T gains as a reasonable default [0, 9, 14, 27, 37, 77, 87, 125, 144, 157, 166, 197, 207, 229, 254, 280, 297, 328, 338, 364, 372, 386, 402, 421, 434, 439, 445, 480, 496] end |
#tuner_name ⇒ String
Get human-readable tuner name
137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/rtlsdr/tcp_client.rb', line 137 def tuner_name case @tuner_type when 1 then "Elonics E4000" when 2 then "Fitipower FC0012" when 3 then "Fitipower FC0013" when 4 then "FCI FC2580" when 5 then "Rafael Micro R820T" when 6 then "Rafael Micro R828D" else "Unknown" end end |
#usb_strings ⇒ Hash
Get USB device strings (not available via TCP)
126 127 128 129 130 131 132 |
# File 'lib/rtlsdr/tcp_client.rb', line 126 def usb_strings { manufacturer: "rtl_tcp", product: "Remote RTL-SDR", serial: "#{@host}:#{@port}" } end |
#write_eeprom(_data, _offset) ⇒ Object
Write EEPROM (not supported via TCP)
396 397 398 |
# File 'lib/rtlsdr/tcp_client.rb', line 396 def write_eeprom(_data, _offset) raise OperationFailedError, "EEPROM access not supported via TCP" end |
#xtal_freq ⇒ Array<Integer>
Get crystal frequencies (returns zeros - not readable via TCP)
190 191 192 |
# File 'lib/rtlsdr/tcp_client.rb', line 190 def xtal_freq [0, 0] end |