Polyphase Interpolator (polyphase_interpolator_xs)
A polyphase interpolator is a resource efficient method for interpolating signals.
Introduction
To interpolate by a factor of M, a normal interpolator operates by first inserting M-1 zero samples into the data stream, after each data sample, followed by passing the resulting stream through a low pass filter (with a maximum normalised frequency of 1/M).
This is resource intensive, as it means the filtering is running at the faster sample rate of the two (i.e. the output rate). In addition, a lot of the samples passing through this filter are actually going to be zero and therefore unnecessary multiplications are performed.
Instead with a Polyphase Interpolator the filter is split into sub-filters, each set relating to an output sample which would be created from the same input values, with their relative signal phase present. This subset of filter values can be multiplied with the incoming sample data to produce an output sample, allowing the filtering to be undertaken at the incoming sample rate. In addition this also allows each of these sub-filters to be calculated in parallel.
For example, consider interpolating the following example stream by a factor of three:
Input Samples |
A |
B |
C |
D |
E |
F |
G |
H |
I |
with a set of taps:
Filter Taps |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
Traditional Interpolator:
Zeroes Added |
x |
x |
A |
x |
x |
B |
x |
x |
C |
x |
x |
D |
Sample 0 |
A0 + x1 + x2 + B3 + x4 + x5 + C6 + x7 + x8 |
Sample 1 |
x0 + A1 + x2 + x3 + B4 + x5 + x6 + C7 + x8 |
Sample 2 |
x0 + x1 + A2 + x3 + x4 + B5 + x6 + x7 + C8 |
Polyphase Interpolator:
Sample 0 |
A0 + B3 + C6 |
Sample 1 |
A1 + B4 + C7 |
Sample 2 |
A2 + B5 + C8 |
Each line in the above corresponds to a single branch (or filter bank) of the interpolator.
Design
The Polyphase Interpolator is designed to allow runtime variable interpolation rates, along with customisable taps providing a low pass filter. The user of this component should ensure that the provided taps are correctly scaled, e.g. to provide a unity gain, as the taps are not scaled within this component.
This component uses an HDL primitive, or a generalised C++ class, which produces a single output set of samples for a given set of incoming samples.
The internal data flow follows the following pseudo-states:
Wait for Data
Wait until a new input sample is received. Once a sample is received, add it to the input data buffer.
This buffer should be the same size as the (number of taps)/(interpolation factor).
Then start Processing Sample.
Processing Sample
Beginning at branch(m) = 0, and Tap(n) = 0.
For each branch:
For each tap:
Multiply Tap[m + (n * interpolation factor)] with Sample[n] (performing a normalisation shift for integer data protocols), and accumulate the result.
Increment n by 1.
Once the end of the input sample buffer (and therefore the end of the taps) has been reached, take the value in the accumulator and output it.
Once the value has been exported, increment to the next filter bank (m = m + 1).
Once all the filter banks have been outputted, return to Wait for Data.
Time Message Alignment
In order to help align messages in time, the interpolator allows the group delay of the built-in filter to be entered as a property. If no group delay is defined, then this value is defaulted to zero.
The group delay is subtracted as a time offset from any incoming timestamps (this is the equivalent of delaying the timestamp relative to the samples).
The group delay is defined with two properties; group_delay_seconds for seconds, group_delay_fractional for fractional delay - these properties having the same format as the timestamp and sample interval opcodes.
If the subtraction of the group delay leads to a time value which is less than zero, then the value is wrapped around by the maximum value the timestamp field can hold (UQ32.64).
Interface
<?xml version="1.0"?>
<componentspec>
<property name="interpolation_factor" type="uchar" initial="true" writable="false" default="1"
description="Upsample factor (number of branches)"/>
<property name="max_number_taps" type="ushort" parameter="true" default="64"
description="Defines the maximum number of taps across all interpolation branches"/>
<property name="number_of_taps" type="ushort" initial="true" writable="false" default="max_number_taps"
description="Number of taps across all interpolation branches"/>
<property name="rounding_type" type="enum" initial="true" writable="false" enums="truncate,half_up,half_even" default="truncate"
description="Rounding type"/>
<property name="group_delay_seconds" type="ulong" default="0" writable="true" description="Group delay of the filter in seconds."/>
<property name="group_delay_fractional" type="ulonglong" default="0" writable="true"
description="Group delay of the filter fractional."/>
<!-- LINT EXCEPTION: xml_012: 2:
Cannot set default as size varies with parameter values. -->
<property name="taps" type="short" arraylength="max_number_taps" writable="true" description="Array of tap values"/>
<port name="input" producer="false" protocol="complex_short_timed_sample-prot"/>
<port name="output" producer="true" protocol="complex_short_timed_sample-prot"/>
</componentspec>
Opcode Handling
Samples
Apply interpolation to these samples and send to output port.
Flush
Input
number_of_tapszeros into the sample buffer, outputting any samples generated.Reset sample buffer, and sub-filter offset.
Forward to output port.
Discontinuity
Reset input buffer.
Forward to output port.
Metadata
Forward to output port.
Sample_Interval
Calculate output sample interval as input sample_interval /
interpolation_factor.Forward calculated value to output port.
Time
Calculate output time by removing group delay value.
Forward calculated value to output port.
Properties
interpolation_factor: Upsample factor (number of branches).Type:
ucharAccess:
Parameter:
FalseWritable:
FalseInitial:
TrueVolatile:
False
Default value:
1
max_number_taps: Defines the maximum number of taps across all interpolation branches.Type:
ushortAccess:
Parameter:
TrueWritable:
FalseInitial:
FalseVolatile:
False
Default value:
64
number_of_taps: Number of taps across all interpolation branches.Type:
ushortAccess:
Parameter:
FalseWritable:
FalseInitial:
TrueVolatile:
False
Default value:
max_number_taps
rounding_type: Rounding type.Type:
enumAllowed values:
['truncate', 'half_up', 'half_even']
Access:
Parameter:
FalseWritable:
FalseInitial:
TrueVolatile:
False
Default value:
truncate
group_delay_seconds: Group delay of the filter in seconds.Type:
ulongAccess:
Parameter:
FalseWritable:
TrueInitial:
FalseVolatile:
False
Default value:
0
group_delay_fractional: Group delay of the filter fractional.Type:
ulonglongAccess:
Parameter:
FalseWritable:
TrueInitial:
FalseVolatile:
False
Default value:
0
taps: Array of tap values.Type:
shortArray dimensions:
['max_number_taps']
Access:
Parameter:
FalseWritable:
TrueInitial:
FalseVolatile:
False
Default value:
None
Ports
Inputs:
input: Primary input samples port.Protocol:
complex_short_timed_sample-protOptional:
False
Outputs:
output: Primary output samples port.Protocol:
complex_short_timed_sample-protOptional:
False
Implementations
Example Application
<?xml version="1.0"?>
<application done="file_write">
<instance component="ocpi.core.file_read" connect="polyphase_interpolator_xs">
<property name="filename" value="input.bin"/>
</instance>
<instance component="ocpi.comp.sdr.dsp.polyphase_interpolator_xs" connect="file_write">
<property name="interpolation_factor" value="8"/>
<property name="number_of_taps" value="64"/>
<property name="max_number_taps" value="64"/>
<property name="taps" valuefile="taps.dat"/>
<property name="rounding_type" value="half_even"/>
<property name="group_delay_seconds" value="0"/>
<property name="group_delay_fractional" value="0"/>
</instance>
<instance component="ocpi.core.file_write">
<property name="filename" value="output.bin"/>
</instance>
</application>
Dependencies
The dependencies to other elements in OpenCPI are:
components/dsp/common/polyphase_interpolator/polyphase_interpolator.hh
components/math/common/time_utils.hh
There is also a dependency on:
ieee.std_logic_1164
ieee.numeric_std
ieee.math_real
Limitations
Limitations of polyphase_interpolator_xs are:
The
interpolation_factorproperty must not exceed thenumber_of_tapsproperty.
Testing
Tested platforms: None
Component testing not completed.