-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathMPXMixer.cpp
More file actions
80 lines (75 loc) · 3.1 KB
/
Copy pathMPXMixer.cpp
File metadata and controls
80 lines (75 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/*
* =====================================================================================
*
* PiratESP32 - FM RDS STEREO ENCODER
* (c) 2025 MFINI, Anthropic Claude Code, OpenAI Codex
* FM Stereo Multiplex Signal Mixer Implementation
*
* =====================================================================================
*
* File: MPXMixer.cpp
* Description: Real-time FM multiplex baseband signal construction
*
* Purpose:
* This implementation module provides the signal mixing kernel for constructing
* the complete FM stereo multiplex signal at DAC rate (Config::SAMPLE_RATE_DAC).
* It combines mono (L+R),
* pilot tone (19 kHz), and stereo subcarrier (L-R modulated on 38 kHz) with
* configurable scaling factors.
*
* Algorithm:
* Fused accumulation in single pass over memory minimizes cache misses:
* MPX[i] = mono[i]
* + PILOT_AMP × pilot_buffer[i]
* + DIFF_AMP × diff[i] × subcarrier_buffer[i]
*
* This approach ensures:
* • Deterministic latency (no intermediate buffers)
* • Minimal memory bandwidth (one write, multiple reads)
* • Compiler-friendly for vectorization
*
* Configuration:
* Pilot and subcarrier amplitudes are user-configurable to accommodate different
* stereo receiver standards and RF power constraints. Typical values:
* • pilot_amp = 0.1 (10% for ARI/RDS compatibility)
* • diff_amp = 0.5 (50% of mono reference for DSB-SC modulation)
*
* Thread Safety:
* Not thread-safe. Process state (amplitudes) is immutable once constructed.
* process() must be called exclusively from Core 0 audio task.
*
* Feature Gating:
* Audio output, pilot, and subcarrier can be individually enabled/disabled via
* Config namespace flags for diagnostic and testing purposes.
*
* =====================================================================================
*/
#include "MPXMixer.h"
MPXMixer::MPXMixer(float pilot_amp, float diff_amp)
: pilot_amp_(pilot_amp),
diff_amp_(diff_amp)
{
}
void MPXMixer::process(const float *mono,
const float *diff,
const float *pilot_buffer,
const float *subcarrier_buffer,
float *mpx,
std::size_t samples)
{
if (!mono || !diff || !pilot_buffer || !subcarrier_buffer || !mpx || samples == 0)
{
return;
}
// Pilot and subcarrier buffers are expected to be pre-filled coherently
// Fused accumulation: one pass over memory for best cache behavior
for (std::size_t i = 0; i < samples; ++i)
{
const float mono_term = Config::ENABLE_AUDIO ? mono[i] : 0.0f;
const float pilot_term = Config::ENABLE_STEREO_PILOT_19K ? (pilot_amp_ * pilot_buffer[i]) : 0.0f;
const float dsb_term = (Config::ENABLE_AUDIO && Config::ENABLE_STEREO_SUBCARRIER_38K)
? (diff_amp_ * diff[i] * subcarrier_buffer[i])
: 0.0f;
mpx[i] = mono_term + pilot_term + dsb_term;
}
}