"""Common DSP patterns for block generation.""" COMMON_PATTERNS_PROMPT = ''' # Common GNU Radio DSP Patterns ## Signal Processing Primitives ### Moving Average Filter ```python def __init__(self, window_size=16): gr.sync_block.__init__(self, ...) self.window_size = window_size self._buffer = [] def work(self, input_items, output_items): for i in range(len(input_items[0])): self._buffer.append(input_items[0][i]) if len(self._buffer) > self.window_size: self._buffer.pop(0) output_items[0][i] = numpy.mean(self._buffer) return len(output_items[0]) ``` ### Exponential Moving Average (IIR) ```python def __init__(self, alpha=0.1): gr.sync_block.__init__(self, ...) self.alpha = alpha self._state = 0.0 def work(self, input_items, output_items): for i in range(len(input_items[0])): self._state = self.alpha * input_items[0][i] + (1 - self.alpha) * self._state output_items[0][i] = self._state return len(output_items[0]) ``` ### Peak Detector ```python def __init__(self, threshold=0.5, decay=0.99): gr.sync_block.__init__(self, ...) self.threshold = threshold self.decay = decay self._peak = 0.0 def work(self, input_items, output_items): for i in range(len(input_items[0])): sample = numpy.abs(input_items[0][i]) if sample > self._peak: self._peak = sample else: self._peak *= self.decay output_items[0][i] = 1.0 if sample > self._peak * self.threshold else 0.0 return len(output_items[0]) ``` ### Automatic Gain Control (AGC) ```python def __init__(self, target=1.0, attack=0.01, decay=0.001): gr.sync_block.__init__(self, in_sig=[numpy.complex64], out_sig=[numpy.complex64], ...) self.target = target self.attack = attack self.decay = decay self._gain = 1.0 def work(self, input_items, output_items): for i in range(len(input_items[0])): sample = input_items[0][i] mag = numpy.abs(sample) error = self.target - mag * self._gain if error > 0: self._gain += self.attack * error else: self._gain += self.decay * error self._gain = max(0.001, min(1000, self._gain)) output_items[0][i] = sample * self._gain return len(output_items[0]) ``` ## Frequency Domain Operations ### Simple FFT Magnitude ```python def __init__(self, fft_size=1024): gr.sync_block.__init__(self, in_sig=[(numpy.complex64, fft_size)], out_sig=[(numpy.float32, fft_size)], ...) self.fft_size = fft_size def work(self, input_items, output_items): for i in range(len(input_items[0])): spectrum = numpy.fft.fftshift(numpy.fft.fft(input_items[0][i])) output_items[0][i] = numpy.abs(spectrum) return len(output_items[0]) ``` ### Power Spectral Density ```python def __init__(self, fft_size=1024, avg_count=10): gr.sync_block.__init__(self, in_sig=[(numpy.complex64, fft_size)], out_sig=[(numpy.float32, fft_size)], ...) self.fft_size = fft_size self.avg_count = avg_count self._psd_sum = numpy.zeros(fft_size) self._count = 0 def work(self, input_items, output_items): for i in range(len(input_items[0])): spectrum = numpy.fft.fftshift(numpy.fft.fft(input_items[0][i])) psd = numpy.abs(spectrum) ** 2 self._psd_sum += psd self._count += 1 if self._count >= self.avg_count: output_items[0][i] = 10 * numpy.log10(self._psd_sum / self._count + 1e-10) self._psd_sum = numpy.zeros(self.fft_size) self._count = 0 else: output_items[0][i] = numpy.zeros(self.fft_size) return len(output_items[0]) ``` ## Timing and Synchronization ### Simple Clock Recovery (Zerocrossing) ```python def __init__(self, samples_per_symbol=8): gr.basic_block.__init__(self, in_sig=[numpy.float32], out_sig=[numpy.float32], ...) self.sps = samples_per_symbol self._phase = 0.0 self._last_sample = 0.0 def forecast(self, noutput_items, ninputs): return [int(noutput_items * self.sps) + 1] def general_work(self, input_items, output_items): data = input_items[0] out = output_items[0] n_out = 0 n_in = 0 while n_out < len(out) and n_in < len(data) - 1: # Detect zero crossing for timing adjustment if data[n_in] * self._last_sample < 0: # Adjust phase based on crossing position cross_pos = -self._last_sample / (data[n_in] - self._last_sample) self._phase += 0.1 * (cross_pos - 0.5) # Output at symbol center self._phase += 1.0 / self.sps if self._phase >= 1.0: self._phase -= 1.0 out[n_out] = data[n_in] n_out += 1 self._last_sample = data[n_in] n_in += 1 self.consume_each(n_in) return n_out ``` ## Packet Detection ### Preamble Correlator ```python def __init__(self, preamble="10101010"): gr.sync_block.__init__(self, in_sig=[numpy.float32], out_sig=[numpy.float32], ...) self.preamble = numpy.array([1 if b == '1' else -1 for b in preamble], dtype=numpy.float32) self._buffer = numpy.zeros(len(preamble)) def work(self, input_items, output_items): for i in range(len(input_items[0])): self._buffer = numpy.roll(self._buffer, -1) self._buffer[-1] = 1.0 if input_items[0][i] > 0 else -1.0 output_items[0][i] = numpy.dot(self._buffer, self.preamble) / len(self.preamble) return len(output_items[0]) ``` ### Threshold with Hysteresis ```python def __init__(self, high_thresh=0.7, low_thresh=0.3): gr.sync_block.__init__(self, ...) self.high_thresh = high_thresh self.low_thresh = low_thresh self._state = False def work(self, input_items, output_items): for i in range(len(input_items[0])): sample = input_items[0][i] if self._state: if sample < self.low_thresh: self._state = False else: if sample > self.high_thresh: self._state = True output_items[0][i] = 1.0 if self._state else 0.0 return len(output_items[0]) ``` ## Data Transformation ### Byte Unpacker (1 byte → 8 bits) ```python def __init__(self): gr.basic_block.__init__(self, in_sig=[numpy.uint8], out_sig=[numpy.uint8], ...) def forecast(self, noutput_items, ninputs): return [(noutput_items + 7) // 8] def general_work(self, input_items, output_items): n_bytes = min(len(input_items[0]), len(output_items[0]) // 8) if n_bytes == 0: return 0 for i in range(n_bytes): byte = input_items[0][i] for bit in range(8): output_items[0][i * 8 + bit] = (byte >> (7 - bit)) & 1 self.consume_each(n_bytes) return n_bytes * 8 ``` ### Byte Packer (8 bits → 1 byte) ```python def __init__(self): gr.basic_block.__init__(self, in_sig=[numpy.uint8], out_sig=[numpy.uint8], ...) def forecast(self, noutput_items, ninputs): return [noutput_items * 8] def general_work(self, input_items, output_items): n_bits = len(input_items[0]) n_bytes = min(n_bits // 8, len(output_items[0])) if n_bytes == 0: return 0 for i in range(n_bytes): byte = 0 for bit in range(8): if input_items[0][i * 8 + bit]: byte |= (1 << (7 - bit)) output_items[0][i] = byte self.consume_each(n_bytes * 8) return n_bytes ``` ### Manchester Decoder ```python def __init__(self): gr.basic_block.__init__(self, in_sig=[numpy.uint8], # Binary symbols out_sig=[numpy.uint8], ...) def forecast(self, noutput_items, ninputs): return [noutput_items * 2] def general_work(self, input_items, output_items): data = input_items[0] n_pairs = min(len(data) // 2, len(output_items[0])) if n_pairs == 0: return 0 for i in range(n_pairs): first = data[i * 2] second = data[i * 2 + 1] # Manchester: 0→1 = 0, 1→0 = 1 if first == 0 and second == 1: output_items[0][i] = 0 elif first == 1 and second == 0: output_items[0][i] = 1 else: # Invalid Manchester encoding output_items[0][i] = 255 self.consume_each(n_pairs * 2) return n_pairs ``` ## Performance Tips 1. **Vectorize with numpy** - Avoid Python loops where possible ```python # Slow for i in range(len(data)): output[i] = data[i] * gain # Fast output[:] = data * gain ``` 2. **Pre-allocate buffers** - Create arrays once in __init__ ```python def __init__(self): self._buffer = numpy.zeros(1024) ``` 3. **Use in-place operations** - Modify arrays without copies ```python data *= gain # In-place data = data * gain # Creates copy ``` 4. **Minimize state** - Less state = better cache performance 5. **Profile first** - Use `%timeit` or cProfile to find bottlenecks '''