#!/usr/bin/env ruby # frozen_string_literal: true require 'pwn' require 'json' require 'optparse' opts = {} OptionParser.new do |options| options.banner = "USAGE: #{File.basename($PROGRAM_NAME)} [opts] " options.on('-aPROFILE', '--assume-profile=PROFILE', '') do |l| opts[:list_profiles] = l end options.on('-tFREQ', '--target-freq=FREQ', '') do |e| opts[:target_freq] = e end options.on('-sFREQ', '--start-freq=FREQ', '') do |s| opts[:start_freq] = s end options.on('-hHOST', '--host=HOST', '') do |h| opts[:host] = h end options.on('-pPORT', '--port=PORT', '') do |p| opts[:port] = p end options.on('-AFLOAT', '--audio-gain=FLOAT', '') do |a| opts[:audio_gain_db] = a end options.on('-bHZ', '--bandwidth=HZ', '') do |b| opts[:bandwidth] = b end options.on('-DMODE', '--demodulator-mode=MODE', '') do |d| opts[:demodulator_mode] = d end options.on('-PINT', '--precision=INT', '') do |p| opts[:precision] = p end options.on('-SFLOAT', '--strength-lock=FLOAT', '') do |s| opts[:strength_lock] = s end options.on('-LFLOAT', '--lock-freq-duration=FLOAT', '') do |l| opts[:lock_freq_duration] = l end options.on('-QFLOAT', '--squelch=FLOAT', '') do |q| opts[:squelch] = q end options.on('-RFLOAT', '--rf-gain=FLOAT', '') do |r| opts[:rf_gain] = r end options.on('-IFLOAT', '--intermediate-gain=FLOAT', '') do |i| opts[:intermediate_gain] = i end options.on('-BFLOAT', '--basedband-gain=FLOAT', '') do |b| opts[:baseband_gain] = b end end.parse! if opts.empty? puts `#{File.basename($PROGRAM_NAME)} --help` exit 1 end begin pwn_provider = 'ruby-gem' pwn_provider = ENV.fetch('PWN_PROVIDER') if ENV.keys.any? { |s| s == 'PWN_PROVIDER' } list_profiles = opts[:list_profiles] if list_profiles profiles_available = PWN::Plugins::GQRX.list_profiles puts JSON.pretty_generate(profiles_available) exit 0 end profile = opts[:profile] opts = PWN::Plugins::GQRX.assume_profile(profile: profile) unless profile.nil? target_freq = opts[:target_freq] target_freq = target_freq.to_s.delete('.') unless target_freq.nil? target_freq = target_freq.to_i raise 'ERROR: --assume-profile || --target-freq is required.' if target_freq.zero? && profile.nil? host = opts[:host] port = opts[:port] puts "Connecting to GQRX at #{host}:#{port}..." gqrx_sock = PWN::Plugins::GQRX.connect(target: host, port: port) start_freq = opts[:start_freq] start_freq = start_freq.to_s.delete('.') unless start_freq.nil? start_freq = start_freq.to_i if start_freq.zero? start_freq = PWN::Plugins::GQRX.gqrx_cmd( gqrx_sock: gqrx_sock, cmd: 'f', resp_ok: 'RPRT 0' ).to_i end demodulator_mode = opts[:demodulator_mode] ||= 'WFM_ST' puts "Demodulator Mode: #{demodulator_mode}" # demodulator_mode.upcase! if opts[:demodulator_mode] demodulator_modes = %i[OFF RAW AM FM WFM WFM_ST WFM_ST_OIRT LSB USB CW CWL CWU] raise "ERROR: Invalid demodulator mode: #{demodulator_mode}" unless demodulator_modes.include?(demodulator_mode) bandwidth = opts[:bandwidth] ||= '200.000' puts "Setting demodulator mode to #{demodulator_mode} and bandwidth to #{bandwidth}..." bandwidth = bandwidth.to_s.delete('.').to_i unless bandwidth.nil? demod_resp = PWN::Plugins::GQRX.gqrx_cmd( gqrx_sock: gqrx_sock, cmd: "M #{demodulator_mode} #{bandwidth}", resp_ok: 'RPRT 0' ) audio_gain_db = opts[:audio_gain_db] ||= 1.0 audio_gain_db = audio_gain_db.to_f audio_gain_db_resp = PWN::Plugins::GQRX.gqrx_cmd( gqrx_sock: gqrx_sock, cmd: "L AF #{audio_gain_db}", resp_ok: 'RPRT 0' ) squelch = opts[:squelch] ||= -63.0 squelch = squelch.to_f squelch_resp = PWN::Plugins::GQRX.gqrx_cmd( gqrx_sock: gqrx_sock, cmd: "L SQL #{squelch}", resp_ok: 'RPRT 0' ) precision = opts[:precision] ||= 5 precision = precision.to_i raise "ERROR: Invalid precision: #{precision}" unless (1..12).include?(precision) lock_freq_duration = opts[:lock_freq_duration] ||= 0.5 lock_freq_duration = lock_freq_duration.to_f strength_lock = opts[:strength_lock] ||= -60.0 strength_lock = strength_lock.to_f rf_gain = opts[:rf_gain] ||= 0.0 rf_gain = rf_gain.to_f rf_gain_resp = PWN::Plugins::GQRX.gqrx_cmd( gqrx_sock: gqrx_sock, cmd: "L RF_GAIN #{rf_gain}", resp_ok: 'RPRT 0' ) intermediate_gain = opts[:intermediate_gain] ||= 32.0 intermediate_gain = intermediate_gain.to_f intermediate_resp = PWN::Plugins::GQRX.gqrx_cmd( gqrx_sock: gqrx_sock, cmd: "L IF_GAIN #{intermediate_gain}", resp_ok: 'RPRT 0' ) baseband_gain = opts[:baseband_gain] ||= 10.0 baseband_gain = baseband_gain.to_f baseband_resp = PWN::Plugins::GQRX.gqrx_cmd( gqrx_sock: gqrx_sock, cmd: "L BB_GAIN #{baseband_gain}", 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}\n\n\n" PWN::Plugins::GQRX.scan_range( gqrx_sock: gqrx_sock, demodulator_mode: demodulator_mode, bandwidth: bandwidth, start_freq: start_freq, target_freq: target_freq, precision: precision, lock_freq_duration: lock_freq_duration, strength_lock: strength_lock, squelch: squelch ) puts 'Scan Complete.' rescue StandardError => e raise e rescue Interrupt, SystemExit puts "\nGoodbye." ensure gqrx_sock = PWN::Plugins::GQRX.disconnect(gqrx_sock: gqrx_sock) unless gqrx_sock.nil? end