Decimate-by-N (decimate3)

Decimates a positional 16-sample interleaved frame stream by a configurable integer factor, forwarding one complete message per every factor input messages per channel.

Tested platforms include xsim, zynq, zynq_ultra.

Design

The decimate3 worker sits between the channelizer output (12 samples per symbol at the channelizer’s output rate) and the msk_demod_x16 / front-end fold workers (4 samples per symbol). With the default factor = 3 it reduces the per-channel sample rate by 3:1, matching the 12 SPS channelizer output to the 4 SPS demodulator input.

The channelizer emits 16-sample sample_op_e messages — one complete snapshot of all 16 bins per output frame. decimate3 operates at the message (frame) level, not at the individual sample level: it keeps or drops entire 16-sample messages. This preserves the positional framing that downstream workers rely on to identify bins by slot.

The keep/drop decision is made by a free-running phase counter (frame_phase, 8 bits, range 0..``factor``-1) that advances by one on every input SOM beat. A message is kept when its SOM arrives while frame_phase = 0. The decision is latched into fwd_reg at the SOM and held through the entire message so that all 16 subsequent words in the same message are forwarded. The combinatorial signal fwd_comb merges both cases: (SOM and phase=0) or (not SOM and fwd_reg), ensuring the SOM word itself is included in the forwarded set without a one-cycle gap.

No anti-alias filtering is applied; the implementation is a pure sample-dropping decimator. This is correct for the OPV use-case because the channelizer’s per-channel prototype filter already band-limits each bin to well within the post-decimation Nyquist bandwidth. If the channelizer’s rolloff is insufficient for a given waveform, replace decimate3 with a polyphase-FIR decimator.

Kept messages are forwarded verbatim (SOM, EOM, data, opcode, byte-enable all pass through unchanged). The worker always accepts input (take tracks input_in.ready); no back-pressure is applied upstream.

Latency: zero additional pipeline stages for forwarded messages; dropped messages produce no output. The fwd_reg latched on SOM introduces one clock of pipelining relative to SOM, but the SOM word itself is forwarded combinatorially via fwd_comb, so the total end-to-end latency for a kept message is zero extra cycles.

Interface

<?xml version='1.0' encoding='utf-8'?>
<!-- decimate3: per-channel N:1 decimator on complex_short_timed_sample.
     Sits between the channelizer (SPS=12) and msk_demod_x16 (SPS=4): keeps
     1 of every `factor` (default 3) IQ samples PER CHANNEL, where the channel
     is identified by the metadata(id=0, value=abs_bin) tag.  Metadata-transparent:
     the kept sample's metadata is forwarded so the demod still sees the tag.

     NOTE: this is a simple sample-dropping decimator (no anti-alias FIR); it
     relies on the channelizer's per-channel prototype filtering.  If aliasing
     degrades demod lock, replace with a polyphase-FIR decimator. -->
<ComponentSpec>
  <property name="factor" type="uchar" writable="true" default="3"
            description="Per-channel decimation factor: keep 1 of every N samples per channel"/>
  <port name="input"  producer="false" protocol="complex_short_timed_sample-prot"/>
  <port name="output" producer="true"  protocol="complex_short_timed_sample-prot"/>
</ComponentSpec>

Ports

Inputs:

  • input: Positional 16-sample interleaved frame stream from the channelizer; complex_short_timed_sample-prot, sample opcode, 16 IQ words per message at 12 SPS per channel.

    • Protocol: complex_short_timed_sample-prot

    • Optional: False

Outputs:

  • output: Decimated frame stream; same positional framing as input, forwarded at 1/factor of the input message rate (4 SPS per channel at factor=3).

    • Protocol: complex_short_timed_sample-prot

    • Optional: False

Opcode Handling

decimate3 passes opcodes through from input to output without modification. The keep/drop decision is applied to all input messages regardless of opcode; only messages that arrive when frame_phase = 0 are forwarded. In normal operation all input messages carry the sample_op_e opcode.

Properties

  • factor: Per-channel decimation factor: keep 1 of every N samples per channel.

    • Type: uchar

    • Access:

      • Parameter: False

      • Writable: True

      • Initial: False

      • Volatile: False

    • Default value: 3

Implementations

  • decimate3 (HDL)

    HDL implementation of a frame-level integer decimator for the complex_short_timed_sample protocol, targeting 3:1 reduction of 12 SPS channelizer output to 4 SPS demodulator input.

Dependencies

The decimate3 HDL worker depends on:

  • ieee.std_logic_1164

  • ieee.numeric_std

  • ocpi.types (OpenCPI-generated protocol opcode constants)

No external primitives or generated files are required.

Limitations

Limitations of decimate3 are:

  • No anti-alias filtering. The implementation drops samples without any FIR pre-filter; correctness relies on the channelizer’s prototype filter having already band-limited each bin.

  • The factor property must be at least 1. A value of 0 is clamped to 1 (no decimation) by the registered process, but this is not enforced by the component spec.

  • The decimation factor is applied uniformly across all 16 channels in the frame; per-channel factors are not supported.

  • The factor property is read once per SOM event. Changing it while data is flowing will take effect on the next SOM but may produce a partial phase wrap if the old and new factors differ.

Testing

There is no dedicated unit test directory for decimate3. The worker is exercised as part of the folded-chain assembly test (folded_demod_x16_asm), where the output message rate and sample values are confirmed to match the expected 4 SPS per channel after 3:1 decimation.

Tested platforms: None