parent
794e8b71e5
commit
105e77d7bd
@ -0,0 +1,131 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - Test Suite
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "host/Defines.h"
|
||||
#include "common/p25/P25Defines.h"
|
||||
#include "common/p25/dfsi/DFSIDefines.h"
|
||||
#include "common/p25/dfsi/frames/StartOfStream.h"
|
||||
#include "common/p25/dfsi/frames/FullRateVoice.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace p25;
|
||||
using namespace p25::defines;
|
||||
using namespace p25::dfsi;
|
||||
using namespace p25::dfsi::defines;
|
||||
using namespace p25::dfsi::frames;
|
||||
|
||||
TEST_CASE("DFSI StartOfStream and FullRateVoice encode/decode round-trips", "[p25][dfsi][frames]")
|
||||
{
|
||||
SECTION("StartOfStream preserves NID and low nibble error count") {
|
||||
StartOfStream tx;
|
||||
tx.setNID(0xA55AU);
|
||||
tx.setErrorCount(0x1DU);
|
||||
|
||||
uint8_t buffer[StartOfStream::LENGTH] = { 0U };
|
||||
tx.encode(buffer);
|
||||
|
||||
StartOfStream rx;
|
||||
REQUIRE(rx.decode(buffer));
|
||||
|
||||
REQUIRE(rx.getNID() == 0xA55AU);
|
||||
REQUIRE(rx.getErrorCount() == 0x0DU);
|
||||
}
|
||||
|
||||
SECTION("FullRateVoice LDU1 voice3 includes four additional bytes") {
|
||||
FullRateVoice tx;
|
||||
tx.setFrameType(DFSIFrameType::LDU1_VOICE3);
|
||||
tx.setTotalErrors(5U);
|
||||
tx.setMuteFrame(true);
|
||||
tx.setLostFrame(false);
|
||||
tx.setSuperframeCnt(2U);
|
||||
tx.setBusy(DFSI_BUSY_BITS_BUSY);
|
||||
|
||||
for (uint8_t i = 0U; i < FullRateVoice::IMBE_BUF_LEN; i++)
|
||||
tx.imbeData[i] = (uint8_t)(0x20U + i);
|
||||
|
||||
tx.additionalData[0U] = 0x11U;
|
||||
tx.additionalData[1U] = 0x22U;
|
||||
tx.additionalData[2U] = 0x33U;
|
||||
tx.additionalData[3U] = 0x44U;
|
||||
|
||||
uint8_t buffer[FullRateVoice::LENGTH] = { 0U };
|
||||
tx.encode(buffer);
|
||||
|
||||
FullRateVoice rx;
|
||||
REQUIRE(rx.decode(buffer));
|
||||
|
||||
REQUIRE(rx.getFrameType() == DFSIFrameType::LDU1_VOICE3);
|
||||
REQUIRE(rx.getLength() == FullRateVoice::LENGTH);
|
||||
REQUIRE(rx.getTotalErrors() == 5U);
|
||||
REQUIRE(rx.getMuteFrame() == true);
|
||||
REQUIRE(rx.getLostFrame() == false);
|
||||
REQUIRE(rx.getSuperframeCnt() == 2U);
|
||||
REQUIRE(rx.getBusy() == DFSI_BUSY_BITS_BUSY);
|
||||
REQUIRE(::memcmp(rx.imbeData, tx.imbeData, FullRateVoice::IMBE_BUF_LEN) == 0);
|
||||
REQUIRE(::memcmp(rx.additionalData, tx.additionalData, FullRateVoice::ADDITIONAL_LENGTH) == 0);
|
||||
}
|
||||
|
||||
SECTION("FullRateVoice LDU1 voice9 uses three-byte additional payload") {
|
||||
FullRateVoice tx;
|
||||
tx.setFrameType(DFSIFrameType::LDU1_VOICE9);
|
||||
tx.setTotalErrors(1U);
|
||||
tx.setMuteFrame(false);
|
||||
tx.setLostFrame(true);
|
||||
tx.setSuperframeCnt(1U);
|
||||
tx.setBusy(DFSI_BUSY_BITS_INBOUND);
|
||||
|
||||
for (uint8_t i = 0U; i < FullRateVoice::IMBE_BUF_LEN; i++)
|
||||
tx.imbeData[i] = (uint8_t)(0x40U + i);
|
||||
|
||||
tx.additionalData[0U] = 0xAAU;
|
||||
tx.additionalData[1U] = 0xBBU;
|
||||
tx.additionalData[2U] = 0xCCU;
|
||||
tx.additionalData[3U] = 0xDDU;
|
||||
|
||||
uint8_t buffer[FullRateVoice::LENGTH_918] = { 0U };
|
||||
tx.encode(buffer);
|
||||
|
||||
FullRateVoice rx;
|
||||
REQUIRE(rx.decode(buffer));
|
||||
|
||||
REQUIRE(rx.getFrameType() == DFSIFrameType::LDU1_VOICE9);
|
||||
REQUIRE(rx.getLength() == FullRateVoice::LENGTH_918);
|
||||
REQUIRE(rx.additionalData != nullptr);
|
||||
REQUIRE(rx.additionalData[0U] == 0xAAU);
|
||||
REQUIRE(rx.additionalData[1U] == 0xBBU);
|
||||
REQUIRE(rx.additionalData[2U] == 0xCCU);
|
||||
REQUIRE(rx.additionalData[3U] == 0x00U);
|
||||
}
|
||||
|
||||
SECTION("FullRateVoice LDU1 voice2 has no additional payload") {
|
||||
FullRateVoice tx;
|
||||
tx.setFrameType(DFSIFrameType::LDU1_VOICE2);
|
||||
tx.setTotalErrors(7U);
|
||||
tx.setMuteFrame(false);
|
||||
tx.setLostFrame(false);
|
||||
tx.setSuperframeCnt(3U);
|
||||
tx.setBusy(DFSI_BUSY_BITS_IDLE);
|
||||
|
||||
for (uint8_t i = 0U; i < FullRateVoice::IMBE_BUF_LEN; i++)
|
||||
tx.imbeData[i] = (uint8_t)(0x60U + i);
|
||||
|
||||
uint8_t buffer[FullRateVoice::LENGTH_121011] = { 0U };
|
||||
tx.encode(buffer);
|
||||
|
||||
FullRateVoice rx;
|
||||
REQUIRE(rx.decode(buffer));
|
||||
|
||||
REQUIRE(rx.getFrameType() == DFSIFrameType::LDU1_VOICE2);
|
||||
REQUIRE(rx.getLength() == FullRateVoice::LENGTH_121011);
|
||||
REQUIRE(rx.additionalData == nullptr);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Digital Voice Modem - Test Suite
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* Copyright (C) 2026 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#include "host/Defines.h"
|
||||
#include "common/p25/P25Defines.h"
|
||||
#include "common/p25/kmm/KMMFactory.h"
|
||||
#include "common/p25/kmm/KMMHello.h"
|
||||
#include "common/p25/kmm/KMMNoService.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace p25;
|
||||
using namespace p25::defines;
|
||||
using namespace p25::kmm;
|
||||
|
||||
TEST_CASE("KMM factory decodes encoded HELLO and NO_SERVICE frames", "[p25][kmm][factory]")
|
||||
{
|
||||
SECTION("HELLO frame round-trips with message number") {
|
||||
KMMHello tx;
|
||||
tx.setDstLLId(0x123456U);
|
||||
tx.setSrcLLId(0x654321U);
|
||||
tx.setMessageNumber(0x1122U);
|
||||
tx.setFlag(KMM_HelloFlag::REKEY_REQUEST_NO_UKEK);
|
||||
|
||||
UInt8Array buffer = std::make_unique<uint8_t[]>(tx.fullLength());
|
||||
::memset(buffer.get(), 0x00U, tx.fullLength());
|
||||
tx.encode(buffer.get());
|
||||
|
||||
std::unique_ptr<KMMFrame> base = KMMFactory::create(buffer.get());
|
||||
REQUIRE(base != nullptr);
|
||||
|
||||
KMMHello* rx = dynamic_cast<KMMHello*>(base.get());
|
||||
REQUIRE(rx != nullptr);
|
||||
REQUIRE(rx->getMessageId() == KMM_MessageType::HELLO);
|
||||
REQUIRE(rx->getFlag() == KMM_HelloFlag::REKEY_REQUEST_NO_UKEK);
|
||||
REQUIRE(rx->getDstLLId() == 0x123456U);
|
||||
REQUIRE(rx->getSrcLLId() == 0x654321U);
|
||||
REQUIRE(rx->getHasMessageNumber() == true);
|
||||
REQUIRE(rx->getMessageNumber() == 0x1122U);
|
||||
}
|
||||
|
||||
SECTION("NO_SERVICE frame round-trips") {
|
||||
KMMNoService tx;
|
||||
tx.setDstLLId(0x010203U);
|
||||
tx.setSrcLLId(0xA0B0C0U);
|
||||
|
||||
UInt8Array buffer = std::make_unique<uint8_t[]>(tx.fullLength());
|
||||
::memset(buffer.get(), 0x00U, tx.fullLength());
|
||||
tx.encode(buffer.get());
|
||||
|
||||
std::unique_ptr<KMMFrame> base = KMMFactory::create(buffer.get());
|
||||
REQUIRE(base != nullptr);
|
||||
|
||||
KMMNoService* rx = dynamic_cast<KMMNoService*>(base.get());
|
||||
REQUIRE(rx != nullptr);
|
||||
REQUIRE(rx->getMessageId() == KMM_MessageType::NO_SERVICE);
|
||||
REQUIRE(rx->getDstLLId() == 0x010203U);
|
||||
REQUIRE(rx->getSrcLLId() == 0xA0B0C0U);
|
||||
}
|
||||
|
||||
SECTION("Factory returns nullptr for unknown message ID") {
|
||||
uint8_t buffer[16U] = { 0U };
|
||||
buffer[0U] = 0xFFU;
|
||||
|
||||
std::unique_ptr<KMMFrame> frame = KMMFactory::create(buffer);
|
||||
REQUIRE(frame == nullptr);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue