Module: PWN::SDR::Decoder::LoRa
- Defined in:
- lib/pwn/sdr/decoder/lora.rb
Overview
LoRa (Semtech CSS) true-air preamble/sync-word decoder.
I/Q resampled so fs = BW (default 125 kHz), one complex sample per chirp step. For each SF ∈ 7..12: dechirp with a reference down-chirp (DSP.cmul + PWN::FFI::FFTW.cfft), find ≥6 consecutive symbols whose FFT-argmax bin is identical (preamble), then read the two sync-word symbols and two SFD down-chirps. Emits bw_hz:, sync_word:, preamble_len:, cfo_bins: — the same metadata gr-lora / rtl-lora surface, no external binary.
Defined Under Namespace
Classes: DemodIQ
Constant Summary collapse
- DEFAULT_BW =
125_000- SF_RANGE =
(7..12)
- KNOWN_SYNC =
Public LoRaWAN sync = 0x34; private/Meshtastic default = 0x12.
{ 0x34 => 'LoRaWAN', 0x12 => 'private/RadioLib' }.freeze
Class Method Summary collapse
-
.authors ⇒ Object
- Author(s)
0day Inc.
-
.decode(opts = {}) ⇒ Object
- Supported Method Parameters
PWN::SDR::Decoder::LoRa.decode( freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq' ).
- .help ⇒ Object
- .parse_line(opts = {}) ⇒ Object
Class Method Details
.authors ⇒ Object
- Author(s)
0day Inc. support@0dayinc.com
170 171 172 |
# File 'lib/pwn/sdr/decoder/lora.rb', line 170 public_class_method def self. "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n" end |
.decode(opts = {}) ⇒ Object
- Supported Method Parameters
PWN::SDR::Decoder::LoRa.decode( freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq' )
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/pwn/sdr/decoder/lora.rb', line 135 public_class_method def self.decode(opts = {}) freq_obj = opts[:freq_obj] bw = (opts[:bw] || freq_obj[:lora_bw] || DEFAULT_BW).to_i rate = (opts[:sample_rate] || freq_obj[:iq_rate] || (bw * 4)).to_i PWN::SDR::Decoder::Base.run_iq( freq_obj: freq_obj, protocol: 'LoRa', sample_rate: rate, source: opts[:source], file: opts[:file], demod: DemodIQ.new(rate: rate, bw: bw), note: 'CSS 125–500 kHz — I/Q→resample_iq(fs=BW)→dechirp+FFTW per SF→preamble/sync-word.', describe: proc { |b| { modulation: 'CSS', bw_khz_assumed: bw / 1000, sf_estimate: b[:sf] } } ) end |
.help ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/pwn/sdr/decoder/lora.rb', line 174 public_class_method def self.help puts "USAGE (true-air I/Q via PWN::FFI + detector fallback): #{self}.decode( freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq', source: 'optional - :auto|:rtlsdr|:adalm_pluto|:file', file: 'optional - .cu8/.cs16 capture', bw: 'optional - LoRa BW Hz (default 125000)' ) #{self}.authors " end |
.parse_line(opts = {}) ⇒ Object
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/pwn/sdr/decoder/lora.rb', line 151 public_class_method def self.parse_line(opts = {}) line = opts[:line].to_s h = begin JSON.parse(line, symbolize_names: true) rescue StandardError { unparsed: line } end out = { protocol: 'LoRa' }.merge(h) bits = [] bits << "SF#{out[:sf]}" if out[:sf] bits << "BW#{out[:bw]}" if out[:bw] bits << "sync=#{out[:sync_word]}" if out[:sync_word] bits << out[:payload].to_s[0, 40] if out[:payload] out[:summary] = bits.empty? ? line[0, 120] : bits.join(' ') out end |