bin/pwn_gqrx_scanner in pwn-0.5.52 vs bin/pwn_gqrx_scanner in pwn-0.5.53
- old
+ new
@@ -13,121 +13,225 @@
options.on('-tFREQ', '--target-freq=FREQ', '<Required - Frequency to Conclude Scanning (e.g. 900000000 == 900 mHz>') do |e|
opts[:target_freq] = e
end
+ options.on('-sFREQ', '--start-freq=FREQ', '<Optional - Frequency to Set when Scanning Begins (Defaults to last known frequency)>') do |s|
+ opts[:start_freq] = s
+ end
+
options.on('-hHOST', '--host=HOST', '<Optional - GQRX Host (Defaults to 127.0.0.1)>') do |h|
opts[:host] = h
end
options.on('-pPORT', '--port=PORT', '<Optional - GQRX Port (Defaults to 7356)>') do |p|
opts[:port] = p
end
- options.on('-dMODE', '--demodulator-mode=MODE', '<Optional - Set Demodulator ModeOFF | RAW | AM | FM | WFM | WFM_ST | WFM_ST_OIRT | LSB |USB | CW | CWL | CWU (Defaults to AM)>') do |d|
+ options.on('-AFLOAT', '--audio-gain=FLOAT', '<Optional - Set audio gain -80.0 to 50.0 (Defaults to 1.0)>') do |a|
+ opts[:audio_gain_db] = a
+ end
+
+ options.on('-BHZ', '--bandwidth=HZ', '<Optional - Set Bandwidth (Defaults to 240.000)>') do |b|
+ opts[:bandwidth] = b
+ end
+
+ options.on('-DMODE', '--demodulator-mode=MODE', '<Optional - Set Demodulator ModeOFF | RAW | AM | FM | WFM | WFM_ST | WFM_ST_OIRT | LSB |USB | CW | CWL | CWU (Defaults to WFM_ST)>') do |d|
opts[:demodulator_mode] = d
end
- options.on('-sFREQ', '--start-freq=FREQ', '<Optional - Frequency to Set when Scanning Begins (Defaults to last known frequency)>') do |s|
- opts[:start_freq] = s
+ options.on('-LFLOAT', '--lock-freq-duration=FLOAT', '<Optional - Duration to lock onto Freqency when Strength < --strength value (Defaults to 3)>') do |l|
+ opts[:lock_on_freq_duration] = l
end
- options.on('-qFLOAT', '--squelch=FLOAT', '<Optional - Squelch Threshold (Defaults to -63)>') do |q|
+ options.on('-QFLOAT', '--squelch=FLOAT', '<Optional - Squelch Threshold -150 to 0(Defaults to -63.0)>') do |q|
opts[:squelch] = q
end
- options.on('-PPLACE', '--precision=PLACE', '<Optional - Precision of Frequency 1-12 (Defaults to 3)>') do |p|
+ options.on('-PINT', '--precision=INT', '<Optional - Precision of Frequency 1-12 (Defaults to 6)>') do |p|
opts[:precision] = p
end
- options.on('-LFLOAT', '--lock-on-freq-duration=FLOAT', '<Optional - Duration to lock onto Freqency when Strength < --strength value (Defaults to -63)>') do |l|
- opts[:lock_on_freq_duration] = l
- end
-
- options.on('-SFLOAT', '--strength=FLOAT', '<Optional - Strength to trigger sleep (Defaults to 0)>') do |s|
+ options.on('-SFLOAT', '--strength=FLOAT', '<Optional - Strength to trigger sleep (Defaults to -63.3)>') do |s|
opts[:strength] = s
end
end.parse!
if opts.empty?
puts `#{$PROGRAM_NAME} --help`
exit 1
end
def gqrx_cmd(opts = {})
- # f - Get frequency [Hz]
- # F - Set frequency [Hz]
- # m - Get demodulator mode
- # M - Set demodulator mode (OFF, RAW, AM, FM, WFM, WFM_ST,
- # WFM_ST_OIRT, LSB, USB, CW, CWL, CWU)
- # l STRENGTH - Get signal strength [dBFS]
- # l SQL - Get squelch threshold [dBFS]
- # L SQL <sql> - Set squelch threshold to <sql> [dBFS]
- # u RECORD - Get status of audio recorder
- # U RECORD <status> - Set status of audio recorder to <status>
- # c - Close connection
- # AOS - Acquisition of signal (AOS) event, start audio recording
- # LOS - Loss of signal (LOS) event, stop audio recording
- # \dump_state - Dump state (only usable for compatibility)
gqrx_sock = opts[:gqrx_sock]
cmd = opts[:cmd]
+ resp_ok = opts[:resp_ok]
+ # Most Recent GQRX Command Set:
+ # https://raw.githubusercontent.com/gqrx-sdr/gqrx/master/resources/remote-control.txt
+ # Supported commands:
+ # f Get frequency [Hz]
+ # F <frequency> Set frequency [Hz]
+ # m Get demodulator mode and passband
+ # M <mode> [passband]
+ # Set demodulator mode and passband [Hz]
+ # Passing a '?' as the first argument instead of 'mode' will return
+ # a space separated list of radio backend supported modes.
+ # l|L ?
+ # Get a space separated list of settings available for reading (l) or writing (L).
+ # l STRENGTH
+ # Get signal strength [dBFS]
+ # l SQL
+ # Get squelch threshold [dBFS]
+ # L SQL <sql>
+ # Set squelch threshold to <sql> [dBFS]
+ # l AF
+ # Get audio gain [dB]
+ # L AF <gain>
+ # Set audio gain to <gain> [dB]
+ # l <gain_name>_GAIN
+ # Get the value of the gain setting with the name <gain_name>
+ # L <gain_name>_GAIN <value>
+ # Set the value of the gain setting with the name <gain_name> to <value>
+ # p RDS_PI
+ # Get the RDS PI code (in hexadecimal). Returns 0000 if not applicable.
+ # u RECORD
+ # Get status of audio recorder
+ # U RECORD <status>
+ # Set status of audio recorder to <status>
+ # u DSP
+ # Get DSP (SDR receiver) status
+ # U DSP <status>
+ # Set DSP (SDR receiver) status to <status>
+ # u RDS
+ # Get RDS decoder to <status>. Only functions in WFM mode.
+ # U RDS <status>
+ # Set RDS decoder to <status>. Only functions in WFM mode.
+ # q|Q
+ # Close connection
+ # AOS
+ # Acquisition of signal (AOS) event, start audio recording
+ # LOS
+ # Loss of signal (LOS) event, stop audio recording
+ # LNB_LO [frequency]
+ # If frequency [Hz] is specified set the LNB LO frequency used for
+ # display. Otherwise print the current LNB LO frequency [Hz].
+ # \chk_vfo
+ # Get VFO option status (only usable for hamlib compatibility)
+ # \dump_state
+ # Dump state (only usable for hamlib compatibility)
+ # \get_powerstat
+ # Get power status (only usable for hamlib compatibility)
+ # v
+ # Get 'VFO' (only usable for hamlib compatibility)
+ # V
+ # Set 'VFO' (only usable for hamlib compatibility)
+ # s
+ # Get 'Split' mode (only usable for hamlib compatibility)
+ # S
+ # Set 'Split' mode (only usable for hamlib compatibility)
+ # _
+ # Get version
+ #
+ # Reply:
+ # RPRT 0
+ # Command successful
+ # RPRT 1
+ # Command failed
+
gqrx_sock.write("#{cmd}\n")
- does_respond = gqrx_sock.wait_readable
- gqrx_sock.readline.chomp if does_respond
+ response = []
+ got_freq = false
+ # Read all responses from gqrx_sock.write
+ timeout = 0.01 if timeout.nil?
+ begin
+ response.push(gqrx_sock.readline.chomp) while gqrx_sock.wait_readable(timeout)
+ raise IOError if response.empty?
+ rescue IOError
+ timeout += 0.001
+ retry
+ end
+
+ got_int_value_in_resp = true if response.first.to_i.positive?
+ response = response.first if response.length == 1
+
+ raise "ERROR!!! Command: #{cmd} Expected Resp: #{resp_ok}, Got: #{response}" if resp_ok && response != resp_ok
+
+ if got_int_value_in_resp
+ fixed_len_freq = format('%0.12d', response.to_i)
+ freq_segments = fixed_len_freq.scan(/.{3}/)
+ first_non_zero_index = freq_segments.index { |s| s.to_i.positive? }
+ freq_segments = freq_segments[first_non_zero_index..-1]
+ freq_segments[0] = freq_segments.first.to_i.to_s
+ response = freq_segments.join('.')
+ end
+
+ # DEBUG
+ # puts response.inspect
+ # puts response.length
+
+ response
end
def init_freq(opts = {})
gqrx_sock = opts[:gqrx_sock]
+ demodulator_mode = opts[:demodulator_mode]
+ bandwidth = opts[:bandwidth]
this_freq = opts[:this_freq]
lock_on_freq_duration = opts[:lock_on_freq_duration]
strength = opts[:strength]
- change_frreq_resp = gqrx_cmd(
+ demod_n_passband = gqrx_cmd(
gqrx_sock: gqrx_sock,
- cmd: "F #{this_freq}"
+ cmd: 'm'
)
- raise "ERROR: Failed to set frequency to #{this_freq}" unless change_frreq_resp == 'RPRT 0'
- raw_freq = gqrx_cmd(
+ change_freq_resp = gqrx_cmd(
gqrx_sock: gqrx_sock,
+ cmd: "F #{this_freq}",
+ resp_ok: 'RPRT 0'
+ )
+
+ current_freq = gqrx_cmd(
+ gqrx_sock: gqrx_sock,
cmd: 'f'
)
- # Split the response from NNNNNNNNN
- # to NNN.NNN.NNN
- this_freq = raw_freq.to_s.chars.insert(-4, '.').insert(-8, '.').join
-
- strength_resp = gqrx_cmd(
+ current_strength = gqrx_cmd(
gqrx_sock: gqrx_sock,
cmd: 'l STRENGTH'
- )
- current_strength = strength_resp.to_f
+ ).to_f
- squelch_resp = gqrx_cmd(
+ current_squelch = gqrx_cmd(
gqrx_sock: gqrx_sock,
cmd: 'l SQL'
- )
- current_squelch = squelch_resp.to_f
+ ).to_f
+ audio_gain_db = gqrx_cmd(
+ gqrx_sock: gqrx_sock,
+ cmd: 'l AF'
+ ).to_f
+
init_freq_hash = {
- frequency: this_freq,
+ demod_mode_n_passband: demod_n_passband,
+ frequency: current_freq,
+ audio_gain_db: audio_gain_db,
+ squelch: current_squelch,
strength: current_strength,
- squelch: current_squelch
+ trigger_lock_on_freq_strength: strength
}
- # puts JSON.parse(init_freq_hash)
- puts init_freq_hash.to_json
+ puts JSON.pretty_generate(init_freq_hash)
sleep lock_on_freq_duration if current_strength < strength
- # This helps level out strength
- sleep 0.03
init_freq_hash
end
def scan_range(opts = {})
gqrx_sock = opts[:gqrx_sock]
+ demodulator_mode = opts[:demodulator_mode]
+ bandwidth = opts[:bandwidth]
start_freq = opts[:start_freq]
target_freq = opts[:target_freq]
precision = opts[:precision]
lock_on_freq_duration = opts[:lock_on_freq_duration]
strength = opts[:strength]
@@ -137,21 +241,24 @@
start_freq.downto(target_freq) do |this_freq|
next unless (i % multiplier).zero?
init_freq_hash = init_freq(
gqrx_sock: gqrx_sock,
+ demodulator_mode: demodulator_mode,
+ bandwidth: bandwidth,
this_freq: this_freq,
lock_on_freq_duration: lock_on_freq_duration,
strength: strength
)
- puts init_freq_hash.to_json
end
else
this_freq = start_freq
while this_freq <= target_freq
init_freq_hash = init_freq(
gqrx_sock: gqrx_sock,
+ demodulator_mode: demodulator_mode,
+ bandwidth: bandwidth,
this_freq: this_freq,
lock_on_freq_duration: lock_on_freq_duration,
strength: strength
)
@@ -163,57 +270,82 @@
begin
pwn_provider = 'ruby-gem'
pwn_provider = ENV.fetch('PWN_PROVIDER') if ENV.keys.any? { |s| s == 'PWN_PROVIDER' }
target_freq = opts[:target_freq]
- target_freq = target_freq.to_s.delete('.').to_i unless target_freq.nil?
- raise 'ERROR: Invalid end frequency' if target_freq.nil?
+ target_freq = target_freq.to_s.delete('.') unless target_freq.nil?
+ target_freq = target_freq.to_i
+ raise "ERROR: Invalid target frequency #{target_freq}" if target_freq.zero?
host = opts[:host] ||= '127.0.0.1'
port = opts[:port] ||= 7356
puts "Connecting to GQRX at #{host}:#{port}..."
gqrx_sock = PWN::Plugins::Sock.connect(target: host, port: port)
- demodulator_mode = opts[:demodulator_mode] ||= 'AM'
+ start_freq = opts[:start_freq]
+ start_freq = start_freq.to_s.delete('.') unless start_freq.nil?
+ start_freq = start_freq.to_i
+ start_freq = gqrx_cmd(gqrx_sock: gqrx_sock, cmd: 'f', resp_ok: 'RPRT 0').to_i if start_freq.zero?
+
+ demodulator_mode = opts[:demodulator_mode] ||= 'WFM_ST'
demodulator_mode.upcase!
raise "ERROR: Invalid demodulator mode: #{demodulator_mode}" unless %w[OFF RAW AM FM WFM WFM_ST WFM_ST_OIRT LSB USB CW CWL CWU].include?(demodulator_mode)
- puts "Setting demodulator mode to #{demodulator_mode}..."
- demod_resp = gqrx_cmd(gqrx_sock: gqrx_sock, cmd: "M #{demodulator_mode}")
- raise "ERROR: Failed to set demodulator mode to #{demodulator_mode}" unless demod_resp == 'RPRT 0'
+ bandwidth = opts[:bandwidth] ||= '240.000'
- start_freq = opts[:start_freq]
- start_freq = start_freq.to_s.delete('.').to_i unless start_freq.nil?
- start_freq = gqrx_cmd(gqrx_sock: gqrx_sock, cmd: 'f').to_i if start_freq.nil?
+ puts "Setting demodulator mode to #{demodulator_mode} and bandwidth to #{bandwidth}..."
+ bandwidth = bandwidth.to_s.delete('.').to_i unless bandwidth.nil?
+ demod_resp = gqrx_cmd(
+ gqrx_sock: gqrx_sock,
+ cmd: "M #{demodulator_mode} #{bandwidth}",
+ resp_ok: 'RPRT 0'
+ )
- squelch = opts[:squelch] ||= -63
+ squelch = opts[:squelch] ||= -63.0
squelch = squelch.to_f
- squelch_resp = gqrx_cmd(gqrx_sock: gqrx_sock, cmd: "L SQL #{squelch}")
- raise "ERROR: Failed to set squelch to #{squelch}" unless squelch_resp == 'RPRT 0'
+ squelch_resp = gqrx_cmd(
+ gqrx_sock: gqrx_sock,
+ cmd: "L SQL #{squelch}",
+ resp_ok: 'RPRT 0'
+ )
- precision = opts[:precision] ||= 3
+ precision = opts[:precision] ||= 6
precision = precision.to_i
raise "ERROR: Invalid precision: #{precision}" unless (1..12).include?(precision)
- lock_on_freq_duration = opts[:lock_on_freq_duration] ||= 0
+ lock_on_freq_duration = opts[:lock_on_freq_duration] ||= 3
lock_on_freq_duration = lock_on_freq_duration.to_f
- strength = opts[:strength] ||= -63
+ strength = opts[:strength] ||= -63.3
strength = strength.to_f
- puts "Scanning from #{start_freq} to #{target_freq}..."
+ audio_gain_db = opts[:audio_gain_db] ||= 1.0
+ audio_gain_db = audio_gain_db.to_f
+ audio_gain_db_resp = gqrx_cmd(
+ gqrx_sock: gqrx_sock,
+ cmd: "L AF #{audio_gain_db}",
+ resp_ok: 'RPRT 0'
+ )
+ s_freq_pretty = start_freq.to_s.chars.insert(-4, '.').insert(-8, '.').join
+ t_freq_pretty = target_freq.to_s.chars.insert(-4, '.').insert(-8, '.').join
+ puts "*** Scanning from #{s_freq_pretty} to #{t_freq_pretty}"
+
scan_range(
gqrx_sock: gqrx_sock,
+ demodulator_mode: demodulator_mode,
start_freq: start_freq,
target_freq: target_freq,
precision: precision,
lock_on_freq_duration: lock_on_freq_duration,
- strength: strength
+ strength: strength,
+ squelch: squelch
)
-rescue SystemExit, Interrupt
+ puts 'Scan Complete.'
+rescue StandardError => e
+ raise e
+rescue Interrupt, SystemExit
puts "\nGoodbye."
ensure
- resp = gqrx_cmd(gqrx_sock: gqrx_sock, cmd: 'c')
gqrx_sock = PWN::Plugins::Sock.disconnect(sock_obj: gqrx_sock)
end