GCC Code Coverage Report


Directory: ./
File: channeloutput/USBRenard.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 86 116 74.1%
Functions: 13 13 100.0%
Branches: 39 82 47.6%

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 <termios.h>
18 #include <unistd.h>
19
20 #include "../Warnings.h"
21 #include "../common_mini.h" // FPPstrerror() -- needed directly for NOPCH builds
22 #include "../log.h"
23
24 #include "SerialChannelOutput.h"
25 #include "serialutil.h"
26 #include "USBRenard.h"
27
28 /////////////////////////////////////////////////////////////////////////////
29
30 class USBRenardOutputData : public SerialChannelOutput {
31 public:
32 2 USBRenardOutputData() :
33 2 maxChannels(0),
34 2 speed(0),
35 2 outputData(nullptr) {
36 2 }
37 8 ~USBRenardOutputData() {
38
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
4 if (outputData) {
39
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 delete[] outputData;
40 }
41 8 }
42 char* outputData;
43 int maxChannels;
44 int speed;
45 char parm[4];
46 };
47
48 // Assume clocks are accurate to 1%, so insert a pad byte every 100 bytes.
49 #define PAD_DISTANCE 100
50
51 /////////////////////////////////////////////////////////////////////////////
52 #include "Plugin.h"
53 class USBRenardPlugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin {
54 public:
55 2 USBRenardPlugin() :
56
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
6 FPPPlugins::Plugin("USBRenard") {
57 2 }
58 2 virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override {
59
2/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
4 return new USBRenardOutput(startChannel, channelCount);
60 }
61 };
62
63 extern "C" {
64 2 FPPPlugins::Plugin* createPlugin() {
65
2/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
4 return new USBRenardPlugin();
66 }
67 }
68
69 /*
70 * Data for this was gathered from the DIYC wiki pages on Renard channels at
71 * 50ms refresh rate. For the newer (faster) speeds the PX1 page was
72 * referenced.
73 *
74 * http://www.doityourselfchristmas.com/wiki/index.php?title=Renard#Number_of_Circuits_.28Channels.29_per_Serial_Port
75 * http://www.doityourselfchristmas.com/wiki/index.php?title=Renard_PX1_Pixel_Controller#Maximum_Number_of_Pixels_per_Controller
76 *
77 */
78 2 static int USBRenard_MaxChannels(USBRenardOutputData* privData) {
79
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (privData->maxChannels != 0)
80 ✗ return privData->maxChannels;
81
82
2/8
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
2 switch (privData->speed) {
83 ✗ case 921600:
84 ✗ privData->maxChannels = 4584;
85 ✗ break;
86 ✗ case 460800:
87 ✗ privData->maxChannels = 2292;
88 ✗ break;
89 ✗ case 230400:
90 ✗ privData->maxChannels = 1146;
91 ✗ break;
92 ✗ case 115200:
93 ✗ privData->maxChannels = 574;
94 ✗ break;
95 1 case 57600:
96 1 privData->maxChannels = 286;
97 1 break;
98 ✗ case 38400:
99 ✗ privData->maxChannels = 190;
100 ✗ break;
101 ✗ case 19200:
102 ✗ privData->maxChannels = 94;
103 ✗ break;
104 }
105 2 return privData->maxChannels;
106 }
107
108 2 USBRenardOutput::USBRenardOutput(unsigned int startChannel, unsigned int channelCount) :
109 ChannelOutput(startChannel, channelCount),
110 2 data(nullptr) {
111 2 }
112 8 USBRenardOutput::~USBRenardOutput() {
113
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 if (data) {
114
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 delete data;
115 }
116 8 }
117 2 void USBRenardOutput::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) {
118 2 addRange(m_startChannel, m_startChannel + m_channelCount - 1);
119 2 }
120 2 int USBRenardOutput::Init(Json::Value config) {
121 2 LogDebug(VB_CHANNELOUT, "USBRenard_Open()\n");
122
123
2/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2 data = new USBRenardOutputData();
124 2 strcpy(data->parm, "8N1");
125
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (config.isMember("renardspeed")) {
126 2 data->speed = config["renardspeed"].asInt();
127 2 LogDebug(VB_CHANNELOUT, "Sending Renard at %d speed\n", data->speed);
128 }
129
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (config.isMember("renardparm")) {
130
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 std::string parm = config["renardparm"].asString();
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (parm.length() != 3) {
132 ✗ LogWarn(VB_CHANNELOUT, "Invalid length on serial parameters: %s\n", parm.c_str());
133 } else {
134 2 strcpy(data->parm, parm.c_str());
135 }
136 2 }
137
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!data->setupSerialPort(config, data->speed, data->parm)) {
138 ✗ return 0;
139 }
140
141 // SendData() refuses (and logs) every frame larger than the speed
142 // allows, and an unlisted speed allows none: say so once, here, and
143 // don't start the output
144 2 int maxChannels = USBRenard_MaxChannels(data);
145
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
2 if (maxChannels == 0 || (int)m_channelCount > maxChannels) {
146 1 LogErr(VB_CHANNELOUT, "Renard at %d baud carries at most %d channels, %u configured\n",
147 data->speed, maxChannels, m_channelCount);
148 1 return 0;
149 }
150 1 data->outputData = new char[maxChannels];
151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (data->outputData == NULL) {
152 ✗ LogErr(VB_CHANNELOUT, "Error %d allocating channel memory: %s\n",
153 errno, FPPstrerror(errno));
154
155 ✗ return 0;
156 }
157 1 bzero(data->outputData, data->maxChannels);
158
159
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 return ChannelOutput::Init(config);
160 }
161
162 1 int USBRenardOutput::Close(void) {
163 1 LogDebug(VB_CHANNELOUT, "USBRenard_Close()\n");
164
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (data) {
165 1 data->closeSerialPort();
166 }
167 1 return ChannelOutput::Close();
168 }
169
170 5 int USBRenardOutput::SendData(unsigned char* channelData) {
171 5 LogDebug(VB_CHANNELDATA, "USBRenard_SendData()\n");
172
173
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (m_channelCount <= data->maxChannels) {
174 5 bzero(data->outputData, data->maxChannels);
175 } else {
176 ✗ LogErr(VB_CHANNELOUT,
177 "USBRenard_SendData() tried to send %d bytes when max is %d\n",
178 m_channelCount, data->maxChannels);
179 ✗ return 0;
180 }
181
182 5 memcpy(data->outputData, channelData, m_channelCount);
183
184 // Act like "Renard (modified)" and don't output special codes. There are
185 // 3 we need to worry about.
186 // 0x7D - Pad Byte - map to 0x7C
187 // 0x7E - Sync Byte - map to 0x7C
188 // 0x7F - Escape Byte - map to 0x80
189 5 char* dptr = data->outputData;
190 5 int i = 0;
191
2/2
✓ Branch 0 taken 1430 times.
✓ Branch 1 taken 5 times.
1435 for (i = 0; i < data->maxChannels; i++) {
192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1430 times.
1430 if (*dptr == '\x7D')
193 ✗ *dptr = '\x7C';
194
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1430 times.
1430 else if (*dptr == '\x7E')
195 ✗ *dptr = '\x7C';
196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1430 times.
1430 else if (*dptr == '\x7F')
197 ✗ *dptr = '\x80';
198 1430 dptr++;
199 }
200
201 // Send start of packet byte
202 5 SerialWrite(data->getFD(), "\x7E\x80", 2);
203 5 dptr = data->outputData;
204
205 // Assume clocks are accurate to 1%, so insert a pad byte every 100 bytes.
206
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 for (i = 0; i * PAD_DISTANCE < m_channelCount; i++) {
207 // Send our pad byte
208 5 SerialWrite(data->getFD(), "\x7D", 1);
209
210 // Send Renard Data (Only send the channels we're given, not max)
211
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if ((i + 1) * PAD_DISTANCE > m_channelCount)
212 5 SerialWrite(data->getFD(), dptr, (m_channelCount - (i * PAD_DISTANCE)));
213 else
214 ✗ SerialWrite(data->getFD(), dptr, PAD_DISTANCE);
215
216 5 dptr += PAD_DISTANCE;
217 }
218 5 return m_channelCount;
219 }
220
221 1 void USBRenardOutput::DumpConfig(void) {
222 1 ChannelOutput::DumpConfig();
223
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!data)
224 ✗ return;
225 1 data->dumpSerialConfig();
226 1 LogDebug(VB_CHANNELOUT, " maxChannels: %i\n", data->maxChannels);
227 1 LogDebug(VB_CHANNELOUT, " speed : %d\n", data->speed);
228 1 LogDebug(VB_CHANNELOUT, " serial Parm: %s\n", data->parm);
229 }
230