Skip to content
This repository was archived by the owner on Jul 19, 2022. It is now read-only.
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 blocklib/digital/crc_append/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
meson.build
61 changes: 61 additions & 0 deletions blocklib/digital/crc_append/crc_append.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module: digital
block: crc_append
label: CRC Append
blocktype: block

parameters:
- id: num_bits
label: CRC Size (bits)
dtype: size_t
grc:
default: 32
- id: poly
label: CRC Polynomial
dtype: uint64_t
grc:
default: 0x4C11DB7
- id: initial_value
label: Initial Register Value
dtype: uint64_t
grc:
default: 0xFFFFFFFF
- id: final_xor
label: Final XOR Value
dtype: uint64_t
grc:
default: 0xFFFFFFFF
- id: input_reflected
label: LSB-first Input
dtype: bool
grc:
default: True
- id: result_reflected
label: LSB-first Result
dtype: bool
grc:
default: True
- id: swap_endianness
label: LSB CRC in PDU
dtype: bool
grc:
default: False
- id: skip_header_bytes
label: Header Bytes to Skip
dtype: size_t
default: 0

ports:
- domain: message
id: in
direction: input

- domain: message
id: out
direction: output
optional: true

implementations:
- id: cpu
# - id: cuda

file_format: 1
36 changes: 36 additions & 0 deletions blocklib/digital/crc_append/crc_append_cpu.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* -*- c++ -*- */
/*
* Copyright 2022 FIXME
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/

#include "crc_append_cpu.h"
#include "crc_append_cpu_gen.h"

namespace gr {
namespace digital {

crc_append_cpu::crc_append_cpu(block_args args)
: INHERITED_CONSTRUCTORS,
d_num_bits(args.num_bits),
d_swap_endianness(args.swap_endianness),
d_crc(kernel::digital::crc(args.num_bits,
args.poly,
args.initial_value,
args.final_xor,
args.input_reflected,
args.result_reflected)),
d_header_bytes(args.skip_header_bytes)
{
if (args.num_bits % 8 != 0) {
throw std::runtime_error("CRC number of bits must be divisible by 8");
}
}


} // namespace digital
} // namespace gr
70 changes: 70 additions & 0 deletions blocklib/digital/crc_append/crc_append_cpu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* -*- c++ -*- */
/*
* Copyright 2022 FIXME
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/

#pragma once

#include <gnuradio/digital/crc_append.h>

#include <pmtf/map.hpp>
#include <pmtf/vector.hpp>

#include <gnuradio/kernel/digital/crc.h>

namespace gr {
namespace digital {

class crc_append_cpu : public virtual crc_append
{
public:
crc_append_cpu(block_args args);

private:
unsigned d_num_bits;
bool d_swap_endianness;
kernel::digital::crc d_crc;
unsigned d_header_bytes;

virtual void handle_msg_in(pmtf::pmt msg) override
{
auto meta = pmtf::get_as<std::map<std::string, pmtf::pmt>>(
pmtf::map(msg)["meta"]);
auto samples = pmtf::get_as<std::vector<uint8_t>>(
pmtf::map(msg)["data"]);

const auto size = samples.size();
if (size <= d_header_bytes) {
d_logger->warn("PDU too short; dropping");
return;
}

uint64_t crc = d_crc.compute(&samples[d_header_bytes], size - d_header_bytes);

unsigned num_bytes = d_num_bits / 8;
if (d_swap_endianness) {
for (unsigned i = 0; i < num_bytes; ++i) {
samples.push_back(crc & 0xff);
crc >>= 8;
}
}
else {
for (unsigned i = 0; i < num_bytes; ++i) {
samples.push_back((crc >> (d_num_bits - 8 * (i + 1))) & 0xff);
}
}

meta["packet_len"] = pmtf::get_as<size_t>(meta["packet_len"]) + num_bytes;
auto pdu = pmtf::map({ { "data", samples }, { "meta", meta } });

this->get_message_port("out")->post(pdu);
}
};

} // namespace digital
} // namespace gr
21 changes: 15 additions & 6 deletions blocklib/digital/meson.build
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
#autogenerated
# This file has been automatically generated and will be overwritten by meson build
# Remove the #autogenerated comment at the top if you wish for the build scripts to leave
# this file alone

subdir('include/gnuradio/digital')

digital_sources = []
digital_cu_sources = []
digital_pure_python_sources = []
digital_pybind_sources = []
digital_pybind_names = []
digital_deps = []



# Individual block subdirectories
subdir('crc_append')


subdir('lib')
# if (get_option('enable_python'))
# subdir('python/digital')
# endif
if (get_option('enable_python'))
subdir('python/digital')
endif

# if (get_option('enable_testing'))
# subdir('test')
# endif
if (get_option('enable_testing'))
subdir('test')
endif
2 changes: 1 addition & 1 deletion blocklib/digital/test/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
###################################################

if get_option('enable_testing')
# test('qa_agc', find_program('qa_agc.py'), env: TEST_ENV)
# test('qa_crc', find_program('qa_crc.py'), env: TEST_ENV)
# if (cuda_available and get_option('enable_cuda'))
# test('qa_cufft', find_program('qa_cufft.py'), env: TEST_ENV)
# endif
Expand Down
177 changes: 177 additions & 0 deletions blocklib/digital/test/qa_crc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#!/usr/bin/env python3
# Copyright 2022 Daniel Estevez <daniel@destevez.net>
#
# This file is part of GNU Radio
#
# SPDX-License-Identifier: GPL-3.0-or-later
#

# FIXME: rework once PDU support is ready in PMTF


from gnuradio import gr, gr_unittest, blocks, digital
import pmtf


class qa_crc(gr_unittest.TestCase):
def setUp(self):
"""Common part of all CRC tests

Creates a flowgraph, a Message Debug block, and a PDU
containing the numbers 0x00 through 0x0F.
"""
self.tb = gr.top_block()
self.dbg = blocks.message_debug()
self.data = list(range(16))
self.pdu = pmt.cons(pmt.PMT_NIL,
pmt.init_u8vector(len(self.data), self.data))


def run_crc_append(self, crc_params, crc_result):
"""Common part of CRC Append tests

Creates a CRC Append block with the specified crc_params parameters,
connects it to the Message Debug block, sends a test PDU to the
CRC Append block, and checks that the output PDU matches the expected
crc_result.
"""
crc_append_block = digital.crc_append(*crc_params)
self.tb.msg_connect((crc_append_block, 'out'), (self.dbg, 'store'))
crc_append_block.to_basic_block()._post(pmt.intern('in'), self.pdu)
crc_append_block.to_basic_block()._post(
pmt.intern('system'),
pmt.cons(pmt.intern('done'), pmt.from_long(1)))

self.tb.start()
self.tb.wait()

self.assertEqual(self.dbg.num_messages(), 1)
out = pmt.u8vector_elements(pmt.cdr(self.dbg.get_message(0)))
self.assertEqual(out[:len(self.data)], self.data)
self.assertEqual(out[len(self.data):], crc_result)

def common_test_crc_check(self, matching_crc, header_bytes=0):
"""Common part of CRC Check tests

Creates a CRC Append block and a CRC Check block using either the
same CRC or a different one depending on the whether matching_crc
is True or False. Connects CRC Append -> CRC Check -> Message Debug
and sends a PDU through. There are two message debugs to allow
checking whether the PDU ended up in the ok or fail port of the
CRC Check block.
"""
crc_append_block = digital.crc_append(
16, 0x1021, 0x0, 0x0, False, False, False, header_bytes)
x = 0x0 if matching_crc else 0xFFFF
crc_check_block = digital.crc_check(
16, 0x1021, x, x, False, False, False, True, header_bytes)

self.dbg_fail = blocks.message_debug()
self.tb.msg_connect((crc_append_block, 'out'), (crc_check_block, 'in'))
self.tb.msg_connect((crc_check_block, 'ok'), (self.dbg, 'store'))
self.tb.msg_connect((crc_check_block, 'fail'),
(self.dbg_fail, 'store'))

crc_append_block.to_basic_block()._post(pmt.intern('in'), self.pdu)
crc_append_block.to_basic_block()._post(
pmt.intern('system'),
pmt.cons(pmt.intern('done'), pmt.from_long(1)))
self.tb.start()
self.tb.wait()

def test_crc_check(self):
"""Test a successful CRC check

Checks that the PDU ends in the ok port of CRC check
"""
self.common_test_crc_check(matching_crc=True)
self.assertEqual(self.dbg.num_messages(), 1)
out = pmt.u8vector_elements(pmt.cdr(self.dbg.get_message(0)))
self.assertEqual(out, self.data)
self.assertEqual(self.dbg_fail.num_messages(), 0)

def test_crc_check_header_bytes(self):
"""Test a successful CRC check (skipping some header bytes)

Checks that the PDU ends in the ok port of CRC check
"""
self.common_test_crc_check(matching_crc=True, header_bytes=5)
self.assertEqual(self.dbg.num_messages(), 1)
out = pmt.u8vector_elements(pmt.cdr(self.dbg.get_message(0)))
self.assertEqual(out, self.data)
self.assertEqual(self.dbg_fail.num_messages(), 0)

def test_crc_check_wrong_crc(self):
"""Test a failed CRC check

Checks that the PDU ends in the fail port of CRC check
"""
self.common_test_crc_check(matching_crc=False)
self.assertEqual(self.dbg.num_messages(), 0)
self.assertEqual(self.dbg_fail.num_messages(), 1)
out = pmt.u8vector_elements(pmt.cdr(self.dbg_fail.get_message(0)))
self.assertEqual(out, self.data)

def test_crc_append_crc16_ccitt_zero(self):
"""Test CRC-16-CCITT-Zero calculation"""
self.run_crc_append(
(16, 0x1021, 0x0, 0x0,
False, False, False),
[0x51, 0x3D])

def test_crc_append_crc16_ccitt_false(self):
"""Test CRC-16-CCITT-False calculation"""
self.run_crc_append(
(16, 0x1021, 0xFFFF, 0x0,
False, False, False),
[0x3B, 0x37])

def test_crc_append_crc16_ccitt_x25(self):
"""Test CRC-16-CCITT-X.25 calculation"""
self.run_crc_append(
(16, 0x1021, 0xFFFF, 0xFFFF,
True, True, False),
[0x13, 0xE9])

def test_crc_append_crc32(self):
"""Test CRC-32 calculation"""
self.run_crc_append(
(32, 0x4C11DB7, 0xFFFFFFFF, 0xFFFFFFFF,
True, True, False),
[0xCE, 0xCE, 0xE2, 0x88])

def test_crc_append_crc32c(self):
"""Test CRC-32C calculation"""
self.run_crc_append(
(32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF,
True, True, False),
[0xD9, 0xC9, 0x08, 0xEB])

def test_crc_append_crc32c_endianness_swap(self):
"""Test CRC-32C calculation with endianness swapped"""
self.run_crc_append(
(32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF,
True, True, True),
[0xEB, 0x08, 0xC9, 0xD9])

def test_crc_append_crc32c_skip_header_bytes(self):
"""Test CRC-32C calculation skipping some header bytes"""
skip_bytes = 3
self.run_crc_append(
(32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF,
True, True, False, skip_bytes),
[0xE8, 0x62, 0x60, 0x68])


class qa_crc_class(gr_unittest.TestCase):
def test_crc_crc32c(self):
"""Test CRC-32C calculation (using crc class directly)"""
c = digital.crc(32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, True, True)
out = c.compute(list(range(16)))
self.assertEqual(c.compute(list(range(16))),
0xD9C908EB)


if __name__ == '__main__':
gr_unittest.run(qa_crc)
gr_unittest.run(qa_crc_class)
Loading