Add Slice 4 tuner stack with AGENTS-compliant core types and HTTP API.
Introduces Si4684/ADAU1701 drivers, TunerService, FrequencyKHz, SeekDirection, /api/tuner routes without file-scope globals, host tests, and firmware blob tooling (binaries remain gitignored). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @file Si4684Band.hpp
|
||||
* @brief Tuner band selection for Si4684 firmware images.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace si4684 {
|
||||
|
||||
/**
|
||||
* @brief Si4684Band — application image loaded after ROM patch.
|
||||
*
|
||||
* @dname Si4684Band
|
||||
* @return n/a (type)
|
||||
* @pubstate n/a
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
enum class Si4684Band {
|
||||
Dab,
|
||||
Fm,
|
||||
};
|
||||
|
||||
} // namespace si4684
|
||||
@@ -0,0 +1,370 @@
|
||||
/**
|
||||
* @file Si4684Driver.hpp
|
||||
* @brief Si4684 DAB+/FM tuner — boot, tuning, status, and service control.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/FrequencyKHz.hpp"
|
||||
#include "core/IFirmwareBlobReader.hpp"
|
||||
#include "core/SeekDirection.hpp"
|
||||
#include "si4684/Si4684Band.hpp"
|
||||
#include "si4684/Si4684Error.hpp"
|
||||
#include "si4684/Si4684Types.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace si4684 {
|
||||
|
||||
/**
|
||||
* @brief Si4684Pins — board GPIO/SPI identifiers for the tuner.
|
||||
*
|
||||
* @dname Si4684Pins
|
||||
* @return n/a (type)
|
||||
* @pubstate Immutable wiring snapshot from board_pins.hpp.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
struct Si4684Pins {
|
||||
int spiHost; ///< ESP32 SPI host peripheral index.
|
||||
int csGpio; ///< Chip-select GPIO.
|
||||
int misoGpio; ///< MISO GPIO.
|
||||
int mosiGpio; ///< MOSI GPIO.
|
||||
int sclkGpio; ///< SCLK GPIO.
|
||||
int rstbGpio; ///< RESET# GPIO (active low).
|
||||
int intbGpio; ///< INTB GPIO (optional status interrupt).
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Si4684Driver — full Si4684 control over SPI (AN649 / PE5PVB).
|
||||
*
|
||||
* @dname Si4684Driver
|
||||
* @return n/a (type)
|
||||
* @pubstate Owns the SPI link and boot state. Register-level command bytes
|
||||
* stay private; callers use intent-level methods only.
|
||||
*
|
||||
* Implements the documented boot sequence plus band-specific tuning, property
|
||||
* access, RSQ/DIGRAD reads, and DAB service selection.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
class Si4684Driver {
|
||||
public:
|
||||
/**
|
||||
* @brief Si4684Driver — construct with board pins and firmware blobs.
|
||||
*
|
||||
* @dname Si4684Driver
|
||||
* @param pins Board SPI and GPIO wiring.
|
||||
* @param patch ROM patch blob reader for HOST_LOAD.
|
||||
* @param dabImage DAB application image reader.
|
||||
* @param fmImage FM application image reader.
|
||||
* @pubstate stores pin map and blob references; not booted until boot().
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
Si4684Driver(Si4684Pins pins,
|
||||
const core::IFirmwareBlobReader& patch,
|
||||
const core::IFirmwareBlobReader& dabImage,
|
||||
const core::IFirmwareBlobReader& fmImage);
|
||||
|
||||
/**
|
||||
* @brief ~Si4684Driver — release SPI resources.
|
||||
*
|
||||
* @dname ~Si4684Driver
|
||||
* @pubstate removes SPI device and bus when active.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
~Si4684Driver();
|
||||
|
||||
Si4684Driver(const Si4684Driver&) = delete;
|
||||
Si4684Driver& operator=(const Si4684Driver&) = delete;
|
||||
|
||||
/**
|
||||
* @brief boot — cold-start: load patch, image, and configure I/O.
|
||||
*
|
||||
* @dname boot
|
||||
* @param band DAB or FM application to load.
|
||||
* @return Ok on success, or Si4684Error.
|
||||
* @pubstate writes booted_ and loadedBand_ on success.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, Si4684Error> boot(Si4684Band band);
|
||||
|
||||
/**
|
||||
* @brief isBooted — query whether boot completed successfully.
|
||||
*
|
||||
* @dname isBooted
|
||||
* @return true after a successful boot().
|
||||
* @pubstate reads booted_.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] bool isBooted() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief loadedBand — read the active application band.
|
||||
*
|
||||
* @dname loadedBand
|
||||
* @return Si4684Band loaded by the last successful boot().
|
||||
* @pubstate reads loadedBand_.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] Si4684Band loadedBand() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief getPartInfo — read chip identity and firmware revision.
|
||||
*
|
||||
* @dname getPartInfo
|
||||
* @return Si4684PartInfo on success, or Si4684Error.
|
||||
* @pubstate reads chip via GET_PART_INFO (AN649).
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<Si4684PartInfo, Si4684Error> getPartInfo();
|
||||
|
||||
/**
|
||||
* @brief getSysState — read the running application state.
|
||||
*
|
||||
* @dname getSysState
|
||||
* @return Si4684SysState on success, or Si4684Error.
|
||||
* @pubstate reads chip via GET_SYS_STATE.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<Si4684SysState, Si4684Error> getSysState();
|
||||
|
||||
/**
|
||||
* @brief setProperty — write a Skyworks property (SET_PROPERTY 0x13).
|
||||
*
|
||||
* @dname setProperty
|
||||
* @param propertyId 16-bit property address.
|
||||
* @param value 16-bit property value.
|
||||
* @return Ok on success, or Si4684Error.
|
||||
* @pubstate writes property via SPI command.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, Si4684Error> setProperty(
|
||||
std::uint16_t propertyId, std::uint16_t value);
|
||||
|
||||
/**
|
||||
* @brief setVolume — set audio attenuator 0–63 (property 0x0300).
|
||||
*
|
||||
* @dname setVolume
|
||||
* @param level Attenuator level 0–63.
|
||||
* @return Ok on success, or Si4684Error.
|
||||
* @pubstate writes AUDIO_VOLUME property.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, Si4684Error> setVolume(std::uint8_t level);
|
||||
|
||||
/**
|
||||
* @brief tuneFm — tune FM to a validated centre frequency.
|
||||
*
|
||||
* @dname tuneFm
|
||||
* @param frequency FM centre frequency in kHz.
|
||||
* @return Ok on success, or Si4684Error::WrongBand / TuneFailed.
|
||||
* @pubstate sends FM_TUNE_FREQ and waits for STC (AN649).
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, Si4684Error> tuneFm(
|
||||
core::FrequencyKHz frequency);
|
||||
|
||||
/**
|
||||
* @brief seekFm — seek FM in the given direction.
|
||||
*
|
||||
* @dname seekFm
|
||||
* @param direction Up or Down scan direction.
|
||||
* @param wrap Band wrap behaviour for FM_SEEK_START.
|
||||
* @return New centre frequency on success, or Si4684Error.
|
||||
* @pubstate sends FM_SEEK_START and waits for STC.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<core::FrequencyKHz, Si4684Error> seekFm(
|
||||
core::SeekDirection direction, SeekBandWrap wrap);
|
||||
|
||||
/**
|
||||
* @brief readFmRsq — read FM signal quality metrics.
|
||||
*
|
||||
* @dname readFmRsq
|
||||
* @return Si4684FmRsq on success, or Si4684Error.
|
||||
* @pubstate reads FM_RSQ_STATUS response bytes.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<Si4684FmRsq, Si4684Error> readFmRsq();
|
||||
|
||||
/**
|
||||
* @brief readFmRds — read a raw RDS group snapshot.
|
||||
*
|
||||
* @dname readFmRds
|
||||
* @return Si4684FmRdsStatus on success, or Si4684Error.
|
||||
* @pubstate reads FM_RDS_STATUS response bytes.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<Si4684FmRdsStatus, Si4684Error> readFmRds();
|
||||
|
||||
/**
|
||||
* @brief installDefaultDabFrequencyPlan — load Band III frequency list.
|
||||
*
|
||||
* @dname installDefaultDabFrequencyPlan
|
||||
* @return Ok on success, or Si4684Error.
|
||||
* @pubstate sends DAB_SET_FREQ_LIST with kDefaultDabFrequencyKhz.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, Si4684Error> installDefaultDabFrequencyPlan();
|
||||
|
||||
/**
|
||||
* @brief tuneDab — tune to a Band III ensemble index.
|
||||
*
|
||||
* @dname tuneDab
|
||||
* @param freqIndex Ensemble index 0–37.
|
||||
* @return Ok on success, or Si4684Error.
|
||||
* @pubstate sends DAB_TUNE_FREQ and waits for STC.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, Si4684Error> tuneDab(std::uint8_t freqIndex);
|
||||
|
||||
/**
|
||||
* @brief readDabDigRadStatus — read ensemble lock metrics.
|
||||
*
|
||||
* @dname readDabDigRadStatus
|
||||
* @return Si4684DabDigRadStatus on success, or Si4684Error.
|
||||
* @pubstate reads DAB_DIGRAD_STATUS response bytes.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<Si4684DabDigRadStatus, Si4684Error>
|
||||
readDabDigRadStatus();
|
||||
|
||||
/**
|
||||
* @brief readDabEventStatus — read DAB event flags.
|
||||
*
|
||||
* @dname readDabEventStatus
|
||||
* @return Si4684DabEventStatus on success, or Si4684Error.
|
||||
* @pubstate reads DAB_GET_EVENT_STATUS response bytes.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<Si4684DabEventStatus, Si4684Error>
|
||||
readDabEventStatus();
|
||||
|
||||
/**
|
||||
* @brief fetchDabServiceList — retrieve programmes for the ensemble.
|
||||
*
|
||||
* @dname fetchDabServiceList
|
||||
* @return Service rows on success, or Si4684Error.
|
||||
* @pubstate reads GET_DIGITAL_SERVICE_LIST chunks from the chip.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<std::vector<Si4684DabService>, Si4684Error>
|
||||
fetchDabServiceList();
|
||||
|
||||
/**
|
||||
* @brief startDabService — start DAB audio for a programme.
|
||||
*
|
||||
* @dname startDabService
|
||||
* @param serviceId Selected service identifier.
|
||||
* @param componentId Audio component within the service.
|
||||
* @param type Digital service type (audio by default).
|
||||
* @return Ok on success, or Si4684Error.
|
||||
* @pubstate sends START_DIGITAL_SERVICE.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, Si4684Error> startDabService(
|
||||
std::uint32_t serviceId,
|
||||
std::uint32_t componentId,
|
||||
Si4684DigitalServiceType type = Si4684DigitalServiceType::Audio);
|
||||
|
||||
private:
|
||||
enum class Command : std::uint8_t {
|
||||
PowerUp = 0x01,
|
||||
HostLoad = 0x04,
|
||||
LoadInit = 0x06,
|
||||
BootCmd = 0x07,
|
||||
GetPartInfo = 0x08,
|
||||
GetSysState = 0x09,
|
||||
GetFuncInfo = 0x12,
|
||||
SetProperty = 0x13,
|
||||
FmTuneFreq = 0x30,
|
||||
FmSeekStart = 0x31,
|
||||
FmRsqStatus = 0x32,
|
||||
FmRdsStatus = 0x34,
|
||||
GetDigitalServiceList = 0x80,
|
||||
StartDigitalService = 0x81,
|
||||
GetDigitalServiceData = 0x84,
|
||||
DabTuneFreq = 0xB0,
|
||||
DabDigRadStatus = 0xB2,
|
||||
DabGetEventStatus = 0xB3,
|
||||
DabSetFreqList = 0xB8,
|
||||
};
|
||||
|
||||
[[nodiscard]] std::expected<void, Si4684Error> ensureBooted() const;
|
||||
[[nodiscard]] std::expected<void, Si4684Error> ensureBand(
|
||||
Si4684Band band) const;
|
||||
[[nodiscard]] std::expected<void, Si4684Error> waitCts();
|
||||
[[nodiscard]] std::expected<void, Si4684Error> waitStc();
|
||||
[[nodiscard]] std::expected<void, Si4684Error> sendCommand(
|
||||
std::span<const std::uint8_t> bytes);
|
||||
[[nodiscard]] std::expected<void, Si4684Error> readRaw(
|
||||
std::span<std::uint8_t> buffer);
|
||||
[[nodiscard]] std::expected<void, Si4684Error> writeCommand(
|
||||
Command cmd, const std::uint8_t* payload, std::size_t length);
|
||||
[[nodiscard]] std::expected<void, Si4684Error> hostLoadBlob(
|
||||
const core::IFirmwareBlobReader& blob, std::size_t chunkPayload);
|
||||
[[nodiscard]] std::expected<void, Si4684Error> configureAfterBoot(
|
||||
Si4684Band band);
|
||||
|
||||
Si4684Pins pins_;
|
||||
const core::IFirmwareBlobReader& patch_;
|
||||
const core::IFirmwareBlobReader& dabImage_;
|
||||
const core::IFirmwareBlobReader& fmImage_;
|
||||
bool booted_;
|
||||
Si4684Band loadedBand_;
|
||||
bool spiBusActive_;
|
||||
void* spiDevice_;
|
||||
};
|
||||
|
||||
} // namespace si4684
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* @file Si4684EmbeddedImages.hpp
|
||||
* @brief Embedded Si4684 ROM patch, DAB and FM firmware blob accessors.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/EmbeddedBlobReader.hpp"
|
||||
#include "si4684/Si4684Band.hpp"
|
||||
|
||||
namespace si4684 {
|
||||
|
||||
/**
|
||||
* @brief Si4684EmbeddedImages — flash-backed Si4684 firmware blobs.
|
||||
*
|
||||
* @dname Si4684EmbeddedImages
|
||||
* @return n/a (type)
|
||||
* @pubstate Owns EmbeddedBlobReader views over EMBED_FILES symbols from
|
||||
* Firmware/Si4684-Firmware/.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
class Si4684EmbeddedImages {
|
||||
public:
|
||||
/**
|
||||
* @brief Si4684EmbeddedImages — bind linker-embedded binaries.
|
||||
*
|
||||
* @dname Si4684EmbeddedImages
|
||||
* @pubstate constructs patch_, dab_, and fm_ readers from flash symbols.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
Si4684EmbeddedImages();
|
||||
|
||||
/**
|
||||
* @brief romPatch — ROM patch blob for HOST_LOAD before main image.
|
||||
*
|
||||
* @dname romPatch
|
||||
* @return Reader over rom_patch_016.bin.
|
||||
* @pubstate reads patch_.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] const core::IFirmwareBlobReader& romPatch() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief dabFirmware — DAB application image blob.
|
||||
*
|
||||
* @dname dabFirmware
|
||||
* @return Reader over dab_firmware.bin.
|
||||
* @pubstate reads dab_.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] const core::IFirmwareBlobReader& dabFirmware() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief fmFirmware — FM application image blob.
|
||||
*
|
||||
* @dname fmFirmware
|
||||
* @return Reader over fm_firmware.bin.
|
||||
* @pubstate reads fm_.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] const core::IFirmwareBlobReader& fmFirmware() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief applicationImage — map band to embedded application blob.
|
||||
*
|
||||
* @dname applicationImage
|
||||
* @param band DAB or FM image selector.
|
||||
* @return Reader for the selected application firmware.
|
||||
* @pubstate reads dab_ or fm_.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] const core::IFirmwareBlobReader& applicationImage(
|
||||
Si4684Band band) const noexcept;
|
||||
|
||||
private:
|
||||
core::EmbeddedBlobReader patch_;
|
||||
core::EmbeddedBlobReader dab_;
|
||||
core::EmbeddedBlobReader fm_;
|
||||
};
|
||||
|
||||
} // namespace si4684
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @file Si4684Error.hpp
|
||||
* @brief Typed errors for Si4684 driver operations.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace si4684 {
|
||||
|
||||
/**
|
||||
* @brief Si4684Error — failure causes for Si4684 bring-up and tuning.
|
||||
*
|
||||
* @dname Si4684Error
|
||||
* @return n/a (type)
|
||||
* @pubstate n/a
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
enum class Si4684Error {
|
||||
SpiInitFailed,
|
||||
ResetFailed,
|
||||
CtsTimeout,
|
||||
PowerUpFailed,
|
||||
PatchLoadFailed,
|
||||
ImageLoadFailed,
|
||||
BootFailed,
|
||||
NotBooted,
|
||||
WrongBand,
|
||||
CommandFailed,
|
||||
StcTimeout,
|
||||
TuneFailed,
|
||||
ReplyTooShort,
|
||||
BufferTooSmall,
|
||||
};
|
||||
|
||||
} // namespace si4684
|
||||
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* @file Si4684Tuner.hpp
|
||||
* @brief core::ITuner adapter over Si4684Driver.
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/FrequencyKHz.hpp"
|
||||
#include "core/ITuner.hpp"
|
||||
#include "si4684/Si4684Driver.hpp"
|
||||
|
||||
namespace si4684 {
|
||||
|
||||
/**
|
||||
* @brief Si4684Tuner — maps Si4684Driver to core::ITuner.
|
||||
*
|
||||
* @dname Si4684Tuner
|
||||
* @param driver Borrowed Si4684Driver (must outlive this adapter).
|
||||
* @return n/a (type)
|
||||
* @pubstate Borrows driver_. Caches last DAB index, FM frequency, and volume
|
||||
* for status reporting. No public data members.
|
||||
*
|
||||
* Translates domain calls into SPI commands and maps Si4684Error to
|
||||
* core::TunerError at this boundary.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
class Si4684Tuner : public core::ITuner {
|
||||
public:
|
||||
/**
|
||||
* @brief Si4684Tuner — bind to a booted or bootable driver instance.
|
||||
*
|
||||
* @dname Si4684Tuner
|
||||
* @param driver Si4684 driver constructed by HardwareBootstrap.
|
||||
* @pubstate stores driver reference; sets default tune targets.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
explicit Si4684Tuner(Si4684Driver& driver);
|
||||
|
||||
/**
|
||||
* @brief boot — load the requested Si4684 application image.
|
||||
*
|
||||
* @dname boot
|
||||
* @param band DAB or FM image to load.
|
||||
* @return Ok on success, or a mapped TunerError.
|
||||
* @pubstate delegates to driver_.boot().
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, core::TunerError> boot(
|
||||
core::TunerBand band) override;
|
||||
|
||||
/**
|
||||
* @brief currentBand — read the loaded application band.
|
||||
*
|
||||
* @dname currentBand
|
||||
* @return Active TunerBand, or TunerError::NotBooted.
|
||||
* @pubstate reads driver_.loadedBand().
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<core::TunerBand, core::TunerError> currentBand()
|
||||
const override;
|
||||
|
||||
/**
|
||||
* @brief readStatus — build a core TunerStatus from driver metrics.
|
||||
*
|
||||
* @dname readStatus
|
||||
* @return TunerStatus on success, or a mapped TunerError.
|
||||
* @pubstate reads driver_; refreshes cached tune targets from RSQ/DIGRAD.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<core::TunerStatus, core::TunerError> readStatus()
|
||||
override;
|
||||
|
||||
/**
|
||||
* @brief tuneDab — tune to a Band III ensemble index.
|
||||
*
|
||||
* @dname tuneDab
|
||||
* @param freqIndex Ensemble index 0–37.
|
||||
* @return Ok on success, or a mapped TunerError.
|
||||
* @pubstate writes dabIndex_ on success.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, core::TunerError> tuneDab(
|
||||
std::uint8_t freqIndex) override;
|
||||
|
||||
/**
|
||||
* @brief tuneFm — tune to an FM centre frequency in kHz.
|
||||
*
|
||||
* @dname tuneFm
|
||||
* @param frequency Validated FM centre frequency.
|
||||
* @return Ok on success, or a mapped TunerError.
|
||||
* @pubstate writes fmFrequency_ on success.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, core::TunerError> tuneFm(
|
||||
core::FrequencyKHz frequency) override;
|
||||
|
||||
/**
|
||||
* @brief seekFm — seek FM with band wrap.
|
||||
*
|
||||
* @dname seekFm
|
||||
* @param direction Up or Down scan direction.
|
||||
* @return New centre frequency, or a mapped TunerError.
|
||||
* @pubstate writes fmFrequency_ on success.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<core::FrequencyKHz, core::TunerError> seekFm(
|
||||
core::SeekDirection direction) override;
|
||||
|
||||
/**
|
||||
* @brief listDabServices — fetch programmes for the current ensemble.
|
||||
*
|
||||
* @dname listDabServices
|
||||
* @return Service entries, ServiceListEmpty, or a mapped TunerError.
|
||||
* @pubstate reads driver_ service list when ready.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<std::vector<core::TunerServiceEntry>,
|
||||
core::TunerError>
|
||||
listDabServices() override;
|
||||
|
||||
/**
|
||||
* @brief playDabService — start DAB audio output.
|
||||
*
|
||||
* @dname playDabService
|
||||
* @param serviceId Selected service identifier.
|
||||
* @param componentId Audio component within the service.
|
||||
* @return Ok on success, or a mapped TunerError.
|
||||
* @pubstate delegates to driver_.startDabService().
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, core::TunerError> playDabService(
|
||||
std::uint32_t serviceId, std::uint32_t componentId) override;
|
||||
|
||||
/**
|
||||
* @brief setVolume — set Si4684 output attenuation.
|
||||
*
|
||||
* @dname setVolume
|
||||
* @param level Attenuator 0–63.
|
||||
* @return Ok on success, or a mapped TunerError.
|
||||
* @pubstate writes volume_ on success.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] std::expected<void, core::TunerError> setVolume(
|
||||
std::uint8_t level) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief mapError — translate Si4684Error to core::TunerError.
|
||||
*
|
||||
* @dname mapError
|
||||
* @param error Driver-level failure cause.
|
||||
* @return Equivalent TunerError for services and HTTP layer.
|
||||
* @pubstate none
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
[[nodiscard]] static core::TunerError mapError(Si4684Error error) noexcept;
|
||||
|
||||
Si4684Driver& driver_;
|
||||
std::uint8_t dabIndex_;
|
||||
core::FrequencyKHz fmFrequency_;
|
||||
std::uint8_t volume_;
|
||||
};
|
||||
|
||||
} // namespace si4684
|
||||
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* @file Si4684Types.hpp
|
||||
* @brief Domain types for Si4684 tuner operations (DAB/FM status, services).
|
||||
*
|
||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||
*
|
||||
* Copyright 2026 Michele Bigi
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/FrequencyKHz.hpp"
|
||||
#include "core/SeekDirection.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace si4684 {
|
||||
|
||||
/**
|
||||
* @brief Si4684DigitalServiceType — START_DIGITAL_SERVICE mode byte.
|
||||
*
|
||||
* @dname Si4684DigitalServiceType
|
||||
* @return n/a (type)
|
||||
* @pubstate n/a
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
enum class Si4684DigitalServiceType : std::uint8_t {
|
||||
Audio = 0x00,
|
||||
Packet = 0x01,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief SeekBandWrap — FM seek band-wrap behaviour (FM_SEEK_START).
|
||||
*
|
||||
* @dname SeekBandWrap
|
||||
* @return n/a (type)
|
||||
* @pubstate n/a
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
enum class SeekBandWrap { Wrap, NoWrap };
|
||||
|
||||
/**
|
||||
* @brief Si4684PartInfo — chip identity from GET_PART_INFO (AN649).
|
||||
*
|
||||
* @dname Si4684PartInfo
|
||||
* @return n/a (type)
|
||||
* @pubstate Plain DTO from GET_PART_INFO response bytes.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
struct Si4684PartInfo {
|
||||
std::uint16_t chipId; ///< Part identifier from the chip.
|
||||
std::uint8_t firmwareMajor; ///< Loaded firmware major version.
|
||||
std::uint8_t firmwareMinor; ///< Loaded firmware minor version.
|
||||
std::uint8_t firmwareBuild; ///< Loaded firmware build number.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Si4684SysState — running application after boot.
|
||||
*
|
||||
* @dname Si4684SysState
|
||||
* @return n/a (type)
|
||||
* @pubstate Plain DTO from GET_SYS_STATE.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
struct Si4684SysState {
|
||||
std::uint8_t imageType; ///< Loaded image type byte from the chip.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Si4684FmRsq — FM received-signal quality (FM_RSQ_STATUS).
|
||||
*
|
||||
* @dname Si4684FmRsq
|
||||
* @return n/a (type)
|
||||
* @pubstate Plain DTO from FM_RSQ_STATUS response bytes.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
struct Si4684FmRsq {
|
||||
core::FrequencyKHz frequency; ///< Tuned centre frequency in kHz.
|
||||
std::int8_t rssiDbuV; ///< RSSI in dBµV.
|
||||
std::int8_t snrDb; ///< SNR in dB.
|
||||
bool valid; ///< RSQ valid flag from the chip.
|
||||
bool stereo; ///< Stereo pilot detected.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Si4684FmRdsStatus — raw RDS group snapshot (FM_RDS_STATUS).
|
||||
*
|
||||
* @dname Si4684FmRdsStatus
|
||||
* @return n/a (type)
|
||||
* @pubstate Plain DTO from FM_RDS_STATUS response bytes.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
struct Si4684FmRdsStatus {
|
||||
std::uint16_t blockA; ///< RDS block A.
|
||||
std::uint16_t blockB; ///< RDS block B.
|
||||
std::uint16_t blockC; ///< RDS block C.
|
||||
std::uint16_t blockD; ///< RDS block D.
|
||||
bool received; ///< Group received flag.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Si4684DabDigRadStatus — ensemble lock metrics (DAB_DIGRAD_STATUS).
|
||||
*
|
||||
* @dname Si4684DabDigRadStatus
|
||||
* @return n/a (type)
|
||||
* @pubstate Plain DTO from DAB_DIGRAD_STATUS response bytes.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
struct Si4684DabDigRadStatus {
|
||||
std::uint8_t ficQuality; ///< FIC quality 0–100.
|
||||
std::uint8_t cnrDb; ///< CNR in dB.
|
||||
bool acquired; ///< Ensemble acquired flag.
|
||||
bool valid; ///< DIGRAD valid flag.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Si4684DabEventStatus — DAB event flags (DAB_GET_EVENT_STATUS).
|
||||
*
|
||||
* @dname Si4684DabEventStatus
|
||||
* @return n/a (type)
|
||||
* @pubstate Plain DTO from DAB_GET_EVENT_STATUS response bytes.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
struct Si4684DabEventStatus {
|
||||
bool serviceListReady; ///< Service list available for fetch.
|
||||
bool reconfig; ///< Ensemble reconfiguration event.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Si4684DabService — one row from GET_DIGITAL_SERVICE_LIST.
|
||||
*
|
||||
* @dname Si4684DabService
|
||||
* @return n/a (type)
|
||||
* @pubstate Plain DTO parsed from a service-list chunk.
|
||||
*
|
||||
* @author Michele Bigi
|
||||
* @date 2026-07-06
|
||||
*/
|
||||
struct Si4684DabService {
|
||||
std::uint32_t serviceId; ///< DAB service identifier.
|
||||
std::uint32_t componentId; ///< Audio component identifier.
|
||||
std::array<char, 17> label; ///< Programme label, NUL-terminated.
|
||||
std::uint8_t serviceType; ///< Service type byte from the list.
|
||||
};
|
||||
|
||||
/** Band III DAB channel plan (kHz), PE5PVB / ETSI EN 300 401 Table 14. */
|
||||
inline constexpr std::array<std::uint32_t, 38> kDefaultDabFrequencyKhz = {
|
||||
174928, 176640, 178352, 180064, 181936, 183648, 185360, 187072, 188928,
|
||||
190640, 192352, 194064, 195936, 197648, 199360, 201072, 202928, 204640,
|
||||
206352, 208064, 209936, 211648, 213360, 215072, 216928, 218640, 220352,
|
||||
222064, 223936, 225648, 227360, 229072, 230784, 232496, 234208, 235776,
|
||||
237488, 239200,
|
||||
};
|
||||
|
||||
} // namespace si4684
|
||||
Reference in New Issue
Block a user