Skip to content

Commit efbbdbb

Browse files
committed
api: Develop C++ wrapper API
The VOLK C API should be in place. We stick with the current API. Except, we always require it to be C. No more C and C++ mix and match. This allows us to be more open about the C++ API. Here, we write wrappers around our C functions. Signed-off-by: Johannes Demel <[email protected]>
1 parent b1c2f61 commit efbbdbb

File tree

1 file changed

+142
-24
lines changed

1 file changed

+142
-24
lines changed

main.cc

Lines changed: 142 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,136 @@
11
#include <fmt/core.h>
2+
#include <algorithm>
23
#include <cmath>
34
#include <complex>
5+
#include <cstdint>
46
#include <cstdlib>
57
#include <iostream>
68
#include <vector>
79

8-
typedef std::complex<float> cmplxf;
10+
/*
11+
* These type definitions are in line with our C definitions.
12+
*
13+
* Alternativele, we could go with the NumPy scheme:
14+
* np.complex64 aka std::complex<float>
15+
* np.complex128 aka std::complex<double>
16+
* The underlying types are probably defined like Ctypes.
17+
* This is about the idea.
18+
*/
19+
typedef std::complex<int8_t> ic8;
20+
typedef std::complex<int16_t> ic16;
21+
typedef std::complex<int32_t> ic32;
22+
typedef std::complex<int64_t> ic64;
23+
typedef std::complex<float> fc32;
24+
typedef std::complex<double> fc64;
925

1026
#include <volk/volk.h>
1127
#include <volk/volk_alloc.hh>
1228

29+
/* C++ Interface requirements
30+
*
31+
* 1. Make C++ STL types usable `std::vector`, `std::complex`.
32+
* 2. Make aligned vectors aka `volk::vector` usable.
33+
* 3. Allow call-by-pointer for GR buffer interface usage etc.
34+
*
35+
* These requirements result in at least 3 functions.
36+
* We might want to think about fancy new C++ features e.g. concepts to consolidate these.
37+
*/
1338

