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: bool

    • Access:

      • Parameter: False

      • Writable: True

      • Initial: False

      • Volatile: 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 (per dsp.dsp.burst_detect).

    • Type: ulong

    • Access:

      • Parameter: False

      • Writable: True

      • Initial: False

      • Volatile: 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_shift samples.

    • Type: uchar

    • Access:

      • Parameter: False

      • Writable: True

      • Initial: False

      • Volatile: 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: ulong

    • Access:

      • Parameter: False

      • Writable: True

      • Initial: False

      • Volatile: False

    • Default value: 0

  • burst_det: Burst-detected (active) status per channel.

    • Type: bool

      • Array dimensions: ['16']

    • Access:

      • Parameter: False

      • Writable: False

      • Initial: False

      • Volatile: True

    • Default value: None

  • energy: Current IIR energy estimate per channel.

    • Type: ulong

      • Array dimensions: ['16']

    • Access:

      • Parameter: False

      • Writable: False

      • Initial: False

      • Volatile: True

    • Default value: None

Ports

Inputs:

  • input: Channelizer positional stream, 16-sample frames (complex_short_timed_sample).

    • Protocol: complex_short_timed_sample-prot

    • Optional: False

Outputs:

  • output: Same positional framing; active channels passed through, quiet channels zeroed.

    • Protocol: complex_short_timed_sample-prot

    • Optional: 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/decimate3 positional stream.

Testing

Tested platforms: None