Add updatable ADAU1701 program via flash partition and HTTP API.
Decouple the SigmaStudio RAM download from compiled-in adau1701_program.c: DRAD v1 blobs in the dsp partition with embedded fallback, POST /api/dsp/program, and host-tested blob parse/serialize helpers. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -46,11 +46,18 @@ add_library(digiradio_core STATIC
|
||||
"${CORE_SRC_DIR}/BroadcastLabel.cpp"
|
||||
"${CORE_SRC_DIR}/RdsMetadataAccumulator.cpp"
|
||||
"${CORE_SRC_DIR}/DabDynamicLabelAccumulator.cpp"
|
||||
"${CORE_SRC_DIR}/DspProgram.cpp"
|
||||
"${CORE_SRC_DIR}/RegisterWrite.cpp"
|
||||
"${CORE_SRC_DIR}/DspProgramBlob.cpp"
|
||||
)
|
||||
target_include_directories(digiradio_core PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../include"
|
||||
)
|
||||
|
||||
add_executable(dsp_program_blob_test dsp_program_blob_test.cpp)
|
||||
target_link_libraries(dsp_program_blob_test PRIVATE digiradio_core)
|
||||
add_test(NAME dsp_program_blob_test COMMAND dsp_program_blob_test)
|
||||
|
||||
add_executable(device_identity_test device_identity_test.cpp)
|
||||
target_link_libraries(device_identity_test PRIVATE digiradio_core)
|
||||
add_test(NAME device_identity_test COMMAND device_identity_test)
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* @file dsp_program_blob_test.cpp
|
||||
* @brief Host tests for ADAU1701 program blob parse/serialise.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-07
|
||||
*/
|
||||
|
||||
#include "core/DspProgram.hpp"
|
||||
#include "core/DspProgramBlob.hpp"
|
||||
#include "core/DspProgramError.hpp"
|
||||
#include "core/RegisterWrite.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] core::DspProgram sampleProgram()
|
||||
{
|
||||
return core::DspProgram(std::vector<core::RegisterWrite>{
|
||||
core::RegisterWrite(0x081CU, {0x00U, 0x1CU}),
|
||||
core::RegisterWrite(0x0400U, std::vector<std::uint8_t>(16U, 0xABU)),
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] int runRoundTripTest()
|
||||
{
|
||||
const core::DspProgram original = sampleProgram();
|
||||
const std::vector<std::uint8_t> blob =
|
||||
core::serializeDspProgramBlob(original);
|
||||
const auto parsed = core::parseDspProgramBlob(blob);
|
||||
if (!parsed) {
|
||||
std::cerr << "round-trip parse failed\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (parsed->writes().size() != original.writes().size()) {
|
||||
std::cerr << "write count mismatch\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
[[nodiscard]] int runEmptyTest()
|
||||
{
|
||||
const std::vector<std::uint8_t> empty;
|
||||
const auto parsed = core::parseDspProgramBlob(empty);
|
||||
if (parsed || parsed.error() != core::DspProgramError::Empty) {
|
||||
std::cerr << "expected empty error\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
[[nodiscard]] int runTruncatedTest()
|
||||
{
|
||||
const std::vector<std::uint8_t> blob = {'D', 'R', 'A', 'D', 1, 0, 1, 0};
|
||||
const auto parsed = core::parseDspProgramBlob(blob);
|
||||
if (parsed || parsed.error() != core::DspProgramError::Truncated) {
|
||||
std::cerr << "expected truncated error\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
[[nodiscard]] int runBadCrcTest()
|
||||
{
|
||||
std::vector<std::uint8_t> blob = core::serializeDspProgramBlob(sampleProgram());
|
||||
blob.back() ^= 0xFFU;
|
||||
const auto parsed = core::parseDspProgramBlob(blob);
|
||||
if (parsed || parsed.error() != core::DspProgramError::BadCrc) {
|
||||
std::cerr << "expected bad_crc error\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
[[nodiscard]] int runBadMagicTest()
|
||||
{
|
||||
std::vector<std::uint8_t> blob = core::serializeDspProgramBlob(sampleProgram());
|
||||
blob[0] = 'X';
|
||||
const auto parsed = core::parseDspProgramBlob(blob);
|
||||
if (parsed || parsed.error() != core::DspProgramError::InvalidMagic) {
|
||||
std::cerr << "expected invalid_magic error\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
if (runRoundTripTest() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (runEmptyTest() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (runTruncatedTest() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (runBadCrcTest() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (runBadMagicTest() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user