Burst Detect x16 (bd_x16)
16-channel folded burst detector with per-channel squelch — the first worker of the folded receive chain.
Function
bd_x16 consumes the channelizer/decimate3 positional stream: one
16-sample complex_short_timed_sample message per frame (SOM on position 0,
EOM on position 15), where the sample at position k belongs to channel k.
For each channel it runs the same IIR energy estimator as
dsp.dsp.burst_detect (energy <- energy - (energy >> decay_shift) +
|IQ|^2; burst = energy >= threshold), time-shared across all 16 channels
with a 16-entry energy RAM. It then applies squelch (Option A): active
channels pass through unchanged, while channels below threshold output exact zero
IQ.
This restores the per-channel energy gate that the folded front end
(cr_x16/ted_x16/dsd_x16/fs_x16) dropped relative to the
single-channel msk_demod monolith. Without it, on a real ADC the inactive
channelizer bins carry noise and leakage, so the timing loop wanders and frame
sync false-locks on quiet channels. By emitting exact zeros on quiet channels —
the condition the chain already handles cleanly — cr_x16’s 4th-power error
and ted_x16’s Gardner error stay zero on quiet channels, and fs_x16 sees
no correlation. No loop-freeze threading is needed; re-acquisition on the next
burst rides the preamble.
Place it immediately before cr_x16 and instantiate one per channelizer port
(4× for 64 channels).
Interface
<?xml version='1.0' encoding='utf-8'?>
<!-- bd_x16: 16-channel FOLDED burst detector + per-channel SQUELCH.
Restores the per-channel energy gate that the folded front-end
(cr_x16/ted_x16/dsd_x16/fs_x16) dropped relative to the msk_demod monolith
(where burst_detect drove demod_freeze = not burst_det). Without it, on a
REAL ADC the inactive channelizer bins carry noise + leakage, so ted's
timing loop wanders and fs false-locks on every quiet channel.
Consumes the channelizer/decimate3 POSITIONAL stream: one 16-sample
`complex_short_timed_sample` message per frame (SOM on position 0, EOM on
position 15); the sample at position k belongs to channel k. Per channel it
runs the same IIR energy estimator as dsp.dsp.burst_detect
(energy <- energy - (energy >> decay_shift) + |IQ|^2; burst = energy >=
threshold), time-shared across all 16 channels with a 16-entry energy RAM.
OPTION A (squelch): emits an identically-framed positional stream, passing
ACTIVE channels through unchanged and outputting ZERO IQ for channels below
threshold. Downstream then sees exact zeros on quiet channels (the same
condition the chain already handles cleanly), so cr's 4th-power error and
ted's Gardner error are 0 on quiet channels (omega stays nominal, no wander)
and fs sees zero soft (no correlation, no false lock). No loop-freeze
threading is needed; re-acquisition on the next burst rides the preamble.
Place it right before cr_x16:
channelizer -> dec3 -> bd_x16 -> cr_x16 -> ted -> dsd -> fs -> dec16
Instantiate 4x (one per channelizer port) for 64 channels. -->
<ComponentSpec>
<property name='init' type='bool' writable='true' default='false'
description="Pulse high to reset all 16 per-channel energy/burst states"/>
<property name='threshold' type='ulong' writable='true' default='15000'
description="Energy threshold to declare a burst (per dsp.dsp.burst_detect; ~15000 detects signal well above the noise floor for I=Q~2000, decay_shift=8)"/>
<property name='decay_shift' type='uchar' writable='true' default='8'
description="IIR decay = 2^decay_shift samples; steady energy ~= |IQ|^2 * 2^decay_shift"/>
<property name='hold_syms' type='ulong' writable='true' default='0'
description="Per-channel samples to hold burst HIGH after energy drops below threshold (anti-flicker / mid-frame dropout hysteresis). Default 0 = drop immediately."/>
<!-- Per-channel volatile status (index = position/channel 0..15) -->
<property name='burst_det' type='bool' volatile='true' arraylength='16'
description="Burst-detected (active) status per channel"/>
<property name='energy' type='ulong' volatile='true' arraylength='16'
description="Current IIR energy estimate per channel"/>
<!-- Positional 16-sample IQ in, same positional 16-sample IQ out (quiet
channels zeroed). -->
<port name="input" producer="false" protocol="complex_short_timed_sample-prot"/>
<port name="output" producer="true" protocol="complex_short_timed_sample-prot"/>
</ComponentSpec>
Properties
init: Pulse high to reset all 16 per-channel energy/burst states. Pulse high to reset all 16 per-channel energy/burst states.Type:
boolAccess:
Parameter:
FalseWritable:
TrueInitial:
FalseVolatile:
False
Default value:
false
threshold: Energy threshold to declare a burst (per dsp.dsp.burst_detect; ~15000 detects signal well above the noise floor for I=Q~2000, decay_shift=8). Energy threshold to declare a burst (perdsp.dsp.burst_detect).Type:
ulongAccess:
Parameter:
FalseWritable:
TrueInitial:
FalseVolatile:
False
Default value:
15000
decay_shift: IIR decay = 2^decay_shift samples; steady energy ~= |IQ|^2 * 2^decay_shift. IIR decay time constant =2^decay_shiftsamples.Type:
ucharAccess:
Parameter:
FalseWritable:
TrueInitial:
FalseVolatile:
False
Default value:
8
hold_syms: Per-channel samples to hold burst HIGH after energy drops below threshold (anti-flicker / mid-frame dropout hysteresis). Default 0 = drop immediately. Samples to hold burst HIGH after energy drops below threshold.Type:
ulongAccess:
Parameter:
FalseWritable:
TrueInitial:
FalseVolatile:
False
Default value:
0
burst_det: Burst-detected (active) status per channel.Type:
boolArray dimensions:
['16']
Access:
Parameter:
FalseWritable:
FalseInitial:
FalseVolatile:
True
Default value:
None
energy: Current IIR energy estimate per channel.Type:
ulongArray dimensions:
['16']
Access:
Parameter:
FalseWritable:
FalseInitial:
FalseVolatile:
True
Default value:
None
Ports
Inputs:
input: Channelizer positional stream, 16-sample frames (complex_short_timed_sample).Protocol:
complex_short_timed_sample-protOptional:
False
Outputs:
output: Same positional framing; active channels passed through, quiet channels zeroed.Protocol:
complex_short_timed_sample-protOptional:
False
Implementations
bd_x16(HDL)Skeleton outline: Optional summary of the implementation of this worker. Anything before the next heading will be included as worker summary on component documentation page.
Dependencies
The dependencies to other elements in OpenCPI are:
ieee.std_logic_1164
ieee.numeric_std
Limitations
Limitations of bd_x16 are:
Operates on the fixed 16-sample positional framing; the input must be the channelizer/
decimate3positional stream.
Testing
Tested platforms: None