Add preset polish and broadcast metadata (fw 0.8.0).
Ship station reorder, DAB playing ids in presets, RDS PS/RT and DAB DLS in tuner status, Si4684 data-service read, host tests, and UI now-playing lines. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
@@ -236,6 +237,21 @@ public:
|
||||
*/
|
||||
[[nodiscard]] std::expected<Si4684FmRdsStatus, Si4684Error> readFmRds();
|
||||
|
||||
/**
|
||||
* @brief readDabServiceData — read one queued DAB data-service block.
|
||||
*
|
||||
* @dname readDabServiceData
|
||||
* @param statusOnly Poll queue depth without consuming payload.
|
||||
* @param ack Acknowledge DSRVINT when reading payload.
|
||||
* @return Data block on success, nullopt when queue empty, or Si4684Error.
|
||||
* @pubstate reads GET_DIGITAL_SERVICE_DATA response bytes.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<std::optional<Si4684DabServiceData>, Si4684Error>
|
||||
readDabServiceData(bool statusOnly, bool ack);
|
||||
|
||||
/**
|
||||
* @brief installDefaultDabFrequencyPlan — load Band III frequency list.
|
||||
*
|
||||
|
||||
@@ -12,10 +12,14 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/DabDynamicLabelAccumulator.hpp"
|
||||
#include "core/FrequencyKHz.hpp"
|
||||
#include "core/ITuner.hpp"
|
||||
#include "core/RdsMetadataAccumulator.hpp"
|
||||
#include "si4684/Si4684Driver.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace si4684 {
|
||||
|
||||
/**
|
||||
@@ -190,6 +194,10 @@ private:
|
||||
std::uint8_t dabIndex_;
|
||||
core::FrequencyKHz fmFrequency_;
|
||||
std::uint8_t volume_;
|
||||
core::RdsMetadataAccumulator rdsMetadata_;
|
||||
core::DabDynamicLabelAccumulator dabDynamicLabel_;
|
||||
std::optional<std::uint32_t> lastDabServiceId_;
|
||||
std::optional<std::uint32_t> lastDabComponentId_;
|
||||
};
|
||||
|
||||
} // namespace si4684
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace si4684 {
|
||||
|
||||
@@ -113,6 +114,27 @@ struct Si4684FmRdsStatus {
|
||||
std::uint16_t blockC; ///< RDS block C.
|
||||
std::uint16_t blockD; ///< RDS block D.
|
||||
bool received; ///< Group received flag.
|
||||
std::uint8_t fifoUsed; ///< Remaining groups in the RDS FIFO.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Si4684DabServiceData — one GET_DIGITAL_SERVICE_DATA block.
|
||||
*
|
||||
* @dname Si4684DabServiceData
|
||||
* @return n/a (type)
|
||||
* @pubstate Plain DTO from GET_DIGITAL_SERVICE_DATA response bytes.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
struct Si4684DabServiceData {
|
||||
std::uint32_t serviceId; ///< Associated DAB service identifier.
|
||||
std::uint32_t componentId; ///< Associated component identifier.
|
||||
std::uint8_t dataSrc; ///< Payload source (2 = DLS PAD).
|
||||
std::uint16_t byteCount; ///< Payload byte count.
|
||||
std::uint16_t segmentIndex; ///< Zero-based segment index.
|
||||
std::uint16_t segmentCount; ///< Total segments for this label object.
|
||||
std::vector<std::uint8_t> payload; ///< Raw payload bytes.
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -587,6 +587,7 @@ std::expected<Si4684FmRdsStatus, Si4684Error> Si4684Driver::readFmRds()
|
||||
|
||||
Si4684FmRdsStatus rds = {};
|
||||
rds.received = (raw[4] & 0x01U) != 0U;
|
||||
rds.fifoUsed = raw[10];
|
||||
rds.blockA = readLe16(raw.data() + 12);
|
||||
rds.blockB = readLe16(raw.data() + 14);
|
||||
rds.blockC = readLe16(raw.data() + 16);
|
||||
@@ -594,6 +595,60 @@ std::expected<Si4684FmRdsStatus, Si4684Error> Si4684Driver::readFmRds()
|
||||
return rds;
|
||||
}
|
||||
|
||||
std::expected<std::optional<Si4684DabServiceData>, Si4684Error>
|
||||
Si4684Driver::readDabServiceData(bool statusOnly, bool ack)
|
||||
{
|
||||
if (auto band = ensureBand(Si4684Band::Dab); !band) {
|
||||
return std::unexpected(band.error());
|
||||
}
|
||||
|
||||
const std::uint8_t arg1 =
|
||||
static_cast<std::uint8_t>((statusOnly ? 0x08U : 0x00U)
|
||||
| (ack ? 0x01U : 0x00U));
|
||||
const std::uint8_t args[] = {arg1};
|
||||
if (auto cmd =
|
||||
writeCommand(Command::GetDigitalServiceData, args, sizeof(args));
|
||||
!cmd) {
|
||||
return std::unexpected(Si4684Error::CommandFailed);
|
||||
}
|
||||
|
||||
std::array<std::uint8_t, 24> header = {};
|
||||
if (auto rd = readRaw(header); !rd) {
|
||||
return std::unexpected(rd.error());
|
||||
}
|
||||
|
||||
if (statusOnly) {
|
||||
if (header[5] == 0U) {
|
||||
return std::optional<Si4684DabServiceData>{};
|
||||
}
|
||||
}
|
||||
|
||||
const std::uint16_t byteCount = readLe16(header.data() + 18);
|
||||
if (byteCount == 0U) {
|
||||
return std::optional<Si4684DabServiceData>{};
|
||||
}
|
||||
if (byteCount + 24U > kSpiBufferSize) {
|
||||
return std::unexpected(Si4684Error::ReplyTooShort);
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> body(byteCount, 0U);
|
||||
if (byteCount > 0U) {
|
||||
if (auto rd = readRaw(body); !rd) {
|
||||
return std::unexpected(rd.error());
|
||||
}
|
||||
}
|
||||
|
||||
Si4684DabServiceData data = {};
|
||||
data.dataSrc = static_cast<std::uint8_t>((header[7] >> 6U) & 0x03U);
|
||||
data.serviceId = readLe32(header.data() + 8);
|
||||
data.componentId = readLe32(header.data() + 12);
|
||||
data.byteCount = byteCount;
|
||||
data.segmentIndex = readLe16(header.data() + 20);
|
||||
data.segmentCount = readLe16(header.data() + 22);
|
||||
data.payload = std::move(body);
|
||||
return data;
|
||||
}
|
||||
|
||||
std::expected<void, Si4684Error> Si4684Driver::installDefaultDabFrequencyPlan()
|
||||
{
|
||||
if (auto band = ensureBand(Si4684Band::Dab); !band) {
|
||||
|
||||
@@ -89,6 +89,19 @@ std::expected<core::TunerStatus, core::TunerError> Si4684Tuner::readStatus()
|
||||
} else {
|
||||
return std::unexpected(mapError(dig.error()));
|
||||
}
|
||||
|
||||
if (lastDabServiceId_) {
|
||||
if (auto data = driver_.readDabServiceData(false, true); data) {
|
||||
if (*data && (*data)->dataSrc == 2U) {
|
||||
dabDynamicLabel_.applySegment((*data)->segmentIndex,
|
||||
(*data)->segmentCount,
|
||||
(*data)->payload);
|
||||
status.dabDynamicLabel = dabDynamicLabel_.label();
|
||||
}
|
||||
} else {
|
||||
return std::unexpected(mapError(data.error()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
status.fmFrequency = fmFrequency_;
|
||||
if (auto rsq = driver_.readFmRsq(); rsq) {
|
||||
@@ -101,6 +114,25 @@ std::expected<core::TunerStatus, core::TunerError> Si4684Tuner::readStatus()
|
||||
} else {
|
||||
return std::unexpected(mapError(rsq.error()));
|
||||
}
|
||||
|
||||
for (int attempt = 0; attempt < 8; ++attempt) {
|
||||
auto rds = driver_.readFmRds();
|
||||
if (!rds) {
|
||||
return std::unexpected(mapError(rds.error()));
|
||||
}
|
||||
if (!rds->received) {
|
||||
break;
|
||||
}
|
||||
rdsMetadata_.applyGroup(rds->blockA,
|
||||
rds->blockB,
|
||||
rds->blockC,
|
||||
rds->blockD);
|
||||
if (rds->fifoUsed <= 1U) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
status.fmStationName = rdsMetadata_.programName();
|
||||
status.fmRadiotext = rdsMetadata_.radiotext();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -112,6 +144,9 @@ std::expected<void, core::TunerError> Si4684Tuner::tuneDab(
|
||||
return std::unexpected(mapError(result.error()));
|
||||
}
|
||||
dabIndex_ = freqIndex;
|
||||
dabDynamicLabel_.reset();
|
||||
lastDabServiceId_.reset();
|
||||
lastDabComponentId_.reset();
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -122,6 +157,7 @@ std::expected<void, core::TunerError> Si4684Tuner::tuneFm(
|
||||
return std::unexpected(mapError(result.error()));
|
||||
}
|
||||
fmFrequency_ = frequency;
|
||||
rdsMetadata_.reset();
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -132,6 +168,7 @@ std::expected<core::FrequencyKHz, core::TunerError> Si4684Tuner::seekFm(
|
||||
return std::unexpected(mapError(result.error()));
|
||||
}
|
||||
fmFrequency_ = *result;
|
||||
rdsMetadata_.reset();
|
||||
return *result;
|
||||
}
|
||||
|
||||
@@ -171,6 +208,9 @@ std::expected<void, core::TunerError> Si4684Tuner::playDabService(
|
||||
if (auto result = driver_.startDabService(serviceId, componentId); !result) {
|
||||
return std::unexpected(mapError(result.error()));
|
||||
}
|
||||
lastDabServiceId_ = serviceId;
|
||||
lastDabComponentId_ = componentId;
|
||||
dabDynamicLabel_.reset();
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user