GCC Code Coverage Report


Directory: ./
File: channeloutput/spixels.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 0 148 0.0%
Functions: 0 11 0.0%
Branches: 0 146 0.0%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the GPL v2 as described in the
10 * included LICENSE.GPL file.
11 */
12
13 #include "fpp-pch.h"
14
15 #include "fpp-json.h"
16
17 #include <fcntl.h>
18 #include <signal.h>
19 #include <stdint.h>
20
21 #include "../Sequence.h"
22 #include "../common.h"
23 #include "../log.h"
24
25 #include "../overlays/PixelOverlay.h"
26
27 #include "spixels.h"
28
29 #include "Plugin.h"
30 class SpixelsPlugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin {
31 public:
32 ✗ SpixelsPlugin() :
33 ✗ FPPPlugins::Plugin("Spixels") {
34 ✗ }
35 ✗ virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override {
36 ✗ return new SpixelsOutput(startChannel, channelCount);
37 }
38 };
39
40 extern "C" {
41 ✗ FPPPlugins::Plugin* createPlugin() {
42 ✗ return new SpixelsPlugin();
43 }
44 }
45
46 /////////////////////////////////////////////////////////////////////////////
47
48 /*
49 *
50 */
51 ✗ SpixelsOutput::SpixelsOutput(unsigned int startChannel, unsigned int channelCount) :
52 ThreadedChannelOutput(startChannel, channelCount),
53 ✗ m_spi(NULL) {
54 ✗ LogDebug(VB_CHANNELOUT, "SpixelsOutput::SpixelsOutput(%u, %u)\n",
55 startChannel, channelCount);
56 ✗ }
57
58 /*
59 *
60 */
61 ✗ SpixelsOutput::~SpixelsOutput() {
62 ✗ LogDebug(VB_CHANNELOUT, "SpixelsOutput::~SpixelsOutput()\n");
63
64 ✗ for (int s = 0; s < m_strips.size(); s++)
65 ✗ delete m_strips[s];
66
67 ✗ delete m_spi;
68
69 ✗ for (int s = 0; s < m_strings.size(); s++)
70 ✗ delete m_strings[s];
71 ✗ }
72
73 /*
74 *
75 */
76 ✗ int SpixelsOutput::Init(Json::Value config) {
77 ✗ LogDebug(VB_CHANNELOUT, "SpixelsOutput::Init(JSON)\n");
78
79 // WS2801 wants CreateDMAMultiSPI() for accurate timing, but that path is disabled.
80 ✗ m_spi = CreateDirectMultiSPI();
81
82 ✗ for (int i = 0; i < config["outputs"].size(); i++) {
83 ✗ Json::Value s = config["outputs"][i];
84 ✗ PixelString* newString = new PixelString();
85 ✗ LEDStrip* strip = NULL;
86
87 ✗ if (!newString->Init(s))
88 ✗ return 0;
89
90 ✗ int connector = 0;
91 ✗ int pixels = newString->m_outputBytes / 3; // FIXME, need to confirm this
92
93 ✗ if (pixels == 0) {
94 ✗ delete newString;
95 ✗ continue;
96 }
97
98 ✗ switch (s["portNumber"].asInt()) {
99 ✗ case 0:
100 ✗ connector = MultiSPI::SPI_P1;
101 ✗ break;
102 ✗ case 1:
103 ✗ connector = MultiSPI::SPI_P2;
104 ✗ break;
105 ✗ case 2:
106 ✗ connector = MultiSPI::SPI_P3;
107 ✗ break;
108 ✗ case 3:
109 ✗ connector = MultiSPI::SPI_P4;
110 ✗ break;
111 ✗ case 4:
112 ✗ connector = MultiSPI::SPI_P5;
113 ✗ break;
114 ✗ case 5:
115 ✗ connector = MultiSPI::SPI_P6;
116 ✗ break;
117 ✗ case 6:
118 ✗ connector = MultiSPI::SPI_P7;
119 ✗ break;
120 ✗ case 7:
121 ✗ connector = MultiSPI::SPI_P8;
122 ✗ break;
123 ✗ case 8:
124 ✗ connector = MultiSPI::SPI_P9;
125 ✗ break;
126 ✗ case 9:
127 ✗ connector = MultiSPI::SPI_P10;
128 ✗ break;
129 ✗ case 10:
130 ✗ connector = MultiSPI::SPI_P11;
131 ✗ break;
132 ✗ case 11:
133 ✗ connector = MultiSPI::SPI_P12;
134 ✗ break;
135 ✗ case 12:
136 ✗ connector = MultiSPI::SPI_P13;
137 ✗ break;
138 ✗ case 13:
139 ✗ connector = MultiSPI::SPI_P14;
140 ✗ break;
141 ✗ case 14:
142 ✗ connector = MultiSPI::SPI_P15;
143 ✗ break;
144 ✗ case 15:
145 ✗ connector = MultiSPI::SPI_P16;
146 ✗ break;
147 }
148
149 ✗ std::string protocol = s["protocol"].asString();
150 ✗ toLower(protocol);
151 ✗ if (protocol == "ws2801") {
152 ✗ strip = CreateWS2801Strip(m_spi, connector, pixels);
153 ✗ } else if (protocol == "apa102") {
154 ✗ strip = CreateAPA102Strip(m_spi, connector, pixels);
155 ✗ } else if (protocol == "lpd6803") {
156 ✗ strip = CreateLPD6803Strip(m_spi, connector, pixels);
157 ✗ } else if (protocol == "lpd8806") {
158 ✗ strip = CreateLPD8806Strip(m_spi, connector, pixels);
159 } else {
160 ✗ LogErr(VB_CHANNELOUT, "Unknown Pixel Protocol: %s\n", s["protocol"].asString().c_str());
161 ✗ return 0;
162 }
163
164 ✗ m_strings.push_back(newString);
165 ✗ m_strips.push_back(strip);
166 ✗ }
167
168 ✗ LogDebug(VB_CHANNELOUT, " Found %d strings of pixels\n", m_strings.size());
169 ✗ PixelString::AutoCreateOverlayModels(m_strings, m_autoCreatedModelNames);
170 ✗ return ThreadedChannelOutput::Init(config);
171 }
172
173 /*
174 *
175 */
176 ✗ int SpixelsOutput::Close(void) {
177 ✗ LogDebug(VB_CHANNELOUT, "SpixelsOutput::Close()\n");
178 ✗ for (auto& n : m_autoCreatedModelNames) {
179 ✗ PixelOverlayManager::INSTANCE.removeAutoOverlayModel(n);
180 }
181 ✗ return ThreadedChannelOutput::Close();
182 }
183
184 ✗ void SpixelsOutput::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) {
185 ✗ PixelString* ps = NULL;
186 ✗ for (int s = 0; s < m_strings.size(); s++) {
187 ✗ ps = m_strings[s];
188 ✗ int min = FPPD_MAX_CHANNELS;
189 ✗ int max = 0;
190 ✗ int inCh = 0;
191 ✗ for (int p = 0; p < ps->m_outputBytes; p++) {
192 ✗ int ch = ps->m_outputMap[inCh++];
193 ✗ if (ch < FPPD_MAX_CHANNELS) {
194 ✗ min = std::min(min, ch);
195 ✗ max = std::max(max, ch);
196 }
197 }
198 ✗ if (min < max) {
199 ✗ addRange(min, max);
200 }
201 }
202 ✗ }
203 ✗ void SpixelsOutput::PrepData(unsigned char* channelData) {
204 ✗ unsigned char* c = channelData;
205 ✗ unsigned int r = 0;
206 ✗ unsigned int g = 0;
207 ✗ unsigned int b = 0;
208
209 ✗ PixelString* ps = NULL;
210 ✗ int inCh = 0;
211
212 ✗ for (int s = 0; s < m_strings.size(); s++) {
213 ✗ ps = m_strings[s];
214 ✗ inCh = 0;
215
216 ✗ for (int p = 0, pix = 0; p < ps->m_outputBytes; pix++) {
217 ✗ r = ps->m_brightnessMaps[p++][channelData[ps->m_outputMap[inCh++]]];
218 ✗ g = ps->m_brightnessMaps[p++][channelData[ps->m_outputMap[inCh++]]];
219 ✗ b = ps->m_brightnessMaps[p++][channelData[ps->m_outputMap[inCh++]]];
220
221 ✗ m_strips[s]->SetPixel(pix, RGBc(r, g, b));
222 }
223 }
224 ✗ }
225
226 /*
227 *
228 */
229 ✗ int SpixelsOutput::RawSendData(unsigned char* channelData) {
230 ✗ LogExcess(VB_CHANNELOUT, "SpixelsOutput::RawSendData(%p)\n", channelData);
231
232 ✗ if (m_spi)
233 ✗ m_spi->SendBuffers();
234
235 ✗ return m_channelCount;
236 }
237
238 /*
239 *
240 */
241 ✗ void SpixelsOutput::DumpConfig(void) {
242 ✗ LogDebug(VB_CHANNELOUT, "SpixelsOutput::DumpConfig()\n");
243
244 ✗ for (int i = 0; i < m_strings.size(); i++) {
245 ✗ LogDebug(VB_CHANNELOUT, " String #%d\n", i);
246 ✗ m_strings[i]->DumpConfig();
247 }
248
249 ✗ ThreadedChannelOutput::DumpConfig();
250 ✗ }
251