Skip to content

REQ-DEM-051: where the equaliser's impulse sits, and the law that moves it #434

Description

@TGoodhew

Found while implementing #161. REQ-DEM-051 gives the equaliser's impulse position as a
behaviour and not as a law:

for short filter lengths the impulse sits at the filter centre; as length grows the impulse
"moves proportionally towards the start of the filter" to accommodate channels with large delay
spread

The reference product publishes the behaviour and not the rule that produces it, so where the knee
sits and how fast the impulse moves after it are this build's choice. Implemented one way, with the
alternative and the unroll recorded here.

What the two sides of the filter buy

They are not interchangeable, which is why the position matters at all.

  • Taps before the impulse invert what arrives early. The only anticausal thing the chain knows
    about is the composite pulse itself, whose reach is FilterSymbolSpan symbols either side of
    centre by construction.
  • Taps after it invert delay spread — an echo, and the decaying series that cancels it. This is
    unbounded, and it is the reason anybody lengthens the filter.

A fixed-centre equaliser divides every added tap equally between the two, so half of each one goes
in front of an impulse that has nothing to do there, and a channel whose delay spread exceeds half
the filter stays uncorrected however long the filter is made. That much is not a matter of taste:
the requirement's own rationale — "to accommodate channels with large delay spread" — rules the
fixed centre out.

Implemented

impulse index = min(taps / 2, 2 · FilterSymbolSpan)

The pre-cursor side is capped at what the pulse needs (2 taps per symbol at T/2 spacing), and every
tap beyond that goes after the impulse. Measured, at the default 8-symbol span:

Filter Length Taps Impulse at Fraction of the filter
6 symbols 12 6 0.500
12 symbols 24 12 0.500
24 symbols 48 16 0.333
48 symbols 96 16 0.167

Short filters are centred; the fraction falls monotonically after the knee. EqualiserModeTests
holds it as a trend across four lengths rather than as four numbers, since a fixed-centre
implementation passes the short case and fails only the long one.

The choice inside it

The knee is tied to FilterSymbolSpan. That is the only quantity the chain holds that says how
far the pulse reaches, so it is the only non-arbitrary place to put it — but it does mean the
impulse position moves when the filter span setting is changed for unrelated reasons. At the
default span of 8 the knee is at a 16-symbol equaliser; at the 20-symbol span the demodulation tests
use, it is at 40 and every ordinary filter length is centred.

The index is constant beyond the knee, not sliding. "Moves proportionally" is satisfied as a
fraction of the filter, which is what the delay-spread rationale needs. A reading that wants the
index itself to keep moving would want the alternative below.

The alternative

A smooth ratio: index = round(taps · r) with r falling from 0.5 towards a floor — a third, say —
as the length grows, so the impulse keeps moving at every length rather than stopping at a fixed tap
count. It is defensible as a shape and it is what "moves proportionally" says most literally; what
it is not is derived from anything. The floor and the rate would both be invented.

To adopt it: change DemodSettings.EqualiserImpulseIndex and nothing else — it is the single
place the position is computed, the fit reads it as the reference delay and Reset writes its unit
impulse at it. EqualiserModeTests.TheImpulseStartsCentredAndMovesTowardsTheStartAsTheFilterGrows
asserts the trend and not the values, so it would still hold; the table above and the
"Where the impulse sits, and why it moves" section of docs/help/demodulation-equaliser.md would
need the new numbers.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    deferred:revisitClosed to keep the backlog at zero; revisit once the rest of the product is complete

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions