Files
wdsp/varsamp.h
T
Uladzimir KarpenkaandClaude Opus 4.8 645cbbb2d1 varsamp: transpose the coefficients into phases, vectorize the tap loop
hshift() rebuilds the whole interpolated tap set on every output sample,
reading h[hidx + m*R] for m = 0..rsize-1. With R = 1024 (what rmatch asks
for) that strides 8 KB at a time through a 1.1 MB table, so every one of
the 2*rsize reads is its own cache line. It cost more than the filter it
was feeding: 258 ns per output sample against 146 ns for the tap loop.

Store the coefficients transposed instead, hp[p*rsize + m] = h[p + m*R],
so the two phases hshift() interpolates between are each contiguous. R+1
phases are needed since it reads hidx and hidx+1, and h_offset is kept in
[0,1) by the caller so hidx <= R-1. The untransposed h is freed; the
impulse cache hands back a copy, so varsamp owns it. Net memory is
unchanged.

The tap loop had the same wrap test per tap as resample.c did, so split it
at the wrap and carry four independent accumulator pairs; the ring is split
into I/Q so the taps load unit-stride.

Note a->hs is rewritten by hshift() inside the sample loop, so it must not
be hoisted behind a restrict pointer in xvarsamp().

Measured on an Apple M1 Pro, 512-sample buffers, best of 5:

    48k -> 48k   varmode=0    138580 ns -> 37370 ns   3.71x
    48k -> 48k   varmode=1    138390 ns -> 32333 ns   4.28x
    48k -> 44.1k varmode=1    141473 ns -> 38220 ns   3.70x
    44.1k -> 48k varmode=1    152307 ns -> 45263 ns   3.36x

hshift() is numerically identical -- same coefficients, different layout.
Only the reassociated tap sum rounds differently: worst deviation 7.2e-16
over four rate configurations, an SNR of 344 dB. Driven end to end through
rmatch's public API, output SNR is 306 dB and total energy matches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 23:19:36 +03:00

87 lines
2.3 KiB
C

/* varsamp.h
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2017 Warren Pratt, NR0V
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@wpratt.com
*/
#ifndef _varsamp_h
#define _varsamp_h
typedef struct _varsamp
{
int run;
int size;
double* in;
double* out;
int in_rate;
int out_rate;
double fcin;
double fc;
double fc_low;
double gain;
int idx_in;
int ncoef;
double* hp; // coefficients, polyphase: hp[p * rsize + m] = h[p + m * R],
// p = 0..R. hshift() then reads two adjacent phases
// contiguously instead of striding by R.
int rsize;
double* ringI; // ring buffer, in-phase
double* ringQ; // ring buffer, quadrature (split from I so the tap loop
// reads unit-stride and vectorizes)
double var;
int varmode;
double cvar;
double inv_cvar;
double old_inv_cvar;
double dicvar;
double delta;
double* hs;
int R;
double h_offset;
double isamps;
double nom_ratio;
} varsamp, *VARSAMP;
extern VARSAMP create_varsamp ( int run, int size, double* in, double* out,
int in_rate, int out_rate, double fc, double fc_low, int R, double gain, double var, int varmode);
extern void destroy_varsamp (VARSAMP a);
extern void flush_varsamp (VARSAMP a);
extern int xvarsamp (VARSAMP a, double var);
extern void setBuffers_varsamp (VARSAMP a, double* in, double* out);
extern void setSize_varsamp (VARSAMP a, int size);
extern void setInRate_varsamp (VARSAMP a, int rate);
extern void setOutRate_varsamp (VARSAMP a, int rate);
extern void setFCLow_varsamp (VARSAMP a, double fc_low);
extern void setBandwidth_varsamp (VARSAMP a, double fc_low, double fc_high);
#endif