pre-agent-version

This commit is contained in:
2026-07-06 07:22:56 +02:00
parent 0ff803b4c3
commit 1b225dde1d
13 changed files with 931 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
\chapter{Building and Flashing}
\label{ch:build}
The firmware is built with ESP-IDF; the hardware-free domain core also
builds and is unit-tested on the host machine.
\section{Toolchain}
\begin{table}[htbp]
\centering
\begin{tabular}{@{}ll@{}}
\drhead Item & Choice \\
\midrule
Framework & ESP-IDF v5.5.x (native) \\
Language & C++23 (\texttt{-std=gnu++23}) \\
Error model & \texttt{std::expected}; exceptions off \\
Documentation & Doxygen (build must pass) \\
\bottomrule
\end{tabular}
\caption{Firmware toolchain.}
\label{tab:build-toolchain}
\end{table}
\section{Device build}
\begin{drcode}[Build, flash, monitor]
idf.py set-target esp32s3
idf.py build
idf.py -p <port> flash monitor
\end{drcode}
\section{Host unit tests}
The pure core is tested on the host, with no board attached. On macOS the
tests need a C++23 standard library --- use a Homebrew LLVM (>= 18) or
GCC~14 rather than the system Apple Clang.
\begin{drcode}[Host tests]
cmake -S components/core/test -B build-host \
-DCMAKE_CXX_COMPILER="$(brew --prefix llvm)/bin/clang++"
cmake --build build-host
ctest --test-dir build-host --output-on-failure
\end{drcode}
\section{Documentation}
The API documentation is generated with Doxygen and must build cleanly;
an undocumented class, method, or parameter fails the build.
\begin{drcode}[Docs]
doxygen Doxyfile
\end{drcode}
\begin{drcaution}[Keep it green]
The Doxygen build and the manual-synchronisation check are part of the
definition of done. A change that leaves either failing is not complete.
\end{drcaution}
+35
View File
@@ -0,0 +1,35 @@
\chapter{Class Reference}
\label{ch:classes}
This chapter documents the firmware's public classes at the design level:
what each class is responsible for, which collaborators it depends on, its
key invariants, and how it fits the system. It complements --- and does
not duplicate --- the generated API documentation, which carries the exact
method signatures.
\begin{drnote}[How this chapter grows]
Per the development rules, every public, architecturally-significant class
(the drivers, the application services, and the public domain-core types)
gets a section here, tagged \texttt{\textbackslash label\{cls:ClassName\}},
added in the same change that introduces the class. A tooling check keeps
this chapter in step with the code, so it is always current.
\end{drnote}
The firmware is under active development. As each public class lands, its
section appears below, grouped by layer: domain core, application
services, and hardware drivers.
% ------------------------------------------------------------------
% Per-class sections are added here as classes are implemented, e.g.:
%
% \section{Si4684Driver}\label{cls:Si4684Driver}
% Responsibility, collaborators, invariants, usage.
%
% \section{Adau1701Driver}\label{cls:Adau1701Driver}
% \section{Bt1035Driver}\label{cls:Bt1035Driver}
% \section{TunerService}\label{cls:TunerService}
% ...
% ------------------------------------------------------------------
\section*{(No public classes documented yet)}
Sections will appear here as the firmware is implemented.
+202
View File
@@ -0,0 +1,202 @@
% ============================================================
% DigiRadio — Manual section: Firmware Architecture
% Drop this into the manual as a \section (or \chapter).
%
% Required packages (add to the preamble if not present):
% \usepackage{tikz}
% \usetikzlibrary{positioning, arrows.meta, fit, backgrounds}
% \usepackage{booktabs} % for the interface table
%
% The environments used here are standard. Replace itemize / framed
% blocks with your own box taxonomy where you prefer.
% ============================================================
\chapter{Firmware Architecture}
\label{sec:firmware-architecture}
The DigiRadio firmware runs on the ESP32-S3 and orchestrates three
companion chips into a single high-fidelity audio path: the Si4684
DAB+/FM tuner, the ADAU1701 SigmaDSP, and the FSC-BT1035 Bluetooth
transmitter. This section describes how the firmware is organised, how
audio flows through the system, and how each chip is controlled. The
day-to-day coding conventions (style, documentation, review rules) are
intentionally kept out of this manual and live in the repository's
\texttt{CONTRIBUTING.md} and \texttt{AGENTS.md}.
\section{Design principles}
\label{sec:fw-principles}
Two ideas shape the whole codebase:
\begin{itemize}
\item \textbf{Strong typing.} Domain quantities (frequency, gain,
station identifier, band) are dedicated types rather than raw
integers or floats. Invalid states are made unrepresentable: input
from the network, UART, or flash is validated once at the boundary
into a domain type, and trusted thereafter.
\item \textbf{Functional core, imperative shell.} All hardware-free
logic --- coefficient math, boot-image framing, configuration
parsing, station-list operations --- lives in a pure \emph{core}
that compiles and is unit-tested on the host, with no dependency on
ESP-IDF. Every access to the hardware (I\textsuperscript{2}C, SPI,
UART, flash) lives in a thin \emph{shell} around that core. This
keeps the logic testable without a board attached.
\end{itemize}
\section{Layered structure}
\label{sec:fw-layers}
The firmware is organised in three layers, with dependencies pointing
inward only (Figure~\ref{fig:fw-layers}). The outer shell may depend on
the services and the core; the pure core depends on nothing outside
itself.
\begin{figure}[htbp]
\centering
\begin{tikzpicture}[
layer/.style={draw, rounded corners, minimum width=10cm,
minimum height=1.5cm, align=center, font=\small},
node distance=4mm]
\node[layer, fill=black!5] (shell) {%
\textbf{Imperative shell}\\[2pt]
chip drivers \textbullet\ web server \textbullet\ NVS \textbullet\
FreeRTOS tasks \textbullet\ ISRs};
\node[layer, fill=black!8, below=of shell] (services) {%
\textbf{Application services}\\[2pt]
TunerService \textbullet\ AudioService \textbullet\
ConfigService \textbullet\ NetworkService};
\node[layer, fill=black!12, below=of services] (core) {%
\textbf{Domain core (pure, host-tested)}\\[2pt]
Station \textbullet\ Frequency \textbullet\ EqProfile \textbullet\
MixerState \textbullet\ Credential \textbullet\ validation};
\draw[-{Latex}] (shell) -- (services);
\draw[-{Latex}] (services) -- (core);
\end{tikzpicture}
\caption{Firmware layering. Dependencies point inward; the pure core
has no hardware dependencies.}
\label{fig:fw-layers}
\end{figure}
Application services take driver \emph{interfaces}
(\texttt{ITuner}, \texttt{IDsp}, \texttt{IBtModule},
\texttt{ISecureStore}) injected at construction. Because the services
depend on abstractions rather than concrete hardware, the same logic can
be exercised against fakes in host tests and against real silicon on the
device.
\section{Audio signal path}
\label{sec:fw-audio}
The audio chain is built around sound quality. The Si4684 produces the
DAB+/FM audio stream; the ADAU1701 applies equalisation and mixes it
with the ESP32 audio source; the FSC-BT1035 streams the result over
Bluetooth with aptX Adaptive (Figure~\ref{fig:fw-audio}).
\begin{figure}[htbp]
\centering
\begin{tikzpicture}[
block/.style={draw, rounded corners, minimum height=1cm,
minimum width=2.4cm, align=center, font=\small},
>={Latex}, node distance=8mm]
\node[block] (si) {Si4684\\\scriptsize DAB+/FM};
\node[block, right=16mm of si] (dsp) {ADAU1701\\\scriptsize EQ + mixer};
\node[block, right=16mm of dsp] (bt) {FSC-BT1035\\\scriptsize aptX Adaptive};
\node[block, below=12mm of dsp] (esp) {ESP32-S3\\\scriptsize audio src};
\draw[->] (si) -- (dsp) node[midway, above, font=\scriptsize]{audio};
\draw[->] (dsp) -- (bt) node[midway, above, font=\scriptsize]{out};
\draw[->] (esp) -- (dsp) node[midway, right, font=\scriptsize]{mix in};
\end{tikzpicture}
\caption{Audio signal path. The ADAU1701 input mixer selects/blends
the Si4684 and ESP32 sources; equalisation is applied before the
Bluetooth output stage.}
\label{fig:fw-audio}
\end{figure}
Equalisation and input mixing are exposed to the rest of the firmware as
typed operations --- setting an EQ band from a gain, centre frequency,
and Q, or setting the mix level of a given source --- never as raw DSP
cell addresses. The biquad coefficient math runs in the pure core and is
verified against reference values in host tests.
\section{Chip control and boot}
\label{sec:fw-chips}
\paragraph{Si4684 (tuner).}
At power-up the driver follows the documented boot sequence: power on,
load the patch/bootloader, load the firmware image (FM or DAB), then
boot. Firmware images are large and are streamed to the chip in bounded
chunks from flash rather than buffered whole in RAM.
\begin{drref}[Datasheet]
The exact power-up, patch, image-load, and boot command sequence follows
the Si468x programming guide (AN649). Each step in the driver cites the
relevant section; no command byte is issued without a documented source.
\end{drref} The public
interface is intent-level (\texttt{powerUp}, \texttt{loadImage},
\texttt{tuneTo}, \texttt{readRsq}); register-level detail is private to
the driver.
\paragraph{ADAU1701 (DSP).}
The board carries no self-boot EEPROM. Instead, the ESP32 writes the
SigmaStudio-exported program into the DSP's RAM at \emph{every} boot.
Runtime changes to equalisation and mixing use the ADAU1701 safeload
mechanism, so parameter updates are click-free while audio is playing.
\begin{drcaution}[Safeload]
Writing DSP parameter cells directly while audio is running produces
audible clicks. All runtime EQ and mixer changes must go through the
safeload registers.
\end{drcaution}
\paragraph{FSC-BT1035 (Bluetooth).}
The module is controlled by AT commands over UART. The initialisation
sequence includes enabling Line-In mode (\texttt{AT+AUXCFG=1}), which is
required for the wired audio path from the DSP; command responses are
parsed explicitly, with timeouts treated as errors.
\section{Configuration, storage, and user interface}
\label{sec:fw-config}
Network provisioning uses a SoftAP/captive-portal for first-time setup,
then joins the configured network as a station. Configuration is exposed
through an elegant, minimal web interface served from flash, backed by a
typed JSON API; the interface holds no business logic.
Sensitive data --- Wi-Fi credentials, user credentials, and the
station/frequency list --- is held in encrypted storage at rest. Secrets
are wrapped in a dedicated type that cannot be logged or implicitly
converted to a string, and buffers are cleared after use.
\section{Error-handling model}
\label{sec:fw-errors}
Every operation that can fail returns a typed result
(\texttt{std::expected<T, Error>}); nothing fails silently and every
timeout is an explicit error value. Errors propagate to the layer that
can act on them --- a driver reports, a service decides (retry, degrade,
or surface to the UI), and the top level logs. C++ exceptions are
disabled.
\section{Toolchain}
\label{sec:fw-toolchain}
\begin{table}[htbp]
\centering
\begin{tabular}{@{}ll@{}}
\toprule
\textbf{Item} & \textbf{Choice} \\
\midrule
Framework & ESP-IDF v5.5.x (native) \\
Language & C++23 (\texttt{-std=gnu++23}) \\
Error model & \texttt{std::expected}; exceptions off \\
Hardware licence & CERN-OHL-S v2 \\
Firmware licence & Apache-2.0 \\
\bottomrule
\end{tabular}
\caption{Firmware toolchain and licensing summary.}
\label{tab:fw-toolchain}
\end{table}
The firmware source, build instructions, and development conventions are
maintained in the \texttt{Software/} directory of the project
repository.
+53
View File
@@ -0,0 +1,53 @@
\chapter{Hardware Overview}
\label{ch:hardware}
This chapter describes the board at a block level. The complete hardware
design --- schematics, PCB, Gerbers, and bill of materials --- is
published in the repository under the CERN-OHL-S v2 licence.
\section{System block diagram}
DigiRadio is built around four devices coordinated by the ESP32-S3. The
tuner produces the audio stream, the DSP conditions and mixes it, and the
Bluetooth module transmits it; the host manages all three and provides
the network interface.
\begin{table}[htbp]
\centering
\begin{tabular}{@{}lll@{}}
\drhead Device & Role & Interface \\
\midrule
Si4684 & DAB+/FM tuner & SPI / I\textsuperscript{2}C \\
ADAU1701 & Audio DSP (EQ + mixer) & I\textsuperscript{2}C \\
ESP32-S3-WROOM-1 & Host controller, Wi-Fi/BLE & --- \\
FSC-BT1035 & Bluetooth 5.2 (aptX Adaptive) & UART (AT) \\
\bottomrule
\end{tabular}
\caption{Principal integrated circuits and their roles.}
\label{tab:hw-ics}
\end{table}
\section{Power}
The board is powered from USB-C. An AP63203 buck converter generates the
main rails from the USB input, feeding the digital and analogue sections.
The tuner's sensitive supplies are derived and filtered separately to
keep RF performance clean.
\section{RF and antennas}
Two 2.4\,GHz radios are present --- the ESP32-S3 and the Bluetooth module.
Their antennas are placed on opposite diagonal corners of the board to
maximise separation and minimise mutual desense. Each module keeps its
antenna keep-out clear of copper on all layers.
\begin{drnote}[Board]
The PCB is a six-layer stack-up with controlled impedance on the USB and
RF sections. The full fabrication data is in the repository.
\end{drnote}
\section{User interface and connectors}
Configuration is done over Wi-Fi through a built-in web interface (see
Chapter~\ref{ch:firmware}); no display is required on the board itself.
Audio reaches the listener wirelessly over Bluetooth.
+41
View File
@@ -0,0 +1,41 @@
\chapter{Introduction}
\label{ch:introduction}
DigiRadio is an open-source, high-fidelity digital radio receiver. It
receives DAB+ and FM broadcasts, processes the audio through a dedicated
signal processor, and streams the result over Bluetooth using a
high-resolution codec. The whole project --- hardware and firmware --- is
released as open source for the maker and audio community to study,
build, and improve.
\section{What DigiRadio is}
Most DAB+/FM hobby projects stop at basic reception. DigiRadio adds two
things that lift it into hi-fi territory: a real audio DSP stage for
equalisation and mixing, and a premium-codec Bluetooth output. The result
is a genuine wireless audio source rather than a bench demo, and, being
fully documented, a practical reference for anyone learning multi-chip
audio design or RF-aware PCB layout.
\begin{drkey}[At a glance]
A four-chip design --- Si4684 tuner, ADAU1701 DSP, ESP32-S3 host, and an
FSC-BT1035 Bluetooth transmitter --- on a six-layer board, powered from
USB-C, configured through an elegant web interface.
\end{drkey}
\section{Who this manual is for}
This manual documents the system for someone building, flashing, or
extending DigiRadio: the hardware at a block level
(Chapter~\ref{ch:hardware}), the firmware architecture
(Chapter~\ref{ch:firmware}), the per-class reference that grows with the
code (Chapter~\ref{ch:classes}), and how to build and flash
(Chapter~\ref{ch:build}). The exact register-level programming of each
chip lives in the source code and its generated API documentation; this
manual explains the design and the reasoning behind it.
\section{Licensing summary}
The hardware is licensed under CERN-OHL-S v2 and the firmware under
Apache-2.0; full details are in Chapter~\ref{ch:licensing}. The project
repository is at \url{https://github.com/manvalan/DigiRadio}.
+40
View File
@@ -0,0 +1,40 @@
\chapter{Licensing}
\label{ch:licensing}
DigiRadio is dual-licensed, with hardware and firmware under separate but
compatible open-source licences.
\section{Hardware}
The hardware --- schematics, PCB layout, Gerbers, and bill of materials
--- is licensed under the \textbf{CERN Open Hardware Licence Version~2,
Strongly Reciprocal (CERN-OHL-S v2)}. Anyone who modifies and distributes
the hardware must release their changes under the same terms.
\section{Firmware}
The firmware is licensed under the \textbf{Apache License 2.0}. It grants
patent rights explicitly and pairs naturally with the ESP-IDF ecosystem,
which is Apache-licensed as well. Every source file carries the Apache
header with an SPDX identifier.
\section{Documentation and project pages}
Where a hosting platform does not offer the CERN-OHL-S licence in its
selector, the closest share-alike option is used for that page, with the
authoritative licence stated as CERN-OHL-S v2. The definitive licensing
is always the one declared in the project repository.
\section{Third-party components}
The design integrates third-party silicon (Silicon Labs, Analog Devices,
Espressif, Feasycom/Qualcomm) whose datasheets, firmware images, and DSP
tools remain subject to their respective vendors' terms. Those terms are
unaffected by the DigiRadio licences and must be observed when
redistributing vendor-supplied binaries.
\begin{drnote}[Manufacturing partner]
Prototype fabrication and assembly are supported by PCBWay. Their
contribution is acknowledged in the project documentation and on the
board.
\end{drnote}
+169
View File
@@ -0,0 +1,169 @@
% ============================================================
% digiradio-manual.sty — visual style for the DigiRadio manual
% Optima-like humanist sans, 12pt, 1.3 leading, restrained colour,
% and clear at-a-glance tcolorbox callouts.
%
% Use with a 12pt KOMA class, e.g.:
% \documentclass[12pt,a4paper]{scrartcl}
% \usepackage{digiradio-manual}
%
% FONT: compile with LuaLaTeX or XeLaTeX to get the real Optima
% (a system font on macOS). With pdfLaTeX it falls back to Linux
% Biolinum, a humanist sans in the same spirit. The package detects
% the engine automatically.
% ============================================================
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{digiradio-manual}[2026/07/06 DigiRadio manual style]
\RequirePackage[table,dvipsnames]{xcolor}
\RequirePackage{iftex}
\RequirePackage{geometry}
\RequirePackage{microtype}
\RequirePackage{setspace}
\RequirePackage{titlesec}
\RequirePackage{booktabs}
\RequirePackage{array}
\RequirePackage{enumitem}
\RequirePackage[most]{tcolorbox}
\RequirePackage{graphicx}
% ---------- Fonts (engine-aware) ---------------------------------------
\ifPDFTeX
\RequirePackage[T1]{fontenc}
\RequirePackage[utf8]{inputenc}
\RequirePackage{libertine} % Linux Libertine + Biolinum
\renewcommand{\familydefault}{\sfdefault} % Biolinum as body (Optima-like)
\else
\RequirePackage{fontspec}
% Real Optima on macOS; change the name if yours differs.
\IfFontExistsTF{Optima}{%
\setmainfont{Optima}%
}{%
\setmainfont{Linux Biolinum O}% fallback if Optima not installed
}
\fi
% ---------- Colour palette (elegant, restrained) -----------------------
% Two signature colours (petrol + amber), a muted red reserved for
% cautions, plus ink and neutral. Backgrounds are light tints of these.
\definecolor{drPetrol}{HTML}{0F4C5C} % primary — headings, notes
\definecolor{drAmber} {HTML}{C46210} % accent — key ideas, emphasis
\definecolor{drTeal} {HTML}{1A7F72} % secondary — references
\definecolor{drRed} {HTML}{A4303F} % caution only
\definecolor{drInk} {HTML}{1B1B1D} % body text
\definecolor{drGray} {HTML}{5A5F63} % captions, rules
\color{drInk}
% ---------- Page geometry & spacing ------------------------------------
\geometry{a4paper, top=2.6cm, bottom=2.6cm, inner=2.6cm, outer=3.2cm,
marginparwidth=2.4cm}
\setstretch{1.3}
\setlength{\parskip}{0.5\baselineskip}
\setlength{\parindent}{0pt}
% ---------- Links -------------------------------------------------------
\RequirePackage{hyperref}
\hypersetup{colorlinks=true, linkcolor=drPetrol, urlcolor=drAmber,
citecolor=drTeal}
% ---------- Chapter heading (for report-class manuals) -----------------
\titleformat{\chapter}[display]
{\color{drPetrol}\bfseries}
{\filright\color{drAmber}\large\MakeUppercase{\chaptertitlename}\ \thechapter}
{6pt}{\Huge}
[\vspace{3pt}{\color{drAmber}\titlerule[1.5pt]}]
\titlespacing*{\chapter}{0pt}{0pt}{22pt}
% ---------- Section headings (coloured, sans, distinctive) -------------
\titleformat{\section}
{\color{drPetrol}\Large\bfseries}
{\color{drAmber}\thesection\quad}{0pt}{}
[{\color{drAmber}\titlerule[1.2pt]}]
\titleformat{\subsection}
{\color{drPetrol}\large\bfseries}
{\color{drAmber}\thesubsection\quad}{0pt}{}
\titleformat{\subsubsection}
{\color{drPetrol}\normalsize\bfseries}
{\thesubsubsection\quad}{0pt}{}
\titleformat{\paragraph}[runin]
{\bfseries\color{drPetrol}}{}{0pt}{}[\;]
\titlespacing*{\paragraph}{0pt}{0.6\baselineskip}{0.8em}
\titlespacing*{\section}{0pt}{1.4\baselineskip}{0.5\baselineskip}
% ---------- List markers (coloured) ------------------------------------
\setlist[itemize,1]{label=\textcolor{drAmber}{\textbullet}, leftmargin=*}
\setlist[itemize,2]{label=\textcolor{drTeal}{\textendash}}
\setlist[enumerate,1]{label=\textcolor{drAmber}{\bfseries\arabic*.},
leftmargin=*}
% ---------- Callout boxes (clear at a glance) --------------------------
\tcbuselibrary{skins,breakable}
% Shared base: rounded, light tinted body, bold coloured title bar
% clipped to the top-left, and an accent rule down the left edge.
\tcbset{dr@base/.style={
enhanced, breakable, boxrule=0pt, frame hidden, sharp corners=downhill,
arc=1.4mm, left=3.5mm, right=3.5mm, top=3mm, bottom=3mm,
fonttitle=\bfseries\small, coltitle=white,
attach boxed title to top left={xshift=3mm, yshift=-2.4mm},
boxed title style={boxrule=0pt, arc=0.8mm,
left=2.2mm, right=2.2mm, top=0.6mm, bottom=0.6mm},
before skip=8pt, after skip=8pt,
}}
\newtcolorbox{drnote}[1][Note]{dr@base,
colback=drPetrol!6, colbacktitle=drPetrol,
borderline west={2.6pt}{0pt}{drPetrol}, title={#1}}
\newtcolorbox{drkey}[1][Key idea]{dr@base,
colback=drAmber!9, colbacktitle=drAmber,
borderline west={2.6pt}{0pt}{drAmber}, title={#1}}
\newtcolorbox{drcaution}[1][Caution]{dr@base,
colback=drRed!7, colbacktitle=drRed,
borderline west={2.6pt}{0pt}{drRed}, title={#1}}
\newtcolorbox{drref}[1][Datasheet]{dr@base,
colback=drTeal!7, colbacktitle=drTeal,
borderline west={2.6pt}{0pt}{drTeal}, title={#1}}
\newtcolorbox{drdecision}[1][Design decision]{dr@base,
colback=drPetrol!5, colbacktitle=drGray,
borderline west={2.6pt}{0pt}{drGray}, title={#1}}
% ---------- Table helpers ----------------------------------------------
% A coloured header row for booktabs tables. Usage:
% \begin{tabular}{...}
% \drhead Col A & Col B \\ % coloured header row
% \midrule ...
\newcommand{\drhead}{\rowcolor{drPetrol}\color{white}\bfseries}
% Subtle zebra striping if wanted: \rowcolors{2}{drPetrol!5}{white}
% ---------- Code listings (styled to match the callouts) ---------------
\tcbuselibrary{listings}
\lstdefinestyle{drcpp}{%
language=C++,
basicstyle=\ttfamily\small,
keywordstyle=\color{drPetrol}\bfseries,
commentstyle=\color{drGray}\itshape,
stringstyle=\color{drTeal},
numberstyle=\color{drGray}\scriptsize,
numbers=left, numbersep=9pt,
showstringspaces=false,
tabsize=2, breaklines=true, keepspaces=true,
morekeywords={constexpr,noexcept,nullptr,override,final,explicit,
enum,class,namespace,using,co_await,std,expected,span,
uint32_t,uint8_t},
}
% drcode: a code block with the same title bar + left accent as the boxes
\newtcblisting{drcode}[1][Listing]{%
dr@base,
colback=drPetrol!4, colbacktitle=drGray,
borderline west={2.6pt}{0pt}{drGray},
title={#1},
listing only, listing style=drcpp,
}
% inline code: \drcode|std::expected<T,E>|
\newcommand{\code}[1]{{\ttfamily\color{drPetrol}#1}}
\endinput
Binary file not shown.
+52
View File
@@ -0,0 +1,52 @@
% ============================================================
% DigiRadio — Technical Manual (main file)
%
% Compile with LuaLaTeX or XeLaTeX for the real Optima font
% (a system font on macOS):
% latexmk -lualatex manual.tex
% pdfLaTeX also works and falls back to Linux Biolinum.
% ============================================================
\documentclass[12pt,a4paper,oneside]{scrreprt}
\usepackage{digiradio-manual}
\usepackage{tikz}
\usetikzlibrary{positioning, arrows.meta, fit, backgrounds}
\begin{document}
% ---------------- Cover ----------------
\begin{titlepage}
\thispagestyle{empty}
\vspace*{3.5cm}
{\color{drPetrol}\fontsize{48}{52}\selectfont\bfseries DigiRadio\par}
\vspace{3mm}
{\color{drAmber}\rule{\linewidth}{2.5pt}}
\vspace{6mm}
{\color{drInk}\LARGE Open-Source Hi-Fi DAB+/FM Receiver\par}
\vspace{3mm}
{\color{drGray}\Large Technical Manual\par}
\vfill
{\color{drInk}\large Michele Bigi\par}
\vspace{1mm}
{\color{drGray} 2026 \quad\textbullet\quad Hardware: CERN-OHL-S v2
\quad\textbullet\quad Firmware: Apache-2.0\par}
\vspace{2mm}
{\color{drGray}\small \url{https://github.com/manvalan/DigiRadio}\par}
\end{titlepage}
% ---------------- Contents ----------------
\pagestyle{plain}
{\color{drPetrol}\tableofcontents}
\clearpage
% ---------------- Chapters ----------------
\include{ch-intro}
\include{ch-hardware}
\include{ch-firmware}
\include{ch-classes}
\include{ch-build}
\include{ch-licensing}
\end{document}