14-
void cppmultiply(volk::vector<cmplxf>& result,
15-
volk::vector<cmplxf>& input0,
16-
volk::vector<cmplxf>& input1)
39+
namespace volk {
40+
41+
/*
42+
* Start of wrapper for volk_32fc_s32fc_multiply_32fc
43+
*/
44+
void cppscalarmultiply_pointers(fc32* result,
45+
const fc32* input0,
46+
const fc32 scalar,
47+
const unsigned int num_points)
48+
{
49+
volk_32fc_s32fc_multiply_32fc(reinterpret_cast<lv_32fc_t*>(result),
50+
reinterpret_cast<const lv_32fc_t*>(input0),
51+
lv_32fc_t{ scalar.real(), scalar.imag() },
52+
num_points);
53+
}
54+
55+
void cppscalarmultiply_stl_vector(std::vector<fc32>& result,
56+
const std::vector<fc32>& input0,
57+
const fc32 scalar)
58+
{
59+
unsigned int num_points = std::min({ result.size(), input0.size() });
60+
cppscalarmultiply_pointers(result.data(), input0.data(), scalar, num_points);
61+
}
62+
63+
void cppscalarmultiply_aligned_vector(volk::vector<fc32>& result,
64+
const volk::vector<fc32>& input0,
65+
const fc32 scalar)
66+
{
67+
unsigned int num_points = std::min({ result.size(), input0.size() });
68+
cppscalarmultiply_pointers(result.data(), input0.data(), scalar, num_points);
69+
}
70+
71+
/*
72+
* Start of wrapper for volk_32fc_x2_multiply_32fc
73+
*/
74+
void cppmultiply_pointers(fc32* result,
75+
const fc32* input0,
76+
const fc32* input1,
77+
const unsigned int num_points)
78+
{
79+
volk_32fc_x2_multiply_32fc(reinterpret_cast<lv_32fc_t*>(result),
80+
reinterpret_cast<const lv_32fc_t*>(input0),
81+
reinterpret_cast<const lv_32fc_t*>(input1),
82+
num_points);
83+
}
84+
85+
void cppmultiply_stl_vector(std::vector<fc32>& result,
86+
const std::vector<fc32>& input0,
87+
const std::vector<fc32>& input1)
88+
{
89+
unsigned int num_points = std::min({ result.size(), input0.size(), input1.size() });
90+
cppmultiply_pointers(result.data(), input0.data(), input1.data(), num_points);
91+
}
92+
93+
void cppmultiply_aligned_vector(volk::vector<fc32>& result,
94+
const volk::vector<fc32>& input0,
95+
const volk::vector<fc32>& input1)
1796
{
18-
volk_32fc_x2_multiply_32fc(reinterpret_cast<lv_32fc_t*>(result.data()),
19-
reinterpret_cast<lv_32fc_t*>(input0.data()),
20-
reinterpret_cast<lv_32fc_t*>(input1.data()),
21-
input0.size());
97+
unsigned int num_points = std::min({ result.size(), input0.size(), input1.size() });
98+
cppmultiply_pointers(result.data(), input0.data(), input1.data(), num_points);
2299
}
23100

24-
void function_test(int num_points)
101+
} // namespace volk
102+
103+
104+
std::vector<fc32> fill_vector(int num_points, float step_value)
25105
{
26-
volk::vector<cmplxf> in0(num_points);
27-
volk::vector<cmplxf> in1(num_points);
28-
volk::vector<cmplxf> out(num_points);
106+
std::vector<fc32> vec(num_points);
29107

30108
for (unsigned int ii = 0; ii < num_points; ++ii) {
31-
// Generate two tones
32-
float real_1 = std::cos(0.3f * (float)ii);
33-
float imag_1 = std::sin(0.3f * (float)ii);
34-
in0[ii] = cmplxf(real_1, imag_1);
35-
float real_2 = std::cos(0.1f * (float)ii);
36-
float imag_2 = std::sin(0.1f * (float)ii);
37-
in1[ii] = cmplxf(real_2, imag_2);
109+
float real_1 = std::cos(step_value * (float)ii);
110+
float imag_1 = std::sin(step_value * (float)ii);
111+
vec[ii] = fc32(real_1, imag_1);
38112
}
113+
return vec;
114+
}
115+
116+
void function_test_vectors(int num_points)
117+
{
118+
std::vector<fc32> uin0(fill_vector(num_points, 0.3f));
119+
volk::vector<fc32> in0(uin0.begin(), uin0.end());
120+
std::vector<fc32> uin1(fill_vector(num_points, 0.1f));
121+
volk::vector<fc32> in1(uin1.begin(), uin1.end());
122+
std::vector<fc32> uout(num_points);
123+
volk::vector<fc32> out(num_points);
124+
125+
volk::cppmultiply_aligned_vector(out, in0, in1);
39126

40-
cppmultiply(out, in0, in1);
127+
volk::cppmultiply_stl_vector(uout, uin0, uin1);
128+
volk::cppmultiply_pointers(uout.data(), in0.data(), in1.data(), num_points);
41129

42130
for (int ii = 0; ii < num_points; ++ii) {
43-
cmplxf v0 = in0[ii];
44-
cmplxf v1 = in1[ii];
45-
cmplxf o = out[ii];
131+
fc32 v0 = in0[ii];
132+
fc32 v1 = in1[ii];
133+
fc32 o = out[ii];
46134

47135
fmt::print(
48136
"in0=({:+.1f}{:+.1f}j), in1=({:+.1f}{:+.1f}j), out=({:+.1f}{:+.1f}j)\n",
@@ -55,10 +143,40 @@ void function_test(int num_points)
55143
}
56144
}
57145

146+
void function_test_with_scalar(int num_points)
147+
{
148+
std::vector<fc32> uin0(fill_vector(num_points, 0.3f));
149+
volk::vector<fc32> in0(uin0.begin(), uin0.end());
150+
fc32 scalar{ 0.5f, 4.3f };
151+
std::vector<fc32> uout(num_points);
152+
volk::vector<fc32> out(num_points);
153+
154+
volk::cppscalarmultiply_aligned_vector(out, in0, scalar);
155+
156+
volk::cppscalarmultiply_stl_vector(uout, uin0, scalar);
157+
volk::cppscalarmultiply_pointers(uout.data(), in0.data(), scalar, num_points);
158+
159+
fmt::print("scalar=({:+.1f}{:+.1f}j)\n", std::real(scalar), std::imag(scalar));
160+
for (int ii = 0; ii < num_points; ++ii) {
161+
fc32 v0 = in0[ii];
162+
fc32 o = out[ii];
163+
164+
fmt::print("in0=({:+.1f}{:+.1f}j), out=({:+.1f}{:+.1f}j)\n",
165+
std::real(v0),
166+
std::imag(v0),
167+
std::real(o),
168+
std::imag(o));
169+
}
170+
}
58171

59172
int main(int argc, char* argv[])
60173
{
61-
function_test(32);
174+
fmt::print("Vector function test\n");
175+
function_test_vectors(16);
176+
177+
fmt::print("Scalar function test\n");
178+
function_test_with_scalar(16);
179+
62180
lv_32fc_t fc_cpl[4];
63181
fmt::print("float={}, complex float={}, complex float array[4]={}\n",
64182
sizeof(float),

0 commit comments

Comments
 (0)