Cosmetics
[opentx.git] / radio / src / strhelpers.cpp
blob8dc14d4ba7468cceac84f14e8016813228dde357
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "opentx.h"
23 #if !defined(BOOT)
24 const char s_charTab[] = "_-.,";
26 char hex2zchar(uint8_t hex)
28 return (hex >= 10 ? hex-9 : 27+hex);
31 char zchar2char(int8_t idx)
33 if (idx == 0) return ' ';
34 if (idx < 0) {
35 if (idx > -27) return 'a' - idx - 1;
36 idx = -idx;
38 if (idx < 27) return 'A' + idx - 1;
39 if (idx < 37) return '0' + idx - 27;
40 if (idx <= 40) return *(s_charTab+idx-37);
41 #if LEN_SPECIAL_CHARS > 0
42 if (idx <= (LEN_STD_CHARS + LEN_SPECIAL_CHARS)) return 'z' + 5 + idx - 40;
43 #endif
44 return ' ';
47 char char2lower(char c)
49 return (c >= 'A' && c <= 'Z') ? c + 32 : c;
52 int8_t char2zchar(char c)
54 if (c == '_') return 37;
55 #if LEN_SPECIAL_CHARS > 0
56 if ((int8_t)c < 0 && c+128 <= LEN_SPECIAL_CHARS) return 41 + (c+128);
57 #endif
58 if (c >= 'a') return 'a' - c - 1;
59 if (c >= 'A') return c - 'A' + 1;
60 if (c >= '0') return c - '0' + 27;
61 if (c == '-') return 38;
62 if (c == '.') return 39;
63 if (c == ',') return 40;
64 return 0;
67 void str2zchar(char * dest, const char * src, int size)
69 memset(dest, 0, size);
70 for (int c=0; c<size && src[c]; c++) {
71 dest[c] = char2zchar(src[c]);
75 int zchar2str(char * dest, const char * src, int size)
77 for (int c=0; c<size; c++) {
78 dest[c] = zchar2char(src[c]);
80 do {
81 dest[size--] = '\0';
82 } while (size >= 0 && dest[size] == ' ');
83 return size+1;
86 bool cmpStrWithZchar(const char * charString, const char * zcharString, int size)
88 for (int i=0; i<size; i++) {
89 if (charString[i] != zchar2char(zcharString[i])) {
90 return false;
93 return true;
96 unsigned int effectiveLen(const char * str, unsigned int size)
98 while (size > 0) {
99 if (str[size-1] != ' ')
100 return size;
101 size--;
103 return 0;
106 bool zexist(const char * str, uint8_t size)
108 for (int i=0; i<size; i++) {
109 if (str[i] != 0)
110 return true;
112 return false;
115 uint8_t zlen(const char * str, uint8_t size)
117 while (size > 0) {
118 if (str[size-1] != 0)
119 return size;
120 size--;
122 return 0;
125 char * strcat_zchar(char * dest, const char * name, uint8_t size, const char * defaultName, uint8_t defaultNameSize, uint8_t defaultIdx)
127 int8_t len = 0;
129 if (name) {
130 memcpy(dest, name, size);
131 dest[size] = '\0';
133 int8_t i = size-1;
135 while (i>=0) {
136 if (!len && dest[i])
137 len = i+1;
138 if (len) {
139 if (dest[i])
140 dest[i] = zchar2char(dest[i]);
141 else
142 dest[i] = '_';
144 i--;
148 if (len == 0 && defaultName) {
149 strcpy(dest, defaultName);
150 dest[defaultNameSize] = (char)((defaultIdx / 10) + '0');
151 dest[defaultNameSize + 1] = (char)((defaultIdx % 10) + '0');
152 len = defaultNameSize + 2;
155 return &dest[len];
157 #endif
159 #if !defined(BOOT)
160 char * getStringAtIndex(char * dest, const char * s, int idx)
162 uint8_t len = s[0];
163 strncpy(dest, s+1+len*idx, len);
164 dest[len] = '\0';
165 return dest;
168 char * strAppendStringWithIndex(char * dest, const char * s, int idx)
170 return strAppendUnsigned(strAppend(dest, s), abs(idx));
173 char * getTimerString(char * dest, int32_t tme, uint8_t hours)
175 char * s = dest;
176 div_t qr;
178 if (tme < 0) {
179 tme = -tme;
180 *s++ = '-';
183 qr = div((int)tme, 60);
185 if (hours) {
186 div_t qr2 = div(qr.quot, 60);
187 *s++ = '0' + (qr2.quot / 10);
188 *s++ = '0' + (qr2.quot % 10);
189 *s++ = ':';
190 qr.quot = qr2.rem;
193 if (!hours && qr.quot > 99) {
194 *s++ = '0' + (qr.quot / 100);
195 qr.quot = qr.quot % 100;
198 *s++ = '0' + (qr.quot / 10);
199 *s++ = '0' + (qr.quot % 10);
200 *s++ = ':';
201 *s++ = '0' + (qr.rem / 10);
202 *s++ = '0' + (qr.rem % 10);
203 *s = '\0';
205 return dest;
208 char * getCurveString(char * dest, int idx)
210 if (idx == 0) {
211 return getStringAtIndex(dest, STR_MMMINV, 0);
214 char * s = dest;
215 if (idx < 0) {
216 *s++ = '!';
217 idx = -idx;
220 if (ZEXIST(g_model.curves[idx - 1].name))
221 zchar2str(s, g_model.curves[idx - 1].name, LEN_CURVE_NAME);
222 else
223 strAppendStringWithIndex(s, STR_CV, idx);
225 return dest;
228 char * getGVarString(char * dest, int idx)
230 char * s = dest;
231 if (idx < 0) {
232 *s++ = '-';
233 idx = -idx-1;
236 if (ZEXIST(g_model.gvars[idx].name))
237 zchar2str(s, g_model.gvars[idx].name, LEN_GVAR_NAME);
238 else
239 strAppendStringWithIndex(s, STR_GV, idx+1);
241 return dest;
244 #if !defined(PCBSKY9X)
245 char * getSwitchName(char * dest, swsrc_t idx)
247 div_t swinfo = switchInfo(idx);
248 if (ZEXIST(g_eeGeneral.switchNames[swinfo.quot])) {
249 dest += zchar2str(dest, g_eeGeneral.switchNames[swinfo.quot], LEN_SWITCH_NAME);
250 // TODO tous zchar2str
252 else {
253 *dest++ = 'S';
254 #if defined(PCBX7)
255 if (swinfo.quot >= 5)
256 *dest++ = 'H' + swinfo.quot - 5;
257 else if (swinfo.quot == 4)
258 #if defined(RADIO_T12)
259 *dest++ = 'G';
260 #else
261 *dest++ = 'F';
262 #endif
263 else
264 *dest++ = 'A'+swinfo.quot;
265 #else
266 *dest++ = 'A' + swinfo.quot;
267 #endif
269 return dest;
271 #endif
273 char * getSwitchPositionName(char * dest, swsrc_t idx)
275 if (idx == SWSRC_NONE) {
276 return getStringAtIndex(dest, STR_VSWITCHES, 0);
278 else if (idx == SWSRC_OFF) {
279 return getStringAtIndex(dest, STR_OFFON, 0);
282 char * s = dest;
283 if (idx < 0) {
284 *s++ = '!';
285 idx = -idx;
288 #if defined(PCBSKY9X)
289 #define IDX_TRIMS_IN_STR_VSWITCHES (1+SWSRC_LAST_SWITCH)
290 #define IDX_ON_IN_STR_VSWITCHES (IDX_TRIMS_IN_STR_VSWITCHES+SWSRC_LAST_TRIM-SWSRC_FIRST_TRIM+2)
291 if (idx <= SWSRC_LAST_SWITCH) {
292 getStringAtIndex(s, STR_VSWITCHES, idx);
294 #else
295 #define IDX_TRIMS_IN_STR_VSWITCHES (1)
296 #define IDX_ON_IN_STR_VSWITCHES (IDX_TRIMS_IN_STR_VSWITCHES + SWSRC_LAST_TRIM - SWSRC_FIRST_TRIM + 1)
297 if (idx <= SWSRC_LAST_SWITCH) {
298 div_t swinfo = switchInfo(idx);
299 s = getSwitchName(s, idx);
300 *s++ = "\300-\301"[swinfo.rem];
301 *s = '\0';
303 #endif // PCBSKY9X
305 #if NUM_XPOTS > 0
306 else if (idx <= SWSRC_LAST_MULTIPOS_SWITCH) {
307 div_t swinfo = div(int(idx - SWSRC_FIRST_MULTIPOS_SWITCH), XPOTS_MULTIPOS_COUNT);
308 char temp[LEN_ANA_NAME+1];
309 getSourceString(temp, MIXSRC_FIRST_POT+swinfo.quot);
310 temp[LEN_ANA_NAME]= '\0';
311 strAppendStringWithIndex(s, temp, swinfo.rem+1);
313 #endif
315 #if defined(PCBSKY9X)
316 else if (idx <= SWSRC_REa) {
317 getStringAtIndex(s, STR_VSWITCHES, IDX_TRIMS_IN_STR_VSWITCHES+idx-SWSRC_FIRST_TRIM);
319 #else
320 else if (idx <= SWSRC_LAST_TRIM) {
321 getStringAtIndex(s, STR_VSWITCHES, IDX_TRIMS_IN_STR_VSWITCHES+idx-SWSRC_FIRST_TRIM);
323 #endif
325 else if (idx <= SWSRC_LAST_LOGICAL_SWITCH) {
326 *s++ = 'L';
327 strAppendUnsigned(s, idx-SWSRC_FIRST_LOGICAL_SWITCH+1, 2);
329 else if (idx <= SWSRC_ONE) {
330 getStringAtIndex(s, STR_VSWITCHES, IDX_ON_IN_STR_VSWITCHES + idx - SWSRC_ON);
332 else if (idx <= SWSRC_LAST_FLIGHT_MODE) {
333 strAppendStringWithIndex(s, STR_FM, idx-SWSRC_FIRST_FLIGHT_MODE);
335 else if (idx == SWSRC_TELEMETRY_STREAMING) {
336 strcpy(s, "Tele");
338 else if (idx == SWSRC_RADIO_ACTIVITY) {
339 strcpy(s, "Act");
341 #if defined(DEBUG_LATENCY)
342 else if (idx == SWSRC_LATENCY_TOGGLE) {
343 strcpy(s, "Ltc");
345 #endif
346 else {
347 zchar2str(s, g_model.telemetrySensors[idx-SWSRC_FIRST_SENSOR].label, TELEM_LABEL_LEN);
350 return dest;
353 char * getSourceString(char * dest, mixsrc_t idx)
355 if (idx == MIXSRC_NONE) {
356 return getStringAtIndex(dest, STR_VSRCRAW, 0);
358 else if (idx <= MIXSRC_LAST_INPUT) {
359 idx -= MIXSRC_FIRST_INPUT;
360 *dest++ = '\314';
361 if (ZEXIST(g_model.inputNames[idx])) {
362 zchar2str(dest, g_model.inputNames[idx], LEN_INPUT_NAME);
363 dest[LEN_INPUT_NAME] = '\0';
365 else {
366 strAppendUnsigned(dest, idx+1, 2);
369 #if defined(LUA_INPUTS)
370 else if (idx <= MIXSRC_LAST_LUA) {
371 #if defined(LUA_MODEL_SCRIPTS)
372 div_t qr = div(idx-MIXSRC_FIRST_LUA, MAX_SCRIPT_OUTPUTS);
373 if (qr.quot < MAX_SCRIPTS && qr.rem < scriptInputsOutputs[qr.quot].outputsCount) {
374 *dest++ = '\322';
375 // *dest++ = '1'+qr.quot;
376 strcpy(dest, scriptInputsOutputs[qr.quot].outputs[qr.rem].name);
378 #else
379 strcpy(dest, "N/A");
380 #endif
382 #endif
383 else if (idx <= MIXSRC_LAST_POT) {
384 idx -= MIXSRC_Rud;
385 if (ZEXIST(g_eeGeneral.anaNames[idx])) {
386 zchar2str(dest, g_eeGeneral.anaNames[idx], LEN_ANA_NAME);
387 dest[LEN_ANA_NAME] = '\0';
389 else {
390 getStringAtIndex(dest, STR_VSRCRAW, idx + 1);
393 else if (idx <= MIXSRC_LAST_TRIM) {
394 idx -= MIXSRC_Rud;
395 getStringAtIndex(dest, STR_VSRCRAW, idx + 1);
397 else if (idx <= MIXSRC_LAST_SWITCH) {
398 idx -= MIXSRC_FIRST_SWITCH;
399 if (ZEXIST(g_eeGeneral.switchNames[idx])) {
400 zchar2str(dest, g_eeGeneral.switchNames[idx], LEN_SWITCH_NAME);
401 dest[LEN_SWITCH_NAME] = '\0';
403 else {
404 getStringAtIndex(dest, STR_VSRCRAW, idx + MIXSRC_FIRST_SWITCH - MIXSRC_Rud + 1);
407 else if (idx <= MIXSRC_LAST_LOGICAL_SWITCH) {
408 getSwitchPositionName(dest, SWSRC_SW1 + idx - MIXSRC_SW1);
410 else if (idx <= MIXSRC_LAST_TRAINER) {
411 strAppendStringWithIndex(dest, STR_PPM_TRAINER, idx - MIXSRC_FIRST_TRAINER + 1);
413 else if (idx <= MIXSRC_LAST_CH) {
414 strAppendStringWithIndex(dest, STR_CH, idx - MIXSRC_CH1 + 1);
416 else if (idx <= MIXSRC_LAST_GVAR) {
417 strAppendStringWithIndex(dest, STR_GV, idx - MIXSRC_GVAR1 + 1);
419 else if (idx < MIXSRC_FIRST_TIMER) {
420 getStringAtIndex(dest, STR_VSRCRAW, idx-MIXSRC_Rud+1-MAX_LOGICAL_SWITCHES-MAX_TRAINER_CHANNELS-MAX_OUTPUT_CHANNELS-MAX_GVARS);
422 else if (idx <= MIXSRC_LAST_TIMER) {
423 if(ZEXIST(g_model.timers[idx-MIXSRC_FIRST_TIMER].name)) {
424 zchar2str(dest,g_model.timers[idx-MIXSRC_FIRST_TIMER].name, LEN_TIMER_NAME);
425 dest[LEN_TIMER_NAME] = '\0';
427 else {
428 getStringAtIndex(dest, STR_VSRCRAW, idx-MIXSRC_Rud+1-MAX_LOGICAL_SWITCHES-MAX_TRAINER_CHANNELS-MAX_OUTPUT_CHANNELS-MAX_GVARS);
431 else {
432 idx -= MIXSRC_FIRST_TELEM;
433 div_t qr = div(idx, 3);
434 dest[0] = '\321';
435 int pos = 1 + zchar2str(&dest[1], g_model.telemetrySensors[qr.quot].label, sizeof(g_model.telemetrySensors[qr.quot].label));
436 if (qr.rem) dest[pos++] = (qr.rem==2 ? '+' : '-');
437 dest[pos] = '\0';
440 return dest;
442 #endif
444 char * strAppendUnsigned(char * dest, uint32_t value, uint8_t digits, uint8_t radix)
446 if (digits == 0) {
447 unsigned int tmp = value;
448 digits = 1;
449 while (tmp >= radix) {
450 ++digits;
451 tmp /= radix;
454 uint8_t idx = digits;
455 while (idx > 0) {
456 div_t qr = div(value, radix);
457 uint32_t rem = value % radix;
458 dest[--idx] = (qr.rem >= 10 ? 'A' - 10 : '0') + qr.rem;
459 value = qr.quot;
461 dest[digits] = '\0';
462 return &dest[digits];
465 char * strAppendSigned(char * dest, int32_t value, uint8_t digits, uint8_t radix)
467 if (value < 0) {
468 *dest++ = '-';
469 value = -value;
471 return strAppendUnsigned(dest, (uint32_t)value, digits, radix);
474 char * strAppend(char * dest, const char * source, int len)
476 while ((*dest++ = *source++)) {
477 if (--len == 0) {
478 *dest = '\0';
479 return dest;
482 return dest - 1;
485 char * strSetCursor(char * dest, int position)
487 *dest++ = 0x1F;
488 *dest++ = position;
489 *dest = '\0';
490 return dest;
493 char * strAppendFilename(char * dest, const char * filename, const int size)
495 memset(dest, 0, size);
496 for (int i=0; i<size; i++) {
497 char c = *filename++;
498 if (c == '\0' || c == '.') {
499 *dest = 0;
500 break;
502 *dest++ = c;
504 return dest;
507 #if defined(RTCLOCK)
508 #include "rtc.h"
510 char * strAppendDate(char * str, bool time)
512 str[0] = '-';
513 struct gtm utm;
514 gettime(&utm);
515 div_t qr = div(utm.tm_year+TM_YEAR_BASE, 10);
516 str[4] = '0' + qr.rem;
517 qr = div(qr.quot, 10);
518 str[3] = '0' + qr.rem;
519 qr = div(qr.quot, 10);
520 str[2] = '0' + qr.rem;
521 str[1] = '0' + qr.quot;
522 str[5] = '-';
523 qr = div(utm.tm_mon+1, 10);
524 str[7] = '0' + qr.rem;
525 str[6] = '0' + qr.quot;
526 str[8] = '-';
527 qr = div(utm.tm_mday, 10);
528 str[10] = '0' + qr.rem;
529 str[9] = '0' + qr.quot;
531 if (time) {
532 str[11] = '-';
533 div_t qr = div(utm.tm_hour, 10);
534 str[13] = '0' + qr.rem;
535 str[12] = '0' + qr.quot;
536 qr = div(utm.tm_min, 10);
537 str[15] = '0' + qr.rem;
538 str[14] = '0' + qr.quot;
539 qr = div(utm.tm_sec, 10);
540 str[17] = '0' + qr.rem;
541 str[16] = '0' + qr.quot;
542 str[18] = '\0';
543 return &str[18];
545 else {
546 str[11] = '\0';
547 return &str[11];
550 #endif