Class: PWN::SDR::Decoder::LoRa::PayloadIQ
- Inherits:
-
Object
- Object
- PWN::SDR::Decoder::LoRa::PayloadIQ
- Defined in:
- lib/pwn/sdr/decoder/lora.rb
Overview
Raw PHY receive path. Coding equations adapted from MIT LoRaPHY, commit 4fddd9a7b47682781c663608bfc4f196e4bf656d (Xu et al., ACM TOSN 2022, doi:10.1145/3546869). Full attribution/license: documentation/LoRaPHY-LICENSE.txt. Independent gr-lora_sdr vectors: spec/fixtures/sdr/lora/README.md. SF7-12 with explicit or configured implicit headers, optional CRC, LDRO and inverted IQ; no fractional CFO or clock-drift tracking. Keep acquisition and packet state together across arbitrary IQ chunks.
Instance Method Summary collapse
- #feed_iq(samples, rate: nil, &block) ⇒ Object
-
#initialize(rate:, bw:, sf:, sync_word: 0x12, implicit_header: false, payload_length: nil, cr: 1, crc: true, ldro: nil, invert_iq: false) ⇒ PayloadIQ
constructor
rubocop:disable Metrics/ClassLength.
Constructor Details
#initialize(rate:, bw:, sf:, sync_word: 0x12, implicit_header: false, payload_length: nil, cr: 1, crc: true, ldro: nil, invert_iq: false) ⇒ PayloadIQ
rubocop:disable Metrics/ClassLength
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/pwn/sdr/decoder/lora.rb', line 154 def initialize(rate:, bw:, sf:, sync_word: 0x12, implicit_header: false, payload_length: nil, cr: 1, crc: true, ldro: nil, invert_iq: false) # rubocop:disable Metrics/ParameterLists raise ArgumentError, 'LoRa requires SF7-12, BW125/250/500k and finite sample_rate >= BW' unless sf.is_a?(Integer) && SF_RANGE.cover?(sf) && [125_000, 250_000, 500_000].include?(bw) && rate.is_a?(Numeric) && rate.finite? && rate >= bw raise ArgumentError, 'Implicit LoRa requires payload_length 0..255 and cr 1..4' if implicit_header && (!payload_length.is_a?(Integer) || !(0..255).cover?(payload_length) || !(1..4).cover?(cr)) @implicit = implicit_header @length = payload_length @cr = cr @crc = crc @ldro = ldro.nil? ? (1 << sf).fdiv(bw) > 0.016 : ldro @invert = invert_iq @rate = rate @bw = bw @sf = sf raise ArgumentError, 'LoRa sync_word must be an integer byte' unless sync_word.is_a?(Integer) && (0..255).cover?(sync_word) @sync = sync_word @n = 1 << sf @buf = [] @scan = 8 * @n @reference = Array.new(@n) do |k| phase = Math::PI * k * ((k.to_f / @n) - 1) Complex(Math.cos(phase), Math.sin(phase)) end end |
Instance Method Details
#feed_iq(samples, rate: nil, &block) ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/pwn/sdr/decoder/lora.rb', line 180 def feed_iq(samples, rate: nil, &block) raise ArgumentError, 'LoRa sample rate changed' if rate && rate != @rate @buf.concat(resample_input(samples)) loop do if @packet break unless receive_packet(&block) else break if @scan + (3 * @n) > @buf.length / 2 acquire @scan += @n / 2 unless @packet # Bounded acquisition history, preserving six upchirps and sync. if !@packet && @scan > 24 * @n removed = @scan - (8 * @n) @buf.shift(removed * 2) @scan -= removed end end end end |