Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions blocks/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_subdirectory(basic)
add_subdirectory(digital)
add_subdirectory(electrical)
add_subdirectory(fileio)
add_subdirectory(filter)
Expand Down
47 changes: 47 additions & 0 deletions blocks/digital/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Digital Signal Processing Blocks for GNU Radio 4.0
#
# This module provides digital signal processing blocks including:
# - Core primitives (LFSR, CRC, Scramblers, Constellations)
# - Symbol mapping and constellation encoding/decoding
# - Timing recovery and PLLs
# - Measurement and probing blocks
# - Adaptive equalizers
# - OFDM processing blocks
# - Packet handling and framing
# - Miscellaneous correlation and protocol blocks

include(CMakePackageConfigHelpers)

# Digital blocks library
add_library(gr-digital INTERFACE)
add_library(gnuradio::gr-digital ALIAS gr-digital)

target_include_directories(gr-digital
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)

target_link_libraries(gr-digital
INTERFACE
gnuradio-core
gr-basic
)

target_compile_features(gr-digital INTERFACE cxx_std_23)

# Add tests if enabled
if(ENABLE_TESTING)
add_subdirectory(test)
endif()

# Installation (simplified for now)
install(TARGETS gr-digital
COMPONENT digital
)

install(DIRECTORY include/
DESTINATION include
COMPONENT digital
FILES_MATCHING PATTERN "*.hpp"
)
131 changes: 131 additions & 0 deletions blocks/digital/include/gnuradio-4.0/digital/core/Constellation.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#ifndef GNURADIO_DIGITAL_CONSTELLATION_HPP
#define GNURADIO_DIGITAL_CONSTELLATION_HPP

#include <array>
#include <complex>
#include <cstdint>
#include <limits>
#include <cmath>

namespace gr::digital {

using cfloat = std::complex<float>;

enum class Normalization {
None,
Power,
Amplitude
};

template <std::size_t N>
struct Constellation {
std::array<cfloat, N> points{};
std::array<std::uint32_t, N> labels{};

constexpr cfloat point(std::size_t i) const { return points[i]; }
constexpr std::uint32_t label(std::size_t i) const { return labels[i]; }

std::size_t index_of_label(std::uint32_t lab) const {
for (std::size_t i = 0; i < N; ++i) if (labels[i] == lab) return i;
return N; // not found
}

float avg_power() const {
float s = 0.f;
for (auto z : points) s += std::norm(z);
return s / static_cast<float>(N);
}

float avg_amplitude() const {
float s = 0.f;
for (auto z : points) s += std::abs(z);
return s / static_cast<float>(N);
}

Constellation<N> normalized(Normalization mode) const {
if (mode == Normalization::None) return *this;

Constellation<N> out = *this;
if (mode == Normalization::Power) {
const float ap = std::max(1e-30f, avg_power());
const float g = 1.0f / std::sqrt(ap);
for (auto& z : out.points) z *= g;
} else { // Amplitude
const float aa = std::max(1e-30f, avg_amplitude());
const float g = 1.0f / aa;
for (auto& z : out.points) z *= g;
}
return out;
}
};

inline bool finite(cfloat z) noexcept {
return std::isfinite(z.real()) && std::isfinite(z.imag());
}

struct EuclideanSlicer {
template <std::size_t N>
static std::size_t processOneIndex(const Constellation<N>& C, cfloat sample) {
if (!finite(sample)) return 0; // corner-case fallback

std::size_t best = 0;
float bestd = std::numeric_limits<float>::infinity();
for (std::size_t i = 0; i < N; ++i) {
const float d = std::norm(sample - C.points[i]);
if (d < bestd) { bestd = d; best = i; } // stable tie-break
}
return best;
}

template <std::size_t N>
static std::uint32_t processOneLabel(const Constellation<N>& C, cfloat sample) {
return C.labels[processOneIndex(C, sample)];
}
};

template <std::size_t N>
inline std::size_t closest_euclidean_index(const Constellation<N>& C, cfloat s) {
return EuclideanSlicer::processOneIndex(C, s);
}
template <std::size_t N>
inline std::uint32_t slice_label_euclidean(const Constellation<N>& C, cfloat s) {
return EuclideanSlicer::processOneLabel(C, s);
}

constexpr Constellation<2> BPSK() {
return Constellation<2>{
/* points */ { cfloat{-1.f, 0.f}, cfloat{+1.f, 0.f} },
/* labels */ { 0u, 1u }
};
}

constexpr Constellation<4> QPSK_Gray() {
return Constellation<4>{
/* points */ {
cfloat{-1.f,-1.f}, cfloat{+1.f,-1.f},
cfloat{-1.f,+1.f}, cfloat{+1.f,+1.f}
},
/* labels */ { 0u, 1u, 2u, 3u }
};
}

constexpr Constellation<16> QAM16_Gray() {
return Constellation<16>{
/* points */ {
cfloat{-3,-3}, cfloat{-1,-3}, cfloat{+1,-3}, cfloat{+3,-3},
cfloat{-3,-1}, cfloat{-1,-1}, cfloat{+1,-1}, cfloat{+3,-1},
cfloat{-3,+1}, cfloat{-1,+1}, cfloat{+1,+1}, cfloat{+3,+1},
cfloat{-3,+3}, cfloat{-1,+3}, cfloat{+1,+3}, cfloat{+3,+3}
},
/* labels */ {
0x0u, 0x4u, 0xCu, 0x8u,
0x1u, 0x5u, 0xDu, 0x9u,
0x3u, 0x7u, 0xFu, 0xBu,
0x2u, 0x6u, 0xEu, 0xAu
}
};
}

} // namespace gr::digital

#endif // GNURADIO_DIGITAL_CONSTELLATION_HPP
87 changes: 87 additions & 0 deletions blocks/digital/include/gnuradio-4.0/digital/core/Crc.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#ifndef GNURADIO_DIGITAL_CRC_HPP
#define GNURADIO_DIGITAL_CRC_HPP

#include <array>
#include <cstddef>
#include <cstdint>
#include <stdexcept>

namespace gr::digital {

struct CrcState {
unsigned num_bits = 0;
std::uint64_t poly = 0;
std::uint64_t initial_value = 0;
std::uint64_t final_xor = 0;
bool input_reflected = false;
bool result_reflected= false;
};

struct Crc {
CrcState st{};
std::array<std::uint64_t, 256> table{};
std::uint64_t mask = 0;
std::uint64_t reg = 0;

void start() {
if (st.num_bits == 0 || st.num_bits > 64) throw std::invalid_argument("crc width");
if (st.num_bits < 8) throw std::invalid_argument("crc width < 8");
mask = (st.num_bits == 64) ? ~0ull : ((1ull << st.num_bits) - 1ull);
reg = st.initial_value & mask;

if (st.input_reflected) {
const auto poly_r = reflect(st.poly & mask, st.num_bits);
for (std::size_t i = 0; i < 256; ++i) {
std::uint64_t r = static_cast<std::uint64_t>(i);
for (int b = 0; b < 8; ++b) {
r = (r & 1ull) ? ((r >> 1) ^ poly_r) : (r >> 1);
}
table[i] = r & mask;
}
} else {
const auto poly = st.poly & mask;
const std::uint64_t topbit = 1ull << (st.num_bits - 1);
for (std::size_t i = 0; i < 256; ++i) {
std::uint64_t r = static_cast<std::uint64_t>(i) << (st.num_bits - 8);
for (int b = 0; b < 8; ++b) {
r = (r & topbit) ? ((r << 1) ^ poly) : (r << 1);
r &= mask;
}
table[i] = r & mask;
}
}
}

void stop() {}

std::uint64_t processOne(std::uint8_t byte) noexcept {
if (st.input_reflected) {
const std::uint64_t idx = (reg ^ byte) & 0xFFull;
reg = (reg >> 8) ^ table[idx];
} else {
const std::uint64_t idx =
((reg >> (st.num_bits - 8)) ^ static_cast<std::uint64_t>(byte)) & 0xFFull;
reg = ((reg << 8) & mask) ^ table[idx];
}
return reg;
}

std::uint64_t compute(const std::uint8_t* data, std::size_t len) noexcept {
reg = st.initial_value & mask;
for (std::size_t i = 0; i < len; ++i) processOne(data[i]);
std::uint64_t out = reg & mask;
if (st.input_reflected != st.result_reflected) out = reflect(out, st.num_bits);
out ^= st.final_xor;
return out & mask;
}

static std::uint64_t reflect(std::uint64_t x, unsigned width) noexcept {
std::uint64_t r = 0;
for (unsigned i = 0; i < width; ++i) { r = (r << 1) | (x & 1ull); x >>= 1; }
return r;
}
};

} // namespace gr::digital

#endif
118 changes: 118 additions & 0 deletions blocks/digital/include/gnuradio-4.0/digital/core/Lfsr.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#ifndef GNURADIO_DIGITAL_LFSR_HPP
#define GNURADIO_DIGITAL_LFSR_HPP

#include <cstdint>

namespace gr::digital {

namespace lfsr_type { enum class Type : int { Fibonacci, Galois }; }

template<lfsr_type::Type V> struct LfsrState;
template<lfsr_type::Type V> struct LfsrGen;
template<lfsr_type::Type V> struct LfsrScrambler;
template<lfsr_type::Type V> struct LfsrDescrambler;

namespace detail {
static inline constexpr std::uint8_t parity64(std::uint64_t v) noexcept {
v ^= v >> 32; v ^= v >> 16; v ^= v >> 8; v ^= v >> 4; v &= 0xFu;
return static_cast<std::uint8_t>((0x6996u >> v) & 1u);
}
}

template<lfsr_type::Type V>
struct LfsrState {
std::uint64_t mask = 0;
std::uint64_t seed = 1;
std::uint8_t len = 0;
std::uint64_t sr = 0;

void start() noexcept { sr = seed; }
void stop() noexcept {}
void advance(std::size_t n) noexcept { while (n--) (void)step_(); }
std::uint64_t state() const noexcept { return sr; }

private:
std::uint8_t step_() noexcept {
if constexpr (V == lfsr_type::Type::Fibonacci) {
const std::uint8_t out = static_cast<std::uint8_t>(sr & 1u);
const std::uint8_t nb = detail::parity64(sr & mask);
sr = (sr >> 1) | (static_cast<std::uint64_t>(nb) << len);
return out;
} else {
const std::uint8_t out = static_cast<std::uint8_t>(sr & 1u);
sr >>= 1;
if (out) sr ^= mask;
return out;
}
}

template<lfsr_type::Type> friend struct LfsrGen;
template<lfsr_type::Type> friend struct LfsrScrambler;
template<lfsr_type::Type> friend struct LfsrDescrambler;
};

template<lfsr_type::Type V>
struct LfsrGen {
LfsrState<V> st;
void start() noexcept { st.start(); }
void stop() noexcept { st.stop(); }
std::uint8_t processOne() noexcept { return st.step_(); }
std::uint64_t state() const noexcept { return st.state(); }
};

template<lfsr_type::Type V>
struct LfsrScrambler {
LfsrState<V> st;
void start() noexcept { st.start(); }
void stop() noexcept { st.stop(); }
std::uint8_t processOne(std::uint8_t in) noexcept {
if constexpr (V == lfsr_type::Type::Fibonacci) {
const std::uint8_t p = detail::parity64(st.sr & st.mask);
const std::uint8_t y = static_cast<std::uint8_t>(p ^ (in & 1u));
st.sr = (st.sr >> 1) | (static_cast<std::uint64_t>(y) << st.len);
return y;
} else {
const std::uint8_t s0 = static_cast<std::uint8_t>(st.sr & 1u);
const std::uint8_t y = static_cast<std::uint8_t>(s0 ^ (in & 1u));
st.sr >>= 1;
if (y) st.sr ^= st.mask;
return y;
}
}
std::uint64_t state() const noexcept { return st.state(); }
};

template<lfsr_type::Type V>
struct LfsrDescrambler {
LfsrState<V> st;
void start() noexcept { st.start(); }
void stop() noexcept { st.stop(); }
std::uint8_t processOne(std::uint8_t in) noexcept {
if constexpr (V == lfsr_type::Type::Fibonacci) {
const std::uint8_t p = detail::parity64(st.sr & st.mask);
const std::uint8_t x = static_cast<std::uint8_t>(p ^ (in & 1u));
st.sr = (st.sr >> 1) | (static_cast<std::uint64_t>(in & 1u) << st.len);
return x;
} else {
const std::uint8_t s0 = static_cast<std::uint8_t>(st.sr & 1u);
const std::uint8_t x = static_cast<std::uint8_t>(s0 ^ (in & 1u));
st.sr >>= 1;
if (in & 1u) st.sr ^= st.mask;
return x;
}
}
std::uint64_t state() const noexcept { return st.state(); }
};

using LfsrGenF = LfsrGen<lfsr_type::Type::Fibonacci>;
using LfsrGenG = LfsrGen<lfsr_type::Type::Galois>;
using LfsrScramblerF = LfsrScrambler<lfsr_type::Type::Fibonacci>;
using LfsrScramblerG = LfsrScrambler<lfsr_type::Type::Galois>;
using LfsrDescramblerF = LfsrDescrambler<lfsr_type::Type::Fibonacci>;
using LfsrDescramblerG = LfsrDescrambler<lfsr_type::Type::Galois>;

namespace primitive_polynomials { inline constexpr std::uint64_t poly_5 = 0x29; }

} // namespace gr::digital

#endif
Loading
Loading