diff --git a/src/config/config.c b/src/config/config.c new file mode 100644 index 0000000000..98a4810d61 --- /dev/null +++ b/src/config/config.c @@ -0,0 +1,291 @@ +/* + This project is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Deviation is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Deviation. If not, see . + */ + +#include "common.h" +#include "gui/gui.h" +#include "config.h" +#include "display.h" +#include "mixer.h" +#include "tx.h" + +#include +#include + +typedef const char* StringCallback(int value); +typedef const char* StringCallback2(char* buffer, int value); + +extern u8 FONT_GetFromString(const char *); + +static u16 get_color(const char *value) { + u8 r, g, b; + u32 color = strtol(value, NULL, 16); + r = 0xff & (color >> 16); + g = 0xff & (color >> 8); + b = 0xff & (color >> 0); + return RGB888_to_RGB565(r, g, b); +} + +static u8 get_button(const char *value) +{ + u8 i; + for (i = 0; i <= NUM_TX_BUTTONS; i++) { + if (strcasecmp(INPUT_ButtonName(i), value) == 0) { + return i; + } + } + printf("Could not parse Button %s\n", value); + return 0; +} + + +static s8 mapstrcasecmp(const char *s1, const char *s2) +{ + int i = 0; + while (1) { + if (s1[i] == s2[i] + || (s2[i] >= 'a' && s1[i] + ('a'-'A') == s2[i]) + || (s1[i] >= 'a' && s2[i] + ('a'-'A') == s1[i]) + || (s2[i] == ' ' && s1[i] == '_') + || (s1[i] == ' ' && s2[i] == '_')) + { + if (s1[i] == '\0') + return 0; + i++; + continue; + } + return (s1[i] < s2[i] ? -1 : 1); + } +} + +u8 get_source(const char *value) +{ + unsigned i; + unsigned val; + const char *ptr = (value[0] == '!') ? value + 1 : value; + const char *tmp; + char cmp[10]; + for (i = 0; i <= NUM_SOURCES; i++) { + if (mapstrcasecmp(INPUT_SourceNameReal(cmp, i), ptr) == 0) { + #if defined(HAS_SWITCHES_NOSTOCK) && HAS_SWITCHES_NOSTOCK + #define SWITCH_NOSTOCK ((1 << INP_HOLD0) | (1 << INP_HOLD1) | \ + (1 << INP_FMOD0) | (1 << INP_FMOD1)) + if ((Transmitter.ignore_src & SWITCH_NOSTOCK) == SWITCH_NOSTOCK) { + if (mapstrcasecmp("FMODE0", ptr) == 0 || + mapstrcasecmp("FMODE1", ptr) == 0 || + mapstrcasecmp("HOLD0", ptr) == 0 || + mapstrcasecmp("HOLD1", ptr) == 0) + break; + } + #endif // HAS_SWITCHES_NOSTOCK + return ((ptr == value) ? 0 : 0x80) | i; + } + } + for (i = 0; i < 4; i++) { + if (mapstrcasecmp(tx_stick_names[i], ptr) == 0) { + return ((ptr == value) ? 0 : 0x80) | (i + 1); + } + } + i = 0; + while ((tmp = INPUT_MapSourceName(i++, &val))) { + if (mapstrcasecmp(tmp, ptr) == 0) { + return ((ptr == value) ? 0 : 0x80) | val; + } + } + printf("Could not parse Source %s\n", value); + return 0; +} + +int assign_int(void* ptr, const struct struct_map *map, int map_size, const char* name, const char* value) { + for (int i = 0; i < map_size; i++) { + if (strcasecmp(name, map[i].str) == 0) { + int size = map[i].offset >> 12; + int offset = map[i].offset & 0xFFF; + switch (size) { + case TYPE_S8: + *((s8 *)((u8*)ptr + offset)) = atoi(value); break; + case TYPE_S16: + *((s16 *)((u8*)ptr + offset)) = atoi(value); break; + case TYPE_S32: + *((s32 *)((u8*)ptr + offset)) = atoi(value); break; + + case TYPE_U8: + *((u8 *)((u8*)ptr + offset)) = atoi(value); break; + case TYPE_U16: + *((u16 *)((u8*)ptr + offset)) = atoi(value); break; + case TYPE_U32: + *((u32 *)((u8*)ptr + offset)) = atoi(value); break; + + case TYPE_COLOR: + *((u16 *)((u8*)ptr + offset)) = get_color(value); break; + case TYPE_FONT: + *((u8 *)((u8*)ptr + offset)) = FONT_GetFromString(value); break; + case TYPE_BUTTON: + *((u8 *)((u8*)ptr + offset)) = get_button(value); break; + case TYPE_SOURCE: + *((u8 *)((u8*)ptr + offset)) = get_source(value); break; + + case TYPE_STR_LIST: { + // get the list + i++; // next entry is additional info for string list + const char* const *list = (const char* const *)map[i].str; + unsigned length = map[i].offset; + for (unsigned j = 0; j < length; j++) { + if (list[j] && strcasecmp(value, list[j]) == 0) { + *((u8 *)((u8*)ptr + offset)) = j; + return 1; + } + } + printf("unknow value %s for %s\n", value, name); + break; + } + case TYPE_STR_CALL: { + i++; + unsigned j; + StringCallback *callback = (StringCallback*)map[i].str; + + // If the length is larger than 256, upper part is start index + if (map[i].offset & 0xFF00) { + j = (map[i].offset & 0xFF00) >> 8; + } else { + j = 0; + } + + for (; j < (map[i].offset & 0xFF); j++) + { + if (mapstrcasecmp(value, callback(j)) == 0) { + *((u8 *)((u8*)ptr + offset)) = j; + return 1; + } + } + printf("unknow value %s for %s\n", value, name); + break; + } + case TYPE_STR_CALL2: { + i++; + char strbuf[30]; + unsigned j; + StringCallback2 *callback = (StringCallback2*)map[i].str; + // If the length is larger than 256, upper part is start index + if (map[i].offset & 0xFF00) { + j = (map[i].offset & 0xFF00) >> 8; + } else { + j = 0; + } + for (; j < (map[i].offset & 0xFF); j++) + { + if (mapstrcasecmp(value, callback(strbuf, j)) == 0) { + *((u8 *)((u8*)ptr + offset)) = j; + return 1; + } + } + printf("unknow value %s for %s\n", value, name); + break; + } + default: + printf("Unknown type: %d\n", size); + break; + } + return 1; + } + } + return 0; +} + +int write_int2(void* ptr, const struct struct_map *map, int map_size, + const u16* defaults, int defaults_size, FILE* fh) { + for (int i = 0; i < map_size; i++) { + int size = map[i].offset >> 12; + int offset = map[i].offset & 0xFFF; + int value; + + switch (size) { + case TYPE_S8: + value = *((s8 *)((u8*)ptr + offset)); break; + case TYPE_S16: + value = *((s16 *)((u8*)ptr + offset)); break; + case TYPE_S32: + value = *((s32 *)((u8*)ptr + offset)); break; + + case TYPE_U8: + value = *((u8 *)((u8*)ptr + offset)); break; + case TYPE_U16: + value = *((u16 *)((u8*)ptr + offset)); break; + case TYPE_U32: + value = *((u32 *)((u8*)ptr + offset)); break; + + case TYPE_STR_LIST: + case TYPE_STR_CALL: + case TYPE_STR_CALL2: + i++; // next entry is additional info for string list + value = *((u8 *)((u8*)ptr + offset)); + break; + + case TYPE_BUTTON: + case TYPE_SOURCE: + value = *((u8 *)((u8*)ptr + offset)); break; + + default: + continue; // unsupported type + } + + if (defaults == DEFAULTS_ZERO) { + if (value == 0) continue; + } else if (defaults == DEFAULTS_ALWAYS) { + // always write out + } else if (defaults != NULL) { + if (i < defaults_size && value == defaults[i]) continue; + } + + switch (size) + { + case TYPE_STR_LIST: { + const char* const *list = (const char* const *)map[i].str; + fprintf(fh, "%s=%s\n", map[i - 1].str, list[value]); + break; + } + case TYPE_BUTTON: + fprintf(fh, "%s=%s\n", map[i].str, INPUT_ButtonName(value)); + break; + case TYPE_SOURCE: { + char tmpstr[20]; + fprintf(fh, "%s=%s\n", map[i].str, INPUT_SourceNameReal(tmpstr, value)); + break; + } + case TYPE_STR_CALL: { + StringCallback *callback = (StringCallback*)map[i].str; + fprintf(fh, "%s=%s\n", map[i - 1].str, callback(value)); + break; + } + case TYPE_STR_CALL2: { + char strbuf[30]; + StringCallback2 *callback = (StringCallback2*)map[i].str; + fprintf(fh, "%s=%s\n", map[i - 1].str, callback(strbuf, value)); + break; + } + default: + fprintf(fh, "%s=%d\n", map[i].str, value); + } + } + + return 1; +} + +int write_int(void* ptr, const struct struct_map *map, int map_size, FILE* fh) { + return write_int2(ptr, map, map_size, DEFAULTS_ALWAYS, 0, fh); +} + +#define TESTNAME config +#include "tests.h" + diff --git a/src/config/config.h b/src/config/config.h new file mode 100644 index 0000000000..fdc0838328 --- /dev/null +++ b/src/config/config.h @@ -0,0 +1,64 @@ +/* + This project is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Deviation is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Deviation. If not, see . + */ + +#ifndef __CONFIG_H_ +#define __CONFIG_H_ + +#include + +enum { + TYPE_U8 = 0, + TYPE_U16 = 1, + TYPE_U32 = 3, + + TYPE_S8 = 4, + TYPE_S16 = 5, + TYPE_S32 = 7, + + TYPE_STR_LIST = 8, + TYPE_STR_CALL = 9, + TYPE_STR_CALL2 = 10, + + TYPE_COLOR = 11, + TYPE_FONT = 12, + TYPE_SOURCE = 13, + TYPE_BUTTON = 14, +}; + +struct struct_map {const char *str; u16 offset;}; +#define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0])) +#define OFFSET(s, v) (offsetof(s, v) | ((sizeof(((s*)0)->v) - 1) << 12)) +#define OFFSETS(s, v) (offsetof(s, v) | ((sizeof(((s*)0)->v) + 3) << 12)) +#define OFFSET_COL(s, v) (offsetof(s, v) | (TYPE_COLOR << 12)) +#define OFFSET_FON(s, v) (offsetof(s, v) | (TYPE_FONT << 12)) +#define OFFSET_STRLIST(s, v, StrList, StrListSize) \ + (offsetof(s, v) | (TYPE_STR_LIST << 12)) }, { (const char*)StrList, StrListSize +#define OFFSET_STRCALL(s, v, StrCallback, N) \ + (offsetof(s, v) | (TYPE_STR_CALL << 12)) }, { (const char*)StrCallback, N +#define OFFSET_STRCALL2(s, v, StrCallback, N) \ + (offsetof(s, v) | (TYPE_STR_CALL2 << 12)) }, { (const char*)StrCallback, N +#define OFFSET_SRC(s, v) (offsetof(s, v) | (TYPE_SOURCE << 12)) +#define OFFSET_BUT(s, v) (offsetof(s, v) | (TYPE_BUTTON << 12)) + +int assign_int(void* ptr, const struct struct_map *map, int map_size, + const char* name, const char* value); +int write_int(void* ptr, const struct struct_map *map, int map_size, FILE* fh); + +#define DEFAULTS_ZERO (const u16*)0x01 +#define DEFAULTS_ALWAYS (const u16*)0x02 +int write_int2(void* ptr, const struct struct_map *map, int map_size, + const u16* defaults, int defaults_size, FILE* fh); + +#endif diff --git a/src/config/display.c b/src/config/display.c index 7b6e0f033d..723c5c9771 100644 --- a/src/config/display.c +++ b/src/config/display.c @@ -15,6 +15,7 @@ #include "common.h" #include "gui/gui.h" +#include "config.h" #include "display.h" #include #include @@ -86,132 +87,83 @@ static const char * const ALIGN_VAL[] = { #define MATCH_START(x,y) strncasecmp(x, y, sizeof(y)-1) == 0 #define MATCH_KEY(s) strcasecmp(name, s) == 0 #define MATCH_VALUE(s) strcasecmp(value, s) == 0 -#define NUM_STR_ELEMS(s) (sizeof(s) / sizeof(char *)) #define SET_FLAG(var, value, flag) ((value) ? ((var) | (flag)) : ((var) & ~(flag))) -extern u8 FONT_GetFromString(const char *); -struct display_settings Display; -u16 get_color(const char *value) { - u8 r, g, b; - u32 color = strtol(value, NULL, 16); - r = 0xff & (color >> 16); - g = 0xff & (color >> 8); - b = 0xff & (color >> 0); - return RGB888_to_RGB565(r, g, b); -} +struct display_settings Display; -static int handle_label(struct LabelDesc *label, const char *name, const char *value) +static const struct struct_map _seclabel[] = { - if(MATCH_KEY(FONT)) { - label->font = FONT_GetFromString(value); - return 1; - } - if(MATCH_KEY(FONT_COLOR)) { - label->font_color = get_color(value); - return 1; - } - if(MATCH_KEY(BG_COLOR)) { - label->fill_color = get_color(value); - return 1; - } - if(MATCH_KEY(OUTLINE_COLOR)) { - label->outline_color = get_color(value); - return 1; - } - if(MATCH_KEY(BOX)) { - u8 idx; - for (idx = 0; idx < NUM_STR_ELEMS(BOX_VAL); idx++) { - if(BOX_VAL[idx] && MATCH_VALUE(BOX_VAL[idx])) { - label->style = idx; - // For compatibility reasons, - // use the old alignment values as default - // if the new one is not yet set: - if(label->align == 0) { - switch(idx) { - case LABEL_CENTER: label->align = ALIGN_CENTER; break; - case LABEL_LEFT: label->align = ALIGN_LEFT; break; - case LABEL_RIGHT: label->align = ALIGN_RIGHT; break; - } - } - } - } - } - if(MATCH_KEY(ALIGN)) { - u8 idx; - for (idx = 0; idx < NUM_STR_ELEMS(ALIGN_VAL); idx++) { - if(ALIGN_VAL[idx] && MATCH_VALUE(ALIGN_VAL[idx])) { - label->align = idx; - } - } - } - return 0; -} + {FONT, OFFSET_FON(struct LabelDesc, font)}, + {FONT_COLOR, OFFSET_COL(struct LabelDesc, font_color)}, + {BG_COLOR, OFFSET_COL(struct LabelDesc, fill_color)}, + {OUTLINE_COLOR, OFFSET_COL(struct LabelDesc, outline_color)}, + {ALIGN, OFFSET_STRLIST(struct LabelDesc, align, ALIGN_VAL, ARRAYSIZE(ALIGN_VAL))}, + {BOX, OFFSET_STRLIST(struct LabelDesc, style, BOX_VAL, ARRAYSIZE(BOX_VAL))} +}; -struct struct_map {const char *str; u16 offset;}; -#define MAPSIZE(x) (sizeof(x) / sizeof(struct struct_map)) -#define OFFSET(s,v) (((long)(&s.v) - (long)(&s)) | ((sizeof(s.v)-1) << 13)) -#define OFFSETS(s,v) (((long)(&s.v) - (long)(&s)) | ((sizeof(s.v)+3) << 13)) -#define OFFSET_COL(s,v) (((long)(&s.v) - (long)(&s)) | (2 << 13)) -#define OFFSET_FON(s,v) (((long)(&s.v) - (long)(&s)) | (6 << 13)) static const struct struct_map _secgeneral[] = { - {"header_height", OFFSET(Display.metrics, header_height)}, - {"header_widget_height", OFFSET(Display.metrics, header_widget_height)}, - {"line_height", OFFSET(Display.metrics, line_height)}, - {"line_space", OFFSET(Display.metrics, line_space)}, + {"header_height", OFFSET(struct disp_metrics, header_height)}, + {"header_widget_height", OFFSET(struct disp_metrics, header_widget_height)}, + {"line_height", OFFSET(struct disp_metrics, line_height)}, + {"line_space", OFFSET(struct disp_metrics, line_space)}, }; static const struct struct_map _secselect[] = { - {"width", OFFSET(Display, select_width)}, - {COLOR, OFFSET_COL(Display, select_color)}, + {"width", OFFSET(struct display_settings, select_width)}, + {COLOR, OFFSET_COL(struct display_settings, select_color)}, }; static const struct struct_map _seckeybd[] = { - {FONT, OFFSET_FON(Display.keyboard, font)}, - {"bg_key1", OFFSET_COL(Display.keyboard, bg_key1)}, - {"fg_key1", OFFSET_COL(Display.keyboard, fg_key1)}, - {"bg_key2", OFFSET_COL(Display.keyboard, bg_key2)}, - {"fg_key2", OFFSET_COL(Display.keyboard, fg_key2)}, - {"bg_key3", OFFSET_COL(Display.keyboard, bg_key3)}, - {"fg_key3", OFFSET_COL(Display.keyboard, fg_key3)}, - {BG_COLOR, OFFSET_COL(Display.keyboard, fill_color)}, + {FONT, OFFSET_FON(struct disp_keyboard, font)}, + {"bg_key1", OFFSET_COL(struct disp_keyboard, bg_key1)}, + {"fg_key1", OFFSET_COL(struct disp_keyboard, fg_key1)}, + {"bg_key2", OFFSET_COL(struct disp_keyboard, bg_key2)}, + {"fg_key2", OFFSET_COL(struct disp_keyboard, fg_key2)}, + {"bg_key3", OFFSET_COL(struct disp_keyboard, bg_key3)}, + {"fg_key3", OFFSET_COL(struct disp_keyboard, fg_key3)}, + {BG_COLOR, OFFSET_COL(struct disp_keyboard, fill_color)}, }; static const struct struct_map _seclistbox[] = { - {FONT, OFFSET_FON(Display.listbox, font)}, - {BG_COLOR, OFFSET_COL(Display.listbox, bg_color)}, - {FG_COLOR, OFFSET_COL(Display.listbox, fg_color)}, - {"bg_select", OFFSET_COL(Display.listbox, bg_select)}, - {"fg_select", OFFSET_COL(Display.listbox, fg_select)}, + {FONT, OFFSET_FON(struct disp_listbox, font)}, + {BG_COLOR, OFFSET_COL(struct disp_listbox, bg_color)}, + {FG_COLOR, OFFSET_COL(struct disp_listbox, fg_color)}, + {"bg_select", OFFSET_COL(struct disp_listbox, bg_select)}, + {"fg_select", OFFSET_COL(struct disp_listbox, fg_select)}, }; static const struct struct_map _secscroll[] = { - {BG_COLOR, OFFSET_COL(Display.scrollbar, bg_color)}, - {FG_COLOR, OFFSET_COL(Display.scrollbar, fg_color)}, + {BG_COLOR, OFFSET_COL(struct disp_scrollbar, bg_color)}, + {FG_COLOR, OFFSET_COL(struct disp_scrollbar, fg_color)}, }; static const struct struct_map _secxygraph[] = { - {BG_COLOR, OFFSET_COL(Display.xygraph, bg_color)}, - {FG_COLOR, OFFSET_COL(Display.xygraph, fg_color)}, - {XY_AXIS_COLOR, OFFSET_COL(Display.xygraph, axis_color)}, - {XY_GRID_COLOR, OFFSET_COL(Display.xygraph, grid_color)}, - {XY_POINT_COLOR, OFFSET_COL(Display.xygraph, point_color)}, - {OUTLINE_COLOR, OFFSET_COL(Display.xygraph, outline_color)}, + {BG_COLOR, OFFSET_COL(struct disp_xygraph, bg_color)}, + {FG_COLOR, OFFSET_COL(struct disp_xygraph, fg_color)}, + {XY_AXIS_COLOR, OFFSET_COL(struct disp_xygraph, axis_color)}, + {XY_GRID_COLOR, OFFSET_COL(struct disp_xygraph, grid_color)}, + {XY_POINT_COLOR, OFFSET_COL(struct disp_xygraph, point_color)}, + {OUTLINE_COLOR, OFFSET_COL(struct disp_xygraph, outline_color)}, }; static const struct struct_map _secbargraph[] = { - {BG_COLOR, OFFSET_COL(Display.bargraph, bg_color)}, - {FG_COLOR_POS, OFFSET_COL(Display.bargraph, fg_color_pos)}, - {FG_COLOR_NEG, OFFSET_COL(Display.bargraph, fg_color_neg)}, - {FG_COLOR_ZERO, OFFSET_COL(Display.bargraph, fg_color_zero)}, - {OUTLINE_COLOR, OFFSET_COL(Display.bargraph, outline_color)}, + {BG_COLOR, OFFSET_COL(struct disp_bargraph, bg_color)}, + {FG_COLOR_POS, OFFSET_COL(struct disp_bargraph, fg_color_pos)}, + {FG_COLOR_NEG, OFFSET_COL(struct disp_bargraph, fg_color_neg)}, + {FG_COLOR_ZERO, OFFSET_COL(struct disp_bargraph, fg_color_zero)}, + {OUTLINE_COLOR, OFFSET_COL(struct disp_bargraph, outline_color)}, +}; +static const struct struct_map _secbargraph_legacy[] = +{ + {FG_COLOR, OFFSET_COL(struct disp_bargraph, fg_color_pos)}, }; #if (LCD_WIDTH == 480) || (LCD_WIDTH == 320) static const struct struct_map _secbackground[] = { - {"drawn_background", OFFSET(Display.background, drawn_background)}, - {"bg_color", OFFSET_COL(Display.background, bg_color)}, - {"hd_color", OFFSET_COL(Display.background, hd_color)}, + {"drawn_background", OFFSET(struct disp_background, drawn_background)}, + {"bg_color", OFFSET_COL(struct disp_background, bg_color)}, + {"hd_color", OFFSET_COL(struct disp_background, hd_color)}, }; #endif @@ -221,35 +173,10 @@ static int ini_handler(void* user, const char* section, const char* name, const struct display_settings *d = (struct display_settings *)user; int value_int = atoi(value); - int assign_int(void* ptr, const struct struct_map *map, int map_size) - { - for(int i = 0; i < map_size; i++) { - if(MATCH_KEY(map[i].str)) { - int size = map[i].offset >> 13; - int offset = map[i].offset & 0x1FFF; - switch(size) { - case 0: - *((u8 *)((long)ptr + offset)) = value_int; break; - case 1: - *((u16 *)((long)ptr + offset)) = value_int; break; - case 2: - *((u16 *)((long)ptr + offset)) = get_color(value); break; - case 3: - *((u32 *)((long)ptr + offset)) = value_int; break; - case 6: - *((u8 *)((long)ptr + offset)) = FONT_GetFromString(value); - } - return 1; - } - } - return 0; - } - if(MATCH_START(section, FONT) && strlen(section) > sizeof(FONT)) { - for (idx = 0; idx < NUM_STR_ELEMS(FONT_VAL); idx++) { + for (idx = 0; idx < ARRAYSIZE(FONT_VAL); idx++) { if (FONT_VAL[idx] && 0 == strcasecmp(section + sizeof(FONT), FONT_VAL[idx])) { - handle_label(&d->font[idx], name, value); - return 1; + return assign_int(&d->font[idx], _seclabel, ARRAYSIZE(_seclabel), name, value); } } printf("Couldn't parse font: %s\n", section); @@ -266,36 +193,36 @@ static int ini_handler(void* user, const char* section, const char* name, const #endif return 1; } - if(assign_int(&d->metrics, _secgeneral, MAPSIZE(_secgeneral))) + if (assign_int(&d->metrics, _secgeneral, ARRAYSIZE(_secgeneral), name, value)) return 1; } if(MATCH_START(section, "select")) { - if(assign_int(d, _secselect, MAPSIZE(_secselect))) + if (assign_int(d, _secselect, ARRAYSIZE(_secselect), name, value)) return 1; } if(MATCH_START(section, "keyboard")) { - if(assign_int(&d->keyboard, _seckeybd, MAPSIZE(_seckeybd))) + if (assign_int(&d->keyboard, _seckeybd, ARRAYSIZE(_seckeybd), name, value)) return 1; } if(MATCH_START(section, "listbox")) { - if(assign_int(&d->listbox, _seclistbox, MAPSIZE(_seclistbox))) + if (assign_int(&d->listbox, _seclistbox, ARRAYSIZE(_seclistbox), name, value)) return 1; } if(MATCH_START(section, "scrollbar")) { - if(assign_int(&d->scrollbar, _secscroll, MAPSIZE(_secscroll))) + if (assign_int(&d->scrollbar, _secscroll, ARRAYSIZE(_secscroll), name, value)) return 1; } if(MATCH_SECTION("xygraph")) { - if(assign_int(&d->xygraph, _secxygraph, MAPSIZE(_secxygraph))) + if (assign_int(&d->xygraph, _secxygraph, ARRAYSIZE(_secxygraph), name, value)) return 1; } #if (LCD_WIDTH == 480) || (LCD_WIDTH == 320) if(MATCH_SECTION("background")) { - if(assign_int(&d->background, _secbackground, MAPSIZE(_secbackground))) + if (assign_int(&d->background, _secbackground, ARRAYSIZE(_secbackground), name, value)) return 1; } #endif - for (idx = 0; idx < NUM_STR_ELEMS(BARGRAPH_VAL); idx++) { + for (idx = 0; idx < ARRAYSIZE(BARGRAPH_VAL); idx++) { if(MATCH_SECTION(BARGRAPH_VAL[idx])) { struct disp_bargraph *graph; enum DispFlags flag; @@ -310,11 +237,15 @@ static int ini_handler(void* user, const char* section, const char* name, const d->flags = SET_FLAG(d->flags, value_int, flag); return 1; } - if(MATCH_KEY(FG_COLOR)) { - graph->fg_color_pos = graph->fg_color_neg = graph->fg_color_zero = get_color(value); + + if (assign_int(graph, _secbargraph, ARRAYSIZE(_secbargraph), name, value)) + return 1; + + if (assign_int(graph, _secbargraph_legacy, ARRAYSIZE(_secbargraph_legacy), name, value)) { + graph->fg_color_neg = graph->fg_color_zero = graph->fg_color_pos; return 1; } - assign_int(graph, _secbargraph, MAPSIZE(_secbargraph)); + return 1; } } @@ -322,6 +253,24 @@ static int ini_handler(void* user, const char* section, const char* name, const return 1; } +static void convert_legacy() +{ + for (int i = 0; i < NUM_LABELS; i++) { + struct LabelDesc *label = &Display.font[i]; + // For compatibility reasons, + // use the old alignment values as default + // if the new one is not yet set: + if (label->align == 0) { + switch (label->style) { + case LABEL_CENTER: label->align = ALIGN_CENTER; break; + case LABEL_LEFT: label->align = ALIGN_LEFT; break; + case LABEL_RIGHT: label->align = ALIGN_RIGHT; break; + default: break; + } + } + } +} + u8 CONFIG_ReadDisplay() { memset(&Display, 0, sizeof(Display)); @@ -342,5 +291,10 @@ u8 CONFIG_ReadDisplay() #else char filename[] = "media/config.ini"; #endif - return CONFIG_IniParse(filename, ini_handler, (void *)&Display); + if (CONFIG_IniParse(filename, ini_handler, (void *)&Display)) { + convert_legacy(); + return 1; + } + + return 0; } diff --git a/src/config/model.c b/src/config/model.c index df598122d8..6d410301aa 100644 --- a/src/config/model.c +++ b/src/config/model.c @@ -20,15 +20,19 @@ #include "music.h" #include "extended_audio.h" +#include "config.h" + #include #include + +extern u8 get_source(const char *value); extern const u8 EATRG0[PROTO_MAP_LEN]; struct Model Model; /*set this to write all model data even if it is the same as the default */ static u32 crc32; -const char * const MODEL_TYPE_VAL[MODELTYPE_LAST] = { "heli", "plane", "multi" }; +static const char * const MODEL_TYPE_VAL[MODELTYPE_LAST] = { "heli", "plane", "multi" }; const u8 RADIO_TX_POWER_COUNT[TX_MODULE_LAST] = { // number of power settings 8, // CYRF6936, 7, // A7105, @@ -252,55 +256,6 @@ s8 mapstrcasecmp(const char *s1, const char *s2) return(s1[i] < s2[i] ? -1 : 1); } } -static u8 get_source(const char *section, const char *value) -{ - unsigned i; - unsigned val; - const char *ptr = (value[0] == '!') ? value + 1 : value; - const char *tmp; - char cmp[10]; - for (i = 0; i <= NUM_SOURCES; i++) { - if(mapstrcasecmp(INPUT_SourceNameReal(cmp, i), ptr) == 0) { - #if defined(HAS_SWITCHES_NOSTOCK) && HAS_SWITCHES_NOSTOCK - #define SWITCH_NOSTOCK ((1 << INP_HOLD0) | (1 << INP_HOLD1) | \ - (1 << INP_FMOD0) | (1 << INP_FMOD1)) - if ((Transmitter.ignore_src & SWITCH_NOSTOCK) == SWITCH_NOSTOCK) { - if(mapstrcasecmp("FMODE0", ptr) == 0 || - mapstrcasecmp("FMODE1", ptr) == 0 || - mapstrcasecmp("HOLD0", ptr) == 0 || - mapstrcasecmp("HOLD1", ptr) == 0) - break; - } - #endif //HAS_SWITCHES_NOSTOCK - return ((ptr == value) ? 0 : 0x80) | i; - } - } - for (i = 0; i < 4; i++) { - if(mapstrcasecmp(tx_stick_names[i], ptr) == 0) { - return ((ptr == value) ? 0 : 0x80) | (i + 1); - } - } - i = 0; - while((tmp = INPUT_MapSourceName(i++, &val))) { - if(mapstrcasecmp(tmp, ptr) == 0) { - return ((ptr == value) ? 0 : 0x80) | val; - } - } - printf("%s: Could not parse Source %s\n", section, value); - return 0; -} - -static u8 get_button(const char *section, const char *value) -{ - u8 i; - for (i = 0; i <= NUM_TX_BUTTONS; i++) { - if(strcasecmp(INPUT_ButtonName(i), value) == 0) { - return i; - } - } - printf("%s: Could not parse Button %s\n", section, value); - return 0; -} static int handle_proto_opts(struct Model *m, const char* key, const char* value, const char **opts) { @@ -515,7 +470,7 @@ static int layout_ini_handler(void* user, const char* section, const char* name, } } if (src == -1) { - u8 newsrc = get_source(section, ptr); + u8 newsrc = get_source(ptr); if(newsrc >= NUM_INPUTS) { src = newsrc - (NUM_INPUTS + 1 - (NUM_RTC + NUM_TIMERS + NUM_TELEM + 1)); } @@ -529,7 +484,7 @@ static int layout_ini_handler(void* user, const char* section, const char* name, { if (count != 3) return 1; - u8 src = get_source(section, ptr); + u8 src = get_source(ptr); if (src < NUM_INPUTS) src = 0; data[5] = src - NUM_INPUTS; @@ -553,101 +508,94 @@ static int layout_ini_handler(void* user, const char* section, const char* name, return 1; } -struct struct_map {const char *str; u16 offset; u16 defval;}; -#define MAPSIZE(x) (sizeof(x) / sizeof(struct struct_map)) -#define OFFSET(s,v) (((long)(&s.v) - (long)(&s)) | ((sizeof(s.v)-1) << 13)) -#define OFFSETS(s,v) (((long)(&s.v) - (long)(&s)) | ((sizeof(s.v)+3) << 13)) -#define OFFSET_SRC(s,v) (((long)(&s.v) - (long)(&s)) | (2 << 13)) -#define OFFSET_BUT(s,v) (((long)(&s.v) - (long)(&s)) | (6 << 13)) -#if HAS_PERMANENT_TIMER static const struct struct_map _secnone[] = { - {PERMANENT_TIMER, OFFSET(Model, permanent_timer), 0}, -}; +#if HAS_PERMANENT_TIMER + {PERMANENT_TIMER, OFFSET(struct Model, permanent_timer)}, #endif + {MODEL_TYPE, OFFSET_STRLIST(struct Model, type, MODEL_TYPE_VAL, ARRAYSIZE(MODEL_TYPE_VAL))}, + {MODEL_MIXERMODE, OFFSET_STRCALL(struct Model, mixer_mode, STDMIXER_ModeName, 0x0103)}, +}; static const struct struct_map _secradio[] = { - {RADIO_NUM_CHANNELS, OFFSET(Model, num_channels), 0}, - {RADIO_FIXED_ID, OFFSET(Model, fixed_id), 0}, + {RADIO_NUM_CHANNELS, OFFSET(struct Model, num_channels)}, + {RADIO_FIXED_ID, OFFSET(struct Model, fixed_id)}, #if HAS_VIDEO - {RADIO_VIDEOSRC, OFFSET_SRC(Model, videosrc), 0}, - {RADIO_VIDEOCH, OFFSET(Model, videoch), 0}, - {RADIO_VIDEOCONTRAST,OFFSET(Model, video_contrast), 0}, - {RADIO_VIDEOBRIGHTNESS,OFFSET(Model, video_brightness), 0}, + {RADIO_VIDEOSRC, OFFSET_SRC(struct Model, videosrc)}, + {RADIO_VIDEOCH, OFFSET(struct Model, videoch)}, + {RADIO_VIDEOCONTRAST, OFFSET(struct Model, video_contrast)}, + {RADIO_VIDEOBRIGHTNESS, OFFSET(struct Model, video_brightness)}, #endif #if HAS_EXTENDED_TELEMETRY - {RADIO_GROUND_LEVEL, OFFSET(Model, ground_level), 0}, + {RADIO_GROUND_LEVEL, OFFSET(struct Model, ground_level)}, #endif + {RADIO_PROTOCOL, OFFSET_STRCALL(struct Model, protocol, PROTOCOL_GetName, PROTOCOL_COUNT)}, }; static const struct struct_map _secmixer[] = { - {MIXER_SWITCH, OFFSET_SRC(Model.mixers[0], sw), 0}, - {MIXER_SCALAR, OFFSETS(Model.mixers[0], scalar), 100}, - {MIXER_OFFSET, OFFSETS(Model.mixers[0], offset), 0}, + {MIXER_SCALAR, OFFSETS(struct Mixer, scalar)}, + {MIXER_SWITCH, OFFSET_SRC(struct Mixer, sw)}, + {MIXER_OFFSET, OFFSETS(struct Mixer, offset)}, +}; +static const u16 _secmixer_defaults[] = { + 100, 0, 0 }; static const struct struct_map _seclimit[] = { - {CHAN_LIMIT_SAFETYSW, OFFSET_SRC(Model.limits[0], safetysw), 0}, - {CHAN_LIMIT_SAFETYVAL, OFFSETS(Model.limits[0], safetyval), 0}, - {CHAN_LIMIT_MAX, OFFSET(Model.limits[0], max), DEFAULT_SERVO_LIMIT}, - {CHAN_LIMIT_SPEED, OFFSET(Model.limits[0], speed), 0}, - {CHAN_SCALAR, OFFSET(Model.limits[0], servoscale), 100}, - {CHAN_SCALAR_NEG, OFFSET(Model.limits[0], servoscale_neg), 0}, - {CHAN_SUBTRIM, OFFSETS(Model.limits[0], subtrim), 0}, - {CHAN_DISPLAY_SCALE, OFFSETS(Model.limits[0], displayscale), DEFAULT_DISPLAY_SCALE}, + {CHAN_LIMIT_SAFETYSW, OFFSET_SRC(struct Limit, safetysw)}, + {CHAN_LIMIT_SAFETYVAL, OFFSETS(struct Limit, safetyval)}, + {CHAN_LIMIT_MAX, OFFSET(struct Limit, max)}, + {CHAN_LIMIT_SPEED, OFFSET(struct Limit, speed)}, + {CHAN_SCALAR, OFFSET(struct Limit, servoscale)}, + {CHAN_SCALAR_NEG, OFFSET(struct Limit, servoscale_neg)}, + {CHAN_SUBTRIM, OFFSETS(struct Limit, subtrim)}, + {CHAN_DISPLAY_SCALE, OFFSETS(struct Limit, displayscale)}, +}; +static const u16 _seclimit_defaults[] = { + 0, 0, DEFAULT_SERVO_LIMIT, 0, 100, 0, 0, DEFAULT_DISPLAY_SCALE }; static const struct struct_map _sectrim[] = { - {TRIM_SOURCE, OFFSET_SRC(Model.trims[0], src), 0xFFFF}, - {TRIM_POS, OFFSET_BUT(Model.trims[0], pos), 0}, - {TRIM_NEG, OFFSET_BUT(Model.trims[0], neg), 0}, - {TRIM_STEP, OFFSET(Model.trims[0], step), 1}, + {TRIM_STEP, OFFSET(struct Trim, step)}, + {TRIM_POS, OFFSET_BUT(struct Trim, pos)}, + {TRIM_NEG, OFFSET_BUT(struct Trim, neg)}, +}; +static const u16 _sectrim_defaults[] = { + 1, 0, 0 +}; +static const struct struct_map _sectrim_rdonly[] = { + {TRIM_SOURCE, OFFSET_SRC(struct Trim, src)}, }; + static const struct struct_map _secswash[] = { - {SWASH_AILMIX, OFFSET(Model, swashmix[0]), 60}, - {SWASH_ELEMIX, OFFSET(Model, swashmix[1]), 60}, - {SWASH_COLMIX, OFFSET(Model, swashmix[2]), 60}, + {SWASH_AILMIX, OFFSET(struct Model, swashmix[0])}, + {SWASH_ELEMIX, OFFSET(struct Model, swashmix[1])}, + {SWASH_COLMIX, OFFSET(struct Model, swashmix[2])}, + {SWASH_TYPE, OFFSET_STRCALL(struct Model, swash_type, MIXER_SwashType, SWASH_TYPE_LAST)}, +}; +static const u16 _secswash_defaults[] = { + 60, 60, 60, 0 }; + static const struct struct_map _sectimer[] = { - {TIMER_TIME, OFFSET(Model.timer[0], timer), 0xFFFF}, - {TIMER_VAL, OFFSET(Model.timer[0], val), 0xFFFF}, - {TIMER_SOURCE, OFFSET_SRC(Model.timer[0], src), 0}, - {TIMER_RESETSRC, OFFSET_SRC(Model.timer[0], resetsrc), 0}, + {TIMER_SOURCE, OFFSET_SRC(struct Timer, src)}, + {TIMER_RESETSRC, OFFSET_SRC(struct Timer, resetsrc)}, + {TIMER_TYPE, OFFSET_STRLIST(struct Timer, type, TIMER_TYPE_VAL, ARRAYSIZE(TIMER_TYPE_VAL))} }; +static const struct struct_map _sectimer_rdonly[] = { + {TIMER_TIME, OFFSET(struct Timer, timer)}, + {TIMER_VAL, OFFSET(struct Timer, val)}, +}; + static const struct struct_map _secppm[] = { - {PPMIN_CENTERPW, OFFSET(Model, ppmin_centerpw), 0}, - {PPMIN_DELTAPW, OFFSET(Model, ppmin_deltapw), 0}, - {PPMIN_SWITCH, OFFSET_SRC(Model, train_sw), 0xFFFF}, + {PPMIN_CENTERPW, OFFSET(struct Model, ppmin_centerpw)}, + {PPMIN_DELTAPW, OFFSET(struct Model, ppmin_deltapw)}, }; +static const struct struct_map _secppm_rdonly[] = { + {PPMIN_SWITCH, OFFSET_SRC(struct Model, train_sw)}, +}; + static int ini_handler(void* user, const char* section, const char* name, const char* value) { int value_int = atoi(value); struct Model *m = (struct Model *)user; -int assign_int(void* ptr, const struct struct_map *map, int map_size) -{ - for(int i = 0; i < map_size; i++) { - if(MATCH_KEY(map[i].str)) { - int size = map[i].offset >> 13; - int offset = map[i].offset & 0x1FFF; - switch(size) { - case 0: - *((u8 *)((long)ptr + offset)) = value_int; break; - case 1: - *((u16 *)((long)ptr + offset)) = value_int; break; - case 2: - *((u8 *)((long)ptr + offset)) = get_source(section, value); break; - case 3: - *((u32 *)((long)ptr + offset)) = value_int; break; - case 4: - *((s8 *)((long)ptr + offset)) = value_int; break; - case 5: - *((s16 *)((long)ptr + offset)) = value_int; break; - case 6: - *((u8 *)((long)ptr + offset)) = get_button(section, value); break; - case 7: - *((s32 *)((long)ptr + offset)) = value_int; break; - } - return 1; - } - } - return 0; -} + CLOCK_ResetWatchdog(); unsigned i; if (MATCH_SECTION("")) { @@ -663,45 +611,15 @@ int assign_int(void* ptr, const struct struct_map *map, int map_size) CONFIG_ParseIconName(m->icon, value); return 1; } -#if HAS_PERMANENT_TIMER - if(assign_int(&Model, _secnone, MAPSIZE(_secnone))) - return 1; -#endif - if (MATCH_KEY(MODEL_TYPE)) { - for (i = 0; i < NUM_STR_ELEMS(MODEL_TYPE_VAL); i++) { - if (MATCH_VALUE(MODEL_TYPE_VAL[i])) { - m->type = i; - return 1; - } - } - return 0; - } if (MATCH_KEY(MODEL_AUTOMAP)) { auto_map = atoi(value); return 1; } - if (MATCH_KEY(MODEL_MIXERMODE)) { - for(i = 1; i < 3; i++) { - if(MATCH_VALUE(STDMIXER_ModeName(i))) - m->mixer_mode = i; - } + if (assign_int(&Model, _secnone, ARRAYSIZE(_secnone), name, value)) return 1; - } } if (MATCH_SECTION(SECTION_RADIO)) { - if (MATCH_KEY(RADIO_PROTOCOL)) { - for (i = 0; i < PROTOCOL_COUNT; i++) { - if (MATCH_VALUE(PROTOCOL_GetName(i))) { - m->protocol = i; - m->radio = PROTOCOL_GetRadio(i); - PROTOCOL_Load(1); - return 1; - } - } - printf("Unknown protocol: %s\n", value); - return 1; - } - if(assign_int(&Model, _secradio, MAPSIZE(_secradio))) + if (assign_int(&Model, _secradio, ARRAYSIZE(_secradio), name, value)) return 1; if (MATCH_KEY(RADIO_TX_POWER)) { if (m->radio == TX_MODULE_LAST) { @@ -738,15 +656,15 @@ int assign_int(void* ptr, const struct struct_map *map, int map_size) printf("%s: Only %d mixers are supported\n", section, NUM_MIXERS); return 1; } - m->mixers[idx].src = get_source(section, value); + m->mixers[idx].src = get_source(value); return 1; } idx--; if (MATCH_KEY(MIXER_DEST)) { - m->mixers[idx].dest = get_source(section, value) - NUM_INPUTS - 1; + m->mixers[idx].dest = get_source(value) - NUM_INPUTS - 1; return 1; } - if(assign_int(&m->mixers[idx], _secmixer, MAPSIZE(_secmixer))) + if (assign_int(&m->mixers[idx], _secmixer, ARRAYSIZE(_secmixer), name, value)) return 1; if (MATCH_KEY(MIXER_USETRIM)) { MIXER_SET_APPLY_TRIM(&m->mixers[idx], value_int); @@ -819,7 +737,7 @@ int assign_int(void* ptr, const struct struct_map *map, int map_size) return 1; } - if(assign_int(&m->limits[idx], _seclimit, MAPSIZE(_seclimit))) + if (assign_int(&m->limits[idx], _seclimit, ARRAYSIZE(_seclimit), name, value)) return 1; if (MATCH_KEY(CHAN_LIMIT_MIN)) { m->limits[idx].min = -value_int; @@ -877,7 +795,9 @@ int assign_int(void* ptr, const struct struct_map *map, int map_size) return 1; } idx--; - if(assign_int(&m->trims[idx], _sectrim, MAPSIZE(_sectrim))) + if (assign_int(&m->trims[idx], _sectrim, ARRAYSIZE(_sectrim), name, value)) + return 1; + if (assign_int(&m->trims[idx], _sectrim_rdonly, ARRAYSIZE(_sectrim_rdonly), name, value)) return 1; if (MATCH_KEY(TRIM_SWITCH)) { for (int i = 0; i <= NUM_SOURCES; i++) { @@ -897,16 +817,6 @@ int assign_int(void* ptr, const struct struct_map *map, int map_size) return 0; } if (MATCH_SECTION(SECTION_SWASH)) { - if (MATCH_KEY(SWASH_TYPE)) { - for (i = SWASH_TYPE_NONE; i <= SWASH_TYPE_90; i++) { - if(strcasecmp(MIXER_SwashType(i), value) == 0) { - m->swash_type = i; - return 1; - } - } - printf("%s: Unknown swash_type: %s\n", section, value); - return 1; - } if (MATCH_KEY(SWASH_ELE_INV)) { if (value_int) m->swash_invert |= 0x01; @@ -922,7 +832,7 @@ int assign_int(void* ptr, const struct struct_map *map, int map_size) m->swash_invert |= 0x04; return 1; } - if(assign_int(m, _secswash, MAPSIZE(_secswash))) + if (assign_int(m, _secswash, ARRAYSIZE(_secswash), name, value)) return 1; } if (MATCH_START(section, SECTION_TIMER)) { @@ -946,7 +856,9 @@ int assign_int(void* ptr, const struct struct_map *map, int map_size) printf("%s: Unknown timer type: %s\n", section, value); return 1; } - if(assign_int(&m->timer[idx], _sectimer, MAPSIZE(_sectimer))) + if (assign_int(&m->timer[idx], _sectimer, ARRAYSIZE(_sectimer), name, value)) + return 1; + if (assign_int(&m->timer[idx], _sectimer_rdonly, ARRAYSIZE(_sectimer_rdonly), name, value)) return 1; } if (MATCH_START(section, SECTION_TELEMALARM)) { @@ -991,7 +903,7 @@ int assign_int(void* ptr, const struct struct_map *map, int map_size) #if HAS_DATALOG if (MATCH_SECTION(SECTION_DATALOG)) { if (MATCH_KEY(DATALOG_SWITCH)) { - m->datalog.enable = get_source(section, value); + m->datalog.enable = get_source(value); } else if (MATCH_KEY(DATALOG_RATE)) { for (i = 0; i < DLOG_RATE_LAST; i++) { if(mapstrcasecmp(DATALOG_RateString(i), value) == 0) { @@ -1018,7 +930,7 @@ int assign_int(void* ptr, const struct struct_map *map, int map_size) src = 0; found = 1; } else { - src = get_source(section, name); + src = get_source(name); } if(found || src) { u32 i; @@ -1047,12 +959,14 @@ int assign_int(void* ptr, const struct struct_map *map, int map_size) } return 1; } - if(assign_int(m, _secppm, MAPSIZE(_secppm))) + if (assign_int(m, _secppm, ARRAYSIZE(_secppm), name, value)) + return 1; + if (assign_int(m, _secppm_rdonly, ARRAYSIZE(_secppm_rdonly), name, value)) return 1; if (MATCH_START(name, PPMIN_MAP)) { u8 idx = atoi(name + sizeof(PPMIN_MAP)-1) -1; if (idx < MAX_PPM_IN_CHANNELS) { - m->ppm_map[idx] = get_source(section, value); + m->ppm_map[idx] = get_source(value); if (PPMin_Mode() == PPM_IN_TRAIN1) { m->ppm_map[idx] = (m->ppm_map[idx] <= NUM_INPUTS) ? -1 @@ -1129,36 +1043,6 @@ static void get_model_file(char *file, u8 model_num) sprintf(file, "models/model%d.ini", model_num); } -static void write_int(FILE *fh, void* ptr, const struct struct_map *map, int map_size) -{ - char tmpstr[20]; - for(int i = 0; i < map_size; i++) { - int size = map[i].offset >> 13; - int offset = map[i].offset & 0x1FFF; - int value; - if (map[i].defval == 0xffff) - continue; - switch(size) { - case 0: - case 2: //SRC - case 6: //BUTTON - value = *((u8 *)((long)ptr + offset)); break; - case 1: value = *((u16 *)((long)ptr + offset)); break; - case 3: value = *((u32 *)((long)ptr + offset)); break; - case 4: value = *((s8 *)((long)ptr + offset)); break; - case 5: value = *((s16 *)((long)ptr + offset)); break; - case 7: value = *((s32 *)((long)ptr + offset)); break; - default: continue; - } - if(WRITE_FULL_MODEL || value != map[i].defval) { - if (2 == (size & 0x03)) //2, 6 - fprintf(fh, "%s=%s\n", map[i].str, size == 2 ? INPUT_SourceNameReal(tmpstr, value) : INPUT_ButtonName(value)); - else - fprintf(fh, "%s=%d\n", map[i].str, value); - } - } -} - static u8 write_mixer(FILE *fh, struct Model *m, u8 channel) { int idx; @@ -1172,7 +1056,8 @@ static u8 write_mixer(FILE *fh, struct Model *m, u8 channel) fprintf(fh, "[%s]\n", SECTION_MIXER); fprintf(fh, "%s=%s\n", MIXER_SOURCE, INPUT_SourceNameReal(tmpstr, m->mixers[idx].src)); fprintf(fh, "%s=%s\n", MIXER_DEST, INPUT_SourceNameReal(tmpstr, m->mixers[idx].dest + NUM_INPUTS + 1)); - write_int(fh, &m->mixers[idx], _secmixer, MAPSIZE(_secmixer)); + write_int2(&m->mixers[idx], _secmixer, ARRAYSIZE(_secmixer), + _secmixer_defaults, ARRAYSIZE(_secmixer_defaults), fh); if(WRITE_FULL_MODEL || ! MIXER_APPLY_TRIM(&m->mixers[idx])) fprintf(fh, "%s=%d\n", MIXER_USETRIM, MIXER_APPLY_TRIM(&m->mixers[idx]) ? 1 : 0); if(WRITE_FULL_MODEL || MIXER_MUX(&m->mixers[idx])) @@ -1237,17 +1122,13 @@ u8 CONFIG_WriteModel(u8 model_num) { } CONFIG_EnableLanguage(0); fprintf(fh, "%s=%s\n", MODEL_NAME, m->name); -#if HAS_PERMANENT_TIMER - write_int(fh, m, _secnone, MAPSIZE(_secnone)); -#endif - fprintf(fh, "%s=%s\n", MODEL_MIXERMODE, STDMIXER_ModeName(m->mixer_mode)); + write_int2(m, _secnone, ARRAYSIZE(_secnone), DEFAULTS_ZERO, 0, fh); if(m->icon[0] != 0) fprintf(fh, "%s=%s\n", MODEL_ICON, m->icon + 9); if(WRITE_FULL_MODEL || m->type != 0) fprintf(fh, "%s=%s\n", MODEL_TYPE, MODEL_TYPE_VAL[m->type]); fprintf(fh, "[%s]\n", SECTION_RADIO); - fprintf(fh, "%s=%s\n", RADIO_PROTOCOL, PROTOCOL_GetName(m->protocol)); - write_int(fh, m, _secradio, MAPSIZE(_secradio)); + write_int2(m, _secradio, ARRAYSIZE(_secradio), DEFAULTS_ZERO, 0, fh); fprintf(fh, "%s=%s\n", RADIO_TX_POWER, radio_tx_power_val(m->radio, m->tx_power)); fprintf(fh, "\n"); write_proto_opts(fh, m); @@ -1266,7 +1147,7 @@ u8 CONFIG_WriteModel(u8 model_num) { fprintf(fh, "[%s%d]\n", SECTION_CHANNEL, idx+1); if(WRITE_FULL_MODEL || (m->limits[idx].flags & CH_REVERSE)) fprintf(fh, "%s=%d\n", CHAN_LIMIT_REVERSE, (m->limits[idx].flags & CH_REVERSE) ? 1 : 0); - write_int(fh, &m->limits[idx], _seclimit, MAPSIZE(_seclimit)); + write_int2(&m->limits[idx], _seclimit, ARRAYSIZE(_seclimit), _seclimit_defaults, ARRAYSIZE(_seclimit_defaults), fh); if(WRITE_FULL_MODEL || (m->limits[idx].flags & CH_FAILSAFE_EN)) { if(m->limits[idx].flags & CH_FAILSAFE_EN) { fprintf(fh, "%s=%d\n", CHAN_LIMIT_FAILSAFE, m->limits[idx].failsafe); @@ -1301,7 +1182,7 @@ u8 CONFIG_WriteModel(u8 model_num) { if (PPMin_Mode() != PPM_IN_SOURCE) { fprintf(fh, "%s=%s\n", PPMIN_SWITCH, INPUT_SourceNameReal(file, m->train_sw)); } - write_int(fh, m, _secppm, MAPSIZE(_secppm)); + write_int2(m, _secppm, ARRAYSIZE(_secppm), DEFAULTS_ZERO, 0, fh); //fprintf(fh, "%s=%d\n", PPMIN_CENTERPW, m->ppmin_centerpw); //fprintf(fh, "%s=%d\n", PPMIN_DELTAPW, m->ppmin_deltapw); if (PPMin_Mode() != PPM_IN_SOURCE) { @@ -1322,7 +1203,7 @@ u8 CONFIG_WriteModel(u8 model_num) { m->trims[idx].src >= 1 && m->trims[idx].src <= 4 ? tx_stick_names[m->trims[idx].src-1] : INPUT_SourceNameReal(file, m->trims[idx].src)); - write_int(fh, &m->trims[idx], _sectrim, MAPSIZE(_sectrim)); + write_int2(&m->trims[idx], _sectrim, ARRAYSIZE(_sectrim), _sectrim_defaults, ARRAYSIZE(_sectrim_defaults), fh); if(WRITE_FULL_MODEL || m->trims[idx].sw) fprintf(fh, "%s=%s\n", TRIM_SWITCH, INPUT_SourceNameAbbrevSwitchReal(file, m->trims[idx].sw)); if(WRITE_FULL_MODEL || m->trims[idx].value[0] || m->trims[idx].value[1] || m->trims[idx].value[2] @@ -1333,22 +1214,19 @@ u8 CONFIG_WriteModel(u8 model_num) { } if (WRITE_FULL_MODEL || m->swash_type) { fprintf(fh, "[%s]\n", SECTION_SWASH); - fprintf(fh, "%s=%s\n", SWASH_TYPE, MIXER_SwashType(m->swash_type)); if (WRITE_FULL_MODEL || m->swash_invert & 0x01) fprintf(fh, "%s=1\n", SWASH_ELE_INV); if (WRITE_FULL_MODEL || m->swash_invert & 0x02) fprintf(fh, "%s=1\n", SWASH_AIL_INV); if (WRITE_FULL_MODEL || m->swash_invert & 0x04) fprintf(fh, "%s=1\n", SWASH_COL_INV); - write_int(fh, m, _secswash, MAPSIZE(_secswash)); + write_int2(m, _secswash, ARRAYSIZE(_secswash), _secswash_defaults, ARRAYSIZE(_secswash_defaults), fh); } for(idx = 0; idx < NUM_TIMERS; idx++) { if (! WRITE_FULL_MODEL && m->timer[idx].src == 0 && m->timer[idx].type == TIMER_STOPWATCH) continue; fprintf(fh, "[%s%d]\n", SECTION_TIMER, idx+1); - if (WRITE_FULL_MODEL || m->timer[idx].type != TIMER_STOPWATCH) - fprintf(fh, "%s=%s\n", TIMER_TYPE, TIMER_TYPE_VAL[m->timer[idx].type]); - write_int(fh, &m->timer[idx], _sectimer, MAPSIZE(_sectimer)); + write_int(&m->timer[idx], _sectimer, ARRAYSIZE(_sectimer), fh); if (WRITE_FULL_MODEL || ((m->timer[idx].type == TIMER_COUNTDOWN || m->timer[idx].type == TIMER_COUNTDOWN_PROP) && m->timer[idx].timer)) fprintf(fh, "%s=%d\n", TIMER_TIME, m->timer[idx].timer); if (WRITE_FULL_MODEL || (m->timer[idx].val != 0 && m->timer[idx].type == TIMER_PERMANENT)) @@ -1502,6 +1380,8 @@ u8 CONFIG_ReadModel(u8 model_num) { CONFIG_ReadLayout("layout/default.ini"); if(! PROTOCOL_HasPowerAmp(Model.protocol)) Model.tx_power = TXPOWER_150mW; + Model.radio = PROTOCOL_GetRadio(Model.protocol); + PROTOCOL_Load(1); MIXER_SetMixers(NULL, 0); if(auto_map) RemapChannelsForProtocol(EATRG0); diff --git a/src/config/model.h b/src/config/model.h index 3a2c28bf18..ec9c6f8bd7 100644 --- a/src/config/model.h +++ b/src/config/model.h @@ -20,7 +20,9 @@ extern const char MODEL_NAME[]; extern const char MODEL_ICON[]; extern const char MODEL_TYPE[]; +extern const char MODEL_MIXERMODE[]; extern const char MODEL_TEMPLATE[]; +extern const char MODEL_AUTOMAP[]; #define UNKNOWN_ICON ("media/noicon" IMG_EXT) //This cannot be computed, and must be manually updated diff --git a/src/config/tx.c b/src/config/tx.c index d4cbfe7850..69449105c6 100644 --- a/src/config/tx.c +++ b/src/config/tx.c @@ -15,6 +15,7 @@ #include "common.h" #include "target.h" +#include "config.h" #include "gui/gui.h" #include "tx.h" #include "rtc.h" @@ -87,80 +88,70 @@ static const char TELEM_ALERT_INTERVAL[] ="alertinterval"; #define MATCH_VALUE(s) strcasecmp(value, s) == 0 #define NUM_STR_ELEMS(s) (sizeof(s) / sizeof(char *)) +static const struct struct_map _secmodel[] = +{ + {CURRENT_MODEL, OFFSET(struct Transmitter, current_model)}, + {LANGUAGE, OFFSET(struct Transmitter, language)}, + {MUSIC_SHUTD, OFFSET(struct Transmitter, music_shutdown)}, + {MODE, OFFSET(struct Transmitter, mode)}, + {BRIGHTNESS, OFFSET(struct Transmitter, backlight)}, + {CONTRAST, OFFSET(struct Transmitter, contrast)}, + {VOLUME, OFFSET(struct Transmitter, volume)}, + {VIBRATION, OFFSET(struct Transmitter, vibration_state)}, + {POWER_ALARM, OFFSET(struct Transmitter, power_alarm)}, + {BATT_ALARM, OFFSET(struct Transmitter, batt_alarm)}, + {BATT_CRITICAL, OFFSET(struct Transmitter, batt_critical)}, + {BATT_WARNING_INTERVAL, OFFSET(struct Transmitter, batt_warning_interval)}, + {SPLASH_DELAY, OFFSET(struct Transmitter, splash_delay)}, + +#if HAS_RTC + {TIME_FORMAT, OFFSET(struct Transmitter, rtc_timeformat)}, + {DATE_FORMAT, OFFSET(struct Transmitter, rtc_dateformat)}, +#endif + +#if HAS_EXTENDED_AUDIO + {AUDIO_VOL, OFFSET(struct Transmitter, audio_vol)}, +#endif +}; + +static const struct struct_map _seccalibrate[] = +{ + {CALIBRATE_MAX, OFFSET(struct StickCalibration, max)}, + {CALIBRATE_MIN, OFFSET(struct StickCalibration, min)}, + {CALIBRATE_ZERO, OFFSET(struct StickCalibration, zero)}, +}; + +#if HAS_TOUCH +static const struct struct_map _sectouch[] = +{ + {TOUCH_XSCALE, OFFSET(struct TouchCalibration, xscale)}, + {TOUCH_YSCALE, OFFSET(struct TouchCalibration, yscale)}, + {TOUCH_XOFFSET, OFFSET(struct TouchCalibration, xoffset)}, + {TOUCH_YOFFSET, OFFSET(struct TouchCalibration, yoffset)}, +}; +#endif + +static const struct struct_map _secautodimmer[] = +{ + {AUTODIMMER_TIME, OFFSET(struct AutoDimmer, timer)}, + {AUTODIMMER_DIMVALUE, OFFSET(struct AutoDimmer, backlight_dim_value)}, +}; + +static const struct struct_map _sectimer[] = +{ + {TIMERSETTINGS_PREALERT_TIME, OFFSET(struct CountDownTimerSettings, prealert_time)}, + {TIMERSETTINGS_PREALERT_INTERVAL, OFFSET(struct CountDownTimerSettings, prealert_interval)}, + {TIMERSETTINGS_TIMEUP_INTERVAL, OFFSET(struct CountDownTimerSettings, timeup_interval)}, +}; + static int ini_handler(void* user, const char* section, const char* name, const char* value) { struct Transmitter *t = (struct Transmitter *)user; s32 value_int = atoi(value); if (section[0] == '\0') { - if (MATCH_KEY(CURRENT_MODEL)) { - t->current_model = value_int; - return 1; - } - if (MATCH_KEY(LANGUAGE)) { - t->language = value_int; - return 1; - } - if (MATCH_KEY(MUSIC_SHUTD)) { - t->music_shutdown = atoi(value); - return 1; - } - if (MATCH_KEY(MODE)) { - t->mode = atoi(value); - return 1; - } - if (MATCH_KEY(BRIGHTNESS)) { - t->backlight = atoi(value); - return 1; - } - if (MATCH_KEY(CONTRAST)) { - t->contrast = atoi(value); - return 1; - } - if (MATCH_KEY(VOLUME)) { - t->volume = atoi(value); - return 1; - } -#if HAS_EXTENDED_AUDIO - if (MATCH_KEY(AUDIO_VOL)) { - t->audio_vol = atoi(value); - return 1; - } -#endif - if (MATCH_KEY(VIBRATION)) { - t->vibration_state = atoi(value); - return 1; - } - if (MATCH_KEY(POWER_ALARM)) { - t->power_alarm = atoi(value); - return 1; - } - if (MATCH_KEY(BATT_ALARM)) { - t->batt_alarm = atoi(value); + if (assign_int(t, _secmodel, ARRAYSIZE(_secmodel), name, value)) return 1; - } - if (MATCH_KEY(BATT_CRITICAL)) { - t->batt_critical = atoi(value); - return 1; - } - if (MATCH_KEY(BATT_WARNING_INTERVAL)) { - t->batt_warning_interval = atoi(value); - return 1; - } - if (MATCH_KEY(SPLASH_DELAY)) { - t->splash_delay = atoi(value); - return 1; - } - #if HAS_RTC - if (MATCH_KEY(TIME_FORMAT)) { - t->rtc_timeformat = atoi(value); - return 1; - } - if (MATCH_KEY(DATE_FORMAT)) { - t->rtc_dateformat = atoi(value); - return 1; - } - #endif } if(MATCH_START(section, SECTION_CALIBRATE) && strlen(section) >= sizeof(SECTION_CALIBRATE)) { u8 idx = atoi(section + sizeof(SECTION_CALIBRATE)-1); @@ -173,62 +164,22 @@ static int ini_handler(void* user, const char* section, const char* name, const return 1; } idx--; - if (MATCH_KEY(CALIBRATE_MAX)) { - t->calibration[idx].max = value_int; - return 1; - } - if (MATCH_KEY(CALIBRATE_MIN)) { - t->calibration[idx].min = value_int; - return 1; - } - if (MATCH_KEY(CALIBRATE_ZERO)) { - t->calibration[idx].zero = value_int; + if (assign_int(&t->calibration[idx], _seccalibrate, ARRAYSIZE(_seccalibrate), name, value)) return 1; - } } - if (HAS_TOUCH) { - if (MATCH_SECTION(SECTION_TOUCH)) { - if (MATCH_KEY(TOUCH_XSCALE)) { - t->touch.xscale = value_int; - return 1; - } - if (MATCH_KEY(TOUCH_YSCALE)) { - t->touch.yscale = value_int; - return 1; - } - if (MATCH_KEY(TOUCH_XOFFSET)) { - t->touch.xoffset = value_int; - return 1; - } - if (MATCH_KEY(TOUCH_YOFFSET)) { - t->touch.yoffset = value_int; - return 1; - } - } +#if HAS_TOUCH + if (MATCH_SECTION(SECTION_TOUCH)) { + if (assign_int(&t->touch, _sectouch, ARRAYSIZE(_sectouch), name, value)) + return 1; } +#endif if (MATCH_SECTION(SECTION_AUTODIMMER)) { - if (MATCH_KEY(AUTODIMMER_TIME)) { - t->auto_dimmer.timer = value_int; + if (assign_int(&t->auto_dimmer, _secautodimmer, ARRAYSIZE(_secautodimmer), name, value)) return 1; - } - if (MATCH_KEY(AUTODIMMER_DIMVALUE)) { - t->auto_dimmer.backlight_dim_value = value_int; - return 1; - } } if (MATCH_SECTION(SECTION_TIMERSETTINGS)) { - if (MATCH_KEY(TIMERSETTINGS_PREALERT_TIME)) { - t->countdown_timer_settings.prealert_time = value_int; - return 1; - } - if (MATCH_KEY(TIMERSETTINGS_PREALERT_INTERVAL)) { - t->countdown_timer_settings.prealert_interval = value_int; + if (assign_int(&t->countdown_timer_settings, _sectimer, ARRAYSIZE(_sectimer), name, value)) return 1; - } - if (MATCH_KEY(TIMERSETTINGS_TIMEUP_INTERVAL)) { - t->countdown_timer_settings.timeup_interval = value_int; - return 1; - } } if (MATCH_SECTION(SECTION_TELEMETRY)) { if (MATCH_KEY(TELEM_TEMP)) { @@ -260,54 +211,30 @@ void CONFIG_WriteTx() printf("Couldn't open tx.ini\n"); return; } - CONFIG_EnableLanguage(0); - fprintf(fh, "%s=%d\n", CURRENT_MODEL, Transmitter.current_model); - fprintf(fh, "%s=%d\n", LANGUAGE, Transmitter.language); - fprintf(fh, "%s=%d\n", MUSIC_SHUTD, Transmitter.music_shutdown); - fprintf(fh, "%s=%d\n", MODE, Transmitter.mode); - fprintf(fh, "%s=%d\n", BRIGHTNESS, Transmitter.backlight); - fprintf(fh, "%s=%d\n", CONTRAST, Transmitter.contrast); - fprintf(fh, "%s=%d\n", VOLUME, Transmitter.volume); -#if HAS_EXTENDED_AUDIO - fprintf(fh, "%s=%d\n", AUDIO_VOL, Transmitter.audio_vol); -#endif - fprintf(fh, "%s=%d\n", VIBRATION, Transmitter.vibration_state); - fprintf(fh, "%s=%d\n", POWER_ALARM, Transmitter.power_alarm); - fprintf(fh, "%s=%d\n", BATT_ALARM, Transmitter.batt_alarm); - fprintf(fh, "%s=%d\n", BATT_CRITICAL, Transmitter.batt_critical); - fprintf(fh, "%s=%d\n", BATT_WARNING_INTERVAL, Transmitter.batt_warning_interval); - fprintf(fh, "%s=%d\n", SPLASH_DELAY, Transmitter.splash_delay); -#if HAS_RTC - fprintf(fh, "%s=%d\n", TIME_FORMAT, Transmitter.rtc_timeformat); - fprintf(fh, "%s=%d\n", DATE_FORMAT, Transmitter.rtc_dateformat); -#endif + + write_int(&Transmitter, _secmodel, ARRAYSIZE(_secmodel), fh); + for(i = 0; i < INP_HAS_CALIBRATION; i++) { fprintf(fh, "[%s%d]\n", SECTION_CALIBRATE, i+1); - fprintf(fh, " %s=%d\n", CALIBRATE_MAX, t->calibration[i].max); - fprintf(fh, " %s=%d\n", CALIBRATE_MIN, t->calibration[i].min); - fprintf(fh, " %s=%d\n", CALIBRATE_ZERO, t->calibration[i].zero); - } - if (HAS_TOUCH) { - fprintf(fh, "[%s]\n", SECTION_TOUCH); - fprintf(fh, " %s=%d\n", TOUCH_XSCALE, (int)t->touch.xscale); - fprintf(fh, " %s=%d\n", TOUCH_YSCALE, (int)t->touch.yscale); - fprintf(fh, " %s=%d\n", TOUCH_XOFFSET, (int)t->touch.xoffset); - fprintf(fh, " %s=%d\n", TOUCH_YOFFSET, (int)t->touch.yoffset); + write_int(&t->calibration[i], _seccalibrate, ARRAYSIZE(_seccalibrate), fh); } + +#if HAS_TOUCH + fprintf(fh, "[%s]\n", SECTION_TOUCH); + write_int(&t->touch, _sectouch, ARRAYSIZE(_sectouch), fh); +#endif + fprintf(fh, "[%s]\n", SECTION_AUTODIMMER); - fprintf(fh, "%s=%u\n", AUTODIMMER_TIME, (unsigned int)t->auto_dimmer.timer); - fprintf(fh, "%s=%u\n", AUTODIMMER_DIMVALUE, t->auto_dimmer.backlight_dim_value); + write_int(&t->auto_dimmer, _secautodimmer, ARRAYSIZE(_secautodimmer), fh); + fprintf(fh, "[%s]\n", SECTION_TIMERSETTINGS); - fprintf(fh, "%s=%u\n", TIMERSETTINGS_PREALERT_TIME, (unsigned int)t->countdown_timer_settings.prealert_time); - fprintf(fh, "%s=%u\n", TIMERSETTINGS_PREALERT_INTERVAL, t->countdown_timer_settings.prealert_interval); - fprintf(fh, "%s=%u\n", TIMERSETTINGS_TIMEUP_INTERVAL, t->countdown_timer_settings.timeup_interval); + write_int(&t->countdown_timer_settings, _sectimer, ARRAYSIZE(_sectimer), fh); fprintf(fh, "[%s]\n", SECTION_TELEMETRY); fprintf(fh, "%s=%s\n", TELEM_TEMP, TELEM_TEMP_VAL[(t->telem & TELEMUNIT_FAREN) ? 1 : 0]); fprintf(fh, "%s=%s\n", TELEM_LENGTH, TELEM_LENGTH_VAL[(t->telem & TELEMUNIT_FEET) ? 1 : 0]); fprintf(fh, "%s=%u\n", TELEM_ALERT_INTERVAL, t->telem_alert_interval); - CONFIG_EnableLanguage(1); fclose(fh); } diff --git a/src/target/tx/other/test/old_model.c b/src/target/tx/other/test/old_model.c new file mode 100644 index 0000000000..d1feb298bd --- /dev/null +++ b/src/target/tx/other/test/old_model.c @@ -0,0 +1,1475 @@ +/* + This project is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Deviation is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Deviation. If not, see . + */ + +#include "common.h" +#include "config/model.h" +#include "telemetry.h" +#include "config/tx.h" +#include "music.h" +#include "extended_audio.h" + +#include +#include +extern const u8 EATRG0[PROTO_MAP_LEN]; + +extern struct Model Model; +/*set this to write all model data even if it is the same as the default */ +static u32 crc32; +static const char * const MODEL_TYPE_VAL[MODELTYPE_LAST] = { "heli", "plane", "multi" }; + +#define MATCH_SECTION(s) (strcasecmp(section, s) == 0) +#define MATCH_START(x,y) (strncasecmp(x, y, sizeof(y)-1) == 0) +#define MATCH_KEY(s) (strcasecmp(name, s) == 0) +#define MATCH_VALUE(s) (strcasecmp(value, s) == 0) +#define NUM_STR_ELEMS(s) (sizeof(s) / sizeof(char *)) + +#define WRITE_FULL_MODEL 0 +static u8 auto_map; + +/* Section: Radio */ +static const char SECTION_RADIO[] = "radio"; + +static const char RADIO_PROTOCOL[] = "protocol"; +#if HAS_VIDEO +static const char RADIO_VIDEOSRC[] = "videosrc"; +static const char RADIO_VIDEOCH[] = "videoch"; +static const char RADIO_VIDEOCONTRAST[] = "videocontrast"; +static const char RADIO_VIDEOBRIGHTNESS[] = "videobrightness"; +#endif + +static const char RADIO_NUM_CHANNELS[] = "num_channels"; +static const char RADIO_FIXED_ID[] = "fixed_id"; + +static const char RADIO_TX_POWER[] = "tx_power"; +#if HAS_EXTENDED_TELEMETRY +static const char RADIO_GROUND_LEVEL[] = "ground_level"; +#endif // HAS_EXTENDED_TELEMETRY + +static const char SECTION_PROTO_OPTS[] = "protocol_opts"; +/* Section: Mixer */ +static const char SECTION_MIXER[] = "mixer"; + +static const char MIXER_SOURCE[] = "src"; +static const char MIXER_DEST[] = "dest"; +static const char MIXER_SWITCH[] = "switch"; +static const char MIXER_SCALAR[] = "scalar"; +static const char MIXER_OFFSET[] = "offset"; +static const char MIXER_USETRIM[] = "usetrim"; + +static const char MIXER_MUXTYPE[] = "muxtype"; +static const char * const MIXER_MUXTYPE_VAL[MUX_LAST] = { + "replace", "multiply", "add", "max", "min", "delay", +#if HAS_EXTENDED_AUDIO + "beep","voice", +#endif +}; + +static const char MIXER_CURVETYPE[] = "curvetype"; +static const char * const MIXER_CURVETYPE_VAL[CURVE_MAX+1] = { + "none", "fixed", "min/max", "zero/max", "greater-than-0", "less-than-0", "absval", + "expo", "deadband", "3point", "5point", "7point", "9point", "11point", "13point" }; +static const char MIXER_CURVE_POINTS[] = "points"; +static const char MIXER_CURVE_SMOOTH[] = "smooth"; + +/* Section: Channel */ +static const char SECTION_CHANNEL[] = "channel"; + +static const char CHAN_DISPLAY_FORMAT[] = "display-format"; +static const char CHAN_DISPLAY_SCALE[] = "display-scale"; +static const char CHAN_LIMIT_REVERSE[] = "reverse"; +static const char CHAN_LIMIT_SAFETYSW[] = "safetysw"; +static const char CHAN_LIMIT_SAFETYVAL[] = "safetyval"; +static const char CHAN_LIMIT_FAILSAFE[] = "failsafe"; +static const char CHAN_LIMIT_MAX[] = "max"; +static const char CHAN_LIMIT_MIN[] = "min"; +static const char CHAN_LIMIT_SPEED[] = "speed"; +static const char CHAN_SUBTRIM[] = "subtrim"; +static const char CHAN_SCALAR_NEG[] = "scalar-"; +#define CHAN_SCALAR MIXER_SCALAR +#define CHAN_TEMPLATE MODEL_TEMPLATE +static const char * const CHAN_TEMPLATE_VAL[MIXERTEMPLATE_MAX+1] = + { "none", "simple", "expo_dr", "complex", "cyclic1", "cyclic2", "cyclic3" }; + +/* Section: Virtual Channel */ +static const char SECTION_VIRTCHAN[] = "virtchan"; +#define VCHAN_TEMPLATE CHAN_TEMPLATE +#define VCHAN_TEMPLATE_VAL CHAN_TEMPLATE_VAL +#define VCHAN_NAME MODEL_NAME + +/* Section: PPM-In */ +static const char SECTION_PPMIN[] = "ppm-in"; +static const char PPMIN_MAP[] = "map"; +static const char PPMIN_MODE[] = "mode"; +static const char * const PPMIN_MODE_VALUE[4] = {"none", "channel", "stick", "extend"}; +static const char PPMIN_CENTERPW[] = "centerpw"; +static const char PPMIN_DELTAPW[] = "deltapw"; +#define PPMIN_NUM_CHANNELS RADIO_NUM_CHANNELS +#define PPMIN_SWITCH MIXER_SWITCH + +/* Section: Trim */ +static const char SECTION_TRIM[] = "trim"; + +#define TRIM_SOURCE MIXER_SOURCE +static const char TRIM_POS[] = "pos"; +static const char TRIM_NEG[] = "neg"; +static const char TRIM_STEP[] = "step"; +static const char TRIM_VALUE[] = "value"; +#define TRIM_SWITCH MIXER_SWITCH + +/* Section: Heli */ +static const char SECTION_SWASH[] = "swash"; +#define SWASH_TYPE MODEL_TYPE +static const char SWASH_AIL_INV[] = "ail_inv"; +static const char SWASH_ELE_INV[] = "ele_inv"; +static const char SWASH_COL_INV[] = "col_inv"; +static const char SWASH_AILMIX[] = "ail_mix"; +static const char SWASH_ELEMIX[] = "ele_mix"; +static const char SWASH_COLMIX[] = "col_mix"; + +/* Section: Timer */ +static const char SECTION_TIMER[] = "timer"; + +#define TIMER_SOURCE MIXER_SOURCE +#define TIMER_TYPE MODEL_TYPE +static const char * const TIMER_TYPE_VAL[TIMER_LAST] = { + [TIMER_STOPWATCH] = "stopwatch", + [TIMER_STOPWATCH_PROP] = "stop-prop", + [TIMER_COUNTDOWN] = "countdown", + [TIMER_COUNTDOWN_PROP] = "cntdn-prop", + [TIMER_PERMANENT] = "permanent", + }; +static const char TIMER_TIME[] = "time"; +static const char TIMER_RESETSRC[] = "resetsrc"; +#if HAS_PERMANENT_TIMER +static const char PERMANENT_TIMER[] = "permanent_timer"; +#endif +static const char TIMER_VAL[] = "val"; + +/* Section: Safety */ +static const char SECTION_SAFETY[] = "safety"; +static const char * const SAFETY_VAL[SAFE_MAX+1] = { "none", "min", "zero", "max" }; + +/* Section: Telemetry */ +static const char SECTION_TELEMALARM[] = "telemalarm"; +static const char TELEM_SRC[] = "source"; +static const char TELEM_ABOVE[] = "above"; +static const char TELEM_VALUE[] = "value"; +static const char TELEM_THRESHOLD[] ="threshold"; + +#if HAS_DATALOG +/* Section: Datalog */ +static const char SECTION_DATALOG[] = "datalog"; +static const char DATALOG_RATE[] = "rate"; +#define DATALOG_SWITCH MIXER_SWITCH +#define DATALOG_SOURCE TELEM_SRC +#endif + +/* Section: Gui-QVGA */ +#define STRINGIFY(x) _STRINGIFY(x) +#define _STRINGIFY(x) #x +static const char SECTION_GUI[] = "gui-" STRINGIFY(LCD_WIDTH) "x" STRINGIFY(LCD_HEIGHT); +static const char GUI_QUICKPAGE[] = "quickpage"; + +#if HAS_EXTENDED_AUDIO +/* Section: Music */ +static const char SECTION_VOICE[] = "voice"; +static const char * const VOICE_TELEMALARM[TELEM_NUM_ALARMS] = + { "telemalarm1", "telemalarm2", "telemalarm3", "telemalarm4", "telemalarm5", "telemalarm6" }; +static const char * const VOICE_TIMER[NUM_TIMERS] = + { "timer1", "timer2", "timer3", "timer4" }; + +#endif + + +static s8 mapstrcasecmp(const char *s1, const char *s2) +{ + int i = 0; + while(1) { + if(s1[i] == s2[i] + || (s2[i] >= 'a' && s1[i] + ('a'-'A') == s2[i]) + || (s1[i] >= 'a' && s2[i] + ('a'-'A') == s1[i]) + || (s2[i] == ' ' && s1[i] == '_') + || (s1[i] == ' ' && s2[i] == '_')) + { + if(s1[i] == '\0') + return 0; + i++; + continue; + } + return(s1[i] < s2[i] ? -1 : 1); + } +} +static u8 get_source(const char *section, const char *value) +{ + unsigned i; + unsigned val; + const char *ptr = (value[0] == '!') ? value + 1 : value; + const char *tmp; + char cmp[10]; + for (i = 0; i <= NUM_SOURCES; i++) { + if(mapstrcasecmp(INPUT_SourceNameReal(cmp, i), ptr) == 0) { + #if defined(HAS_SWITCHES_NOSTOCK) && HAS_SWITCHES_NOSTOCK + #define SWITCH_NOSTOCK ((1 << INP_HOLD0) | (1 << INP_HOLD1) | \ + (1 << INP_FMOD0) | (1 << INP_FMOD1)) + if ((Transmitter.ignore_src & SWITCH_NOSTOCK) == SWITCH_NOSTOCK) { + if(mapstrcasecmp("FMODE0", ptr) == 0 || + mapstrcasecmp("FMODE1", ptr) == 0 || + mapstrcasecmp("HOLD0", ptr) == 0 || + mapstrcasecmp("HOLD1", ptr) == 0) + break; + } + #endif //HAS_SWITCHES_NOSTOCK + return ((ptr == value) ? 0 : 0x80) | i; + } + } + for (i = 0; i < 4; i++) { + if(mapstrcasecmp(tx_stick_names[i], ptr) == 0) { + return ((ptr == value) ? 0 : 0x80) | (i + 1); + } + } + i = 0; + while((tmp = INPUT_MapSourceName(i++, &val))) { + if(mapstrcasecmp(tmp, ptr) == 0) { + return ((ptr == value) ? 0 : 0x80) | val; + } + } + printf("%s: Could not parse Source %s\n", section, value); + return 0; +} + +static u8 get_button(const char *section, const char *value) +{ + u8 i; + for (i = 0; i <= NUM_TX_BUTTONS; i++) { + if(strcasecmp(INPUT_ButtonName(i), value) == 0) { + return i; + } + } + printf("%s: Could not parse Button %s\n", section, value); + return 0; +} + +static int handle_proto_opts(struct Model *m, const char* key, const char* value, const char **opts) +{ + const char **popts = opts; + int idx = 0; + while(*popts) { + if(mapstrcasecmp(*popts, key) == 0) { + popts++; + int start = exact_atoi(popts[0]); + int end = exact_atoi(popts[1]); + int is_num = ((start != 0 || end != 0) && (popts[2] == 0 || (popts[3] == 0 && exact_atoi(popts[2]) != 0))) ? 1 : 0; + if(is_num) { + m->proto_opts[idx] = atoi(value); + return 1; + } + int val = 0; + while(popts[val]) { + if(mapstrcasecmp(popts[val], value) == 0) { + m->proto_opts[idx] = val; + return 1; + } + val++; + } + printf("Unknown protocol option '%s' for '%s'\n", value, key); + return 1; + } + //Find end of options + while(*popts) { + popts++; + } + popts++; //Go to next option + idx++; + } + return 0; +} + +enum { + S8, + U8, + S16 +}; +static const char * parse_partial_int_list(const char *ptr, void *vals, int *max_count, int type) +{ + //const char *origptr = ptr; + int value_int = 0; + int idx = 0; + int sign = 0; + + while(1) { + if(*ptr == ',' || *ptr == '\0') { + value_int = value_int * (sign ? -1 : 1); + if (type == S8) { + if (value_int > 127) + value_int = 127; + else if (value_int < -127) + value_int = -127; + ((s8 *)vals)[idx] = value_int; + } else if (type == U8) { + if (value_int > 255) + value_int = 255; + else if (value_int < 0) + value_int = 0; + ((u8 *)vals)[idx] = value_int; + } else { + ((s16 *)vals)[idx] = value_int; + } + sign = 0; + value_int = 0; + idx++; + --*max_count; + if (*max_count == 0 || *ptr == '\0') + return ptr; + } else if(*ptr == '-') { + sign = 1; + } else if (*ptr >= '0' && *ptr <= '9') { + value_int = value_int * 10 + (*ptr - '0'); + } else { + //printf("Bad value '%c' in '%s'\n", *ptr, origptr); + return ptr; + } + ptr++; + } +} + +static int parse_int_list(const char *ptr, void *vals, int max_count, int type) +{ + int count = max_count; + parse_partial_int_list(ptr, vals, &count, type); + return max_count - count; +} + +static void create_element(struct elem *elem, int type, s16 *data) +{ + //int x, int y, int src, int e0, int e1, int e2) + ELEM_SET_X(*elem, data[0]); + ELEM_SET_Y(*elem, data[1]); + ELEM_SET_TYPE(*elem, type); + elem->src = data[5]; + elem->extra[0] = data[2]; + elem->extra[1] = data[3]; + elem->extra[2] = data[4]; +} + +static int layout_ini_handler(void* user, const char* section, const char* name, const char* value) +{ + struct Model *m = (struct Model *)user; + u16 i; + int offset_x = 0, offset_y = 0; + CLOCK_ResetWatchdog(); + int idx; + if (MATCH_START(name, GUI_QUICKPAGE)) { + u8 idx = name[9] - '1'; + if (idx >= NUM_QUICKPAGES) { + printf("%s: Only %d quickpages are supported\n", section, NUM_QUICKPAGES); + return 1; + } + int max = PAGE_GetNumPages(); + for(i = 0; i < max; i++) { + if(mapstrcasecmp(PAGE_GetName(i), value) == 0) { + m->pagecfg2.quickpage[idx] = i; + return 1; + } + } + printf("%s: Unknown page '%s' for quickpage%d\n", section, value, idx+1); + return 1; + } +#ifdef ENABLE_320x240_GUI + static u8 seen_res = 0; + enum { + LOWRES = 1, + HIRES, + }; + if (! MATCH_SECTION(SECTION_GUI)) { + if(MATCH_SECTION("gui-320x240") + && (! ELEM_USED(Model.pagecfg2.elem[0]) || seen_res != HIRES)) + { + seen_res = LOWRES; + offset_x = (LCD_WIDTH - 320) / 2; + offset_y = (LCD_HEIGHT - 240) / 2; + } else + return 1; + } else { + if (seen_res == LOWRES) { + memset(&Model.pagecfg2.elem, 0, sizeof(Model.pagecfg2.elem)); + } + seen_res = HIRES; + } +#else + if (! MATCH_SECTION(SECTION_GUI)) + return 1; +#endif + for (idx = 0; idx < NUM_ELEMS; idx++) { + if (! ELEM_USED(Model.pagecfg2.elem[idx])) + break; + } + + if (idx == NUM_ELEMS) { + printf("No free element available (max = %d)\n", NUM_ELEMS); + return 1; + } + int type; + for (type = 0; type < ELEM_LAST; type++) + if(mapstrcasecmp(name, GetElemName(type)) == 0) + break; + if (type == ELEM_LAST) + return 1; + int count = 5; + s16 data[6] = {0}; + const char *ptr = parse_partial_int_list(value, data, &count, S16); + data[0] += offset_x; + data[1] += offset_y; + if (count > 3) { + printf("Could not parse coordinates from %s=%s\n", name,value); + return 1; + } + switch(type) { + //case ELEM_MODEL: //x, y + case ELEM_VTRIM: //x, y, src + case ELEM_HTRIM: //x, y, src + data[5] = data[2]; + data[2] = 0; + break; + case ELEM_SMALLBOX: //x, y, src + case ELEM_BIGBOX: //x. y. src + { + s16 src = -1; + char str[20]; + if (count != 3) + return 1; +#if HAS_RTC + for(i = 0; i < NUM_RTC; i++) { + if(mapstrcasecmp(ptr, RTC_Name(str, i)) == 0) { + src = i + 1; + break; + } + } +#endif + if (src == -1) { + for(i = 0; i < NUM_TIMERS; i++) { + if(mapstrcasecmp(ptr, TIMER_Name(str, i)) == 0) { + src = i + 1 + NUM_RTC; + break; + } + } + } + if (src == -1) { + for(i = 0; i < NUM_TELEM; i++) { + if(mapstrcasecmp(ptr, TELEMETRY_Name(str, i+1)) == 0) { + src = i + 1 + NUM_RTC + NUM_TIMERS; + break; + } + } + } + if (src == -1) { + u8 newsrc = get_source(section, ptr); + if(newsrc >= NUM_INPUTS) { + src = newsrc - (NUM_INPUTS + 1 - (NUM_RTC + NUM_TIMERS + NUM_TELEM + 1)); + } + } + if (src == -1) + src = 0; + data[5] = src; + break; + } + case ELEM_BAR: //x, y, src + { + if (count != 3) + return 1; + u8 src = get_source(section, ptr); + if (src < NUM_INPUTS) + src = 0; + data[5] = src - NUM_INPUTS; + break; + } + case ELEM_TOGGLE: //x, y, tgl0, tgl1, tgl2, src + { + if(count) + return 1; + for (int j = 0; j <= NUM_SOURCES; j++) { + char cmp[10]; + if(mapstrcasecmp(INPUT_SourceNameAbbrevSwitchReal(cmp, j), ptr+1) == 0) { + data[5] = j; + break; + } + } + break; + } + } + create_element(&m->pagecfg2.elem[idx], type, data); + return 1; +} + +struct struct_map {const char *str; u16 offset; u16 defval;}; +#define MAPSIZE(x) (sizeof(x) / sizeof(struct struct_map)) +#define OFFSET(s,v) (((long)(&s.v) - (long)(&s)) | ((sizeof(s.v)-1) << 13)) +#define OFFSETS(s,v) (((long)(&s.v) - (long)(&s)) | ((sizeof(s.v)+3) << 13)) +#define OFFSET_SRC(s,v) (((long)(&s.v) - (long)(&s)) | (2 << 13)) +#define OFFSET_BUT(s,v) (((long)(&s.v) - (long)(&s)) | (6 << 13)) +#if HAS_PERMANENT_TIMER +static const struct struct_map _secnone[] = +{ + {PERMANENT_TIMER, OFFSET(Model, permanent_timer), 0}, +}; +#endif +static const struct struct_map _secradio[] = { + {RADIO_NUM_CHANNELS, OFFSET(Model, num_channels), 0}, + {RADIO_FIXED_ID, OFFSET(Model, fixed_id), 0}, +#if HAS_VIDEO + {RADIO_VIDEOSRC, OFFSET_SRC(Model, videosrc), 0}, + {RADIO_VIDEOCH, OFFSET(Model, videoch), 0}, + {RADIO_VIDEOCONTRAST,OFFSET(Model, video_contrast), 0}, + {RADIO_VIDEOBRIGHTNESS,OFFSET(Model, video_brightness), 0}, +#endif +#if HAS_EXTENDED_TELEMETRY + {RADIO_GROUND_LEVEL, OFFSET(Model, ground_level), 0}, +#endif +}; +static const struct struct_map _secmixer[] = { + {MIXER_SWITCH, OFFSET_SRC(Model.mixers[0], sw), 0}, + {MIXER_SCALAR, OFFSETS(Model.mixers[0], scalar), 100}, + {MIXER_OFFSET, OFFSETS(Model.mixers[0], offset), 0}, +}; +static const struct struct_map _seclimit[] = { + {CHAN_LIMIT_SAFETYSW, OFFSET_SRC(Model.limits[0], safetysw), 0}, + {CHAN_LIMIT_SAFETYVAL, OFFSETS(Model.limits[0], safetyval), 0}, + {CHAN_LIMIT_MAX, OFFSET(Model.limits[0], max), DEFAULT_SERVO_LIMIT}, + {CHAN_LIMIT_SPEED, OFFSET(Model.limits[0], speed), 0}, + {CHAN_SCALAR, OFFSET(Model.limits[0], servoscale), 100}, + {CHAN_SCALAR_NEG, OFFSET(Model.limits[0], servoscale_neg), 0}, + {CHAN_SUBTRIM, OFFSETS(Model.limits[0], subtrim), 0}, + {CHAN_DISPLAY_SCALE, OFFSETS(Model.limits[0], displayscale), DEFAULT_DISPLAY_SCALE}, +}; +static const struct struct_map _sectrim[] = { + {TRIM_SOURCE, OFFSET_SRC(Model.trims[0], src), 0xFFFF}, + {TRIM_POS, OFFSET_BUT(Model.trims[0], pos), 0}, + {TRIM_NEG, OFFSET_BUT(Model.trims[0], neg), 0}, + {TRIM_STEP, OFFSET(Model.trims[0], step), 1}, +}; +static const struct struct_map _secswash[] = { + {SWASH_AILMIX, OFFSET(Model, swashmix[0]), 60}, + {SWASH_ELEMIX, OFFSET(Model, swashmix[1]), 60}, + {SWASH_COLMIX, OFFSET(Model, swashmix[2]), 60}, +}; +static const struct struct_map _sectimer[] = { + {TIMER_TIME, OFFSET(Model.timer[0], timer), 0xFFFF}, + {TIMER_VAL, OFFSET(Model.timer[0], val), 0xFFFF}, + {TIMER_SOURCE, OFFSET_SRC(Model.timer[0], src), 0}, + {TIMER_RESETSRC, OFFSET_SRC(Model.timer[0], resetsrc), 0}, +}; +static const struct struct_map _secppm[] = { + {PPMIN_CENTERPW, OFFSET(Model, ppmin_centerpw), 0}, + {PPMIN_DELTAPW, OFFSET(Model, ppmin_deltapw), 0}, + {PPMIN_SWITCH, OFFSET_SRC(Model, train_sw), 0xFFFF}, +}; +static int ini_handler(void* user, const char* section, const char* name, const char* value) +{ + int value_int = atoi(value); + struct Model *m = (struct Model *)user; +int assign_int(void* ptr, const struct struct_map *map, int map_size) +{ + for(int i = 0; i < map_size; i++) { + if(MATCH_KEY(map[i].str)) { + int size = map[i].offset >> 13; + int offset = map[i].offset & 0x1FFF; + switch(size) { + case 0: + *((u8 *)((long)ptr + offset)) = value_int; break; + case 1: + *((u16 *)((long)ptr + offset)) = value_int; break; + case 2: + *((u8 *)((long)ptr + offset)) = get_source(section, value); break; + case 3: + *((u32 *)((long)ptr + offset)) = value_int; break; + case 4: + *((s8 *)((long)ptr + offset)) = value_int; break; + case 5: + *((s16 *)((long)ptr + offset)) = value_int; break; + case 6: + *((u8 *)((long)ptr + offset)) = get_button(section, value); break; + case 7: + *((s32 *)((long)ptr + offset)) = value_int; break; + } + return 1; + } + } + return 0; +} + CLOCK_ResetWatchdog(); + unsigned i; + if (MATCH_SECTION("")) { + if(MATCH_KEY(MODEL_NAME)) { + strlcpy(m->name, value, sizeof(m->name)-1); + return 1; + } + if(MATCH_KEY(MODEL_TEMPLATE)) { + //A dummy rule + return 1; + } + if (MATCH_KEY(MODEL_ICON)) { + CONFIG_ParseIconName(m->icon, value); + return 1; + } +#if HAS_PERMANENT_TIMER + if(assign_int(&Model, _secnone, MAPSIZE(_secnone))) + return 1; +#endif + if (MATCH_KEY(MODEL_TYPE)) { + for (i = 0; i < NUM_STR_ELEMS(MODEL_TYPE_VAL); i++) { + if (MATCH_VALUE(MODEL_TYPE_VAL[i])) { + m->type = i; + return 1; + } + } + return 0; + } + if (MATCH_KEY(MODEL_AUTOMAP)) { + auto_map = atoi(value); + return 1; + } + if (MATCH_KEY(MODEL_MIXERMODE)) { + for(i = 1; i < 3; i++) { + if(MATCH_VALUE(STDMIXER_ModeName(i))) + m->mixer_mode = i; + } + return 1; + } + } + if (MATCH_SECTION(SECTION_RADIO)) { + if (MATCH_KEY(RADIO_PROTOCOL)) { + for (i = 0; i < PROTOCOL_COUNT; i++) { + if (MATCH_VALUE(PROTOCOL_GetName(i))) { + m->protocol = i; + m->radio = PROTOCOL_GetRadio(i); + PROTOCOL_Load(1); + return 1; + } + } + printf("Unknown protocol: %s\n", value); + return 1; + } + if(assign_int(&Model, _secradio, MAPSIZE(_secradio))) + return 1; + if (MATCH_KEY(RADIO_TX_POWER)) { + if (m->radio == TX_MODULE_LAST) { + m->tx_power = TXPOWER_150mW; + return 1; + } + for (i = 0; i < RADIO_TX_POWER_COUNT[m->radio]; i++) { + if (MATCH_VALUE(radio_tx_power_val(m->radio, i))) { + m->tx_power = i; + return 1; + } + } + printf("Unknown Tx power: %s\n", value); + m->tx_power = RADIO_TX_POWER_COUNT[m->radio]-1; // default to radio maximum + return 1; + } + printf("Unknown Radio Key: %s\n", name); + return 0; + } + if (MATCH_SECTION(SECTION_PROTO_OPTS)) { + const char **opts = PROTOCOL_GetOptions(); + if (!opts || ! *opts) + return 1; + return handle_proto_opts(m, name, value, opts); + } + if (MATCH_START(section, SECTION_MIXER)) { + int idx; + for (idx = 0; idx < NUM_MIXERS; idx++) { + if(m->mixers[idx].src == 0) + break; + } + if (MATCH_KEY(MIXER_SOURCE)) { + if (idx == NUM_MIXERS) { + printf("%s: Only %d mixers are supported\n", section, NUM_MIXERS); + return 1; + } + m->mixers[idx].src = get_source(section, value); + return 1; + } + idx--; + if (MATCH_KEY(MIXER_DEST)) { + m->mixers[idx].dest = get_source(section, value) - NUM_INPUTS - 1; + return 1; + } + if(assign_int(&m->mixers[idx], _secmixer, MAPSIZE(_secmixer))) + return 1; + if (MATCH_KEY(MIXER_USETRIM)) { + MIXER_SET_APPLY_TRIM(&m->mixers[idx], value_int); + return 1; + } + if (MATCH_KEY(MIXER_MUXTYPE)) { + for (i = 0; i < NUM_STR_ELEMS(MIXER_MUXTYPE_VAL); i++) { + if (MATCH_VALUE(MIXER_MUXTYPE_VAL[i])) { + MIXER_SET_MUX(&m->mixers[idx], i); + return 1; + } + } + printf("%s: Unknown Mux type: %s\n", section, value); + return 1; + } + if (MATCH_KEY(MIXER_CURVETYPE)) { + for (i = 0; i < NUM_STR_ELEMS(MIXER_CURVETYPE_VAL); i++) { + if (MATCH_VALUE(MIXER_CURVETYPE_VAL[i])) { + CURVE_SET_TYPE(&m->mixers[idx].curve, i); + return 1; + } + } + printf("%s: Unknown Curve type: %s\n", section, value); + return 1; + } + if (MATCH_KEY(MIXER_CURVE_POINTS)) { + int count = parse_int_list(value, m->mixers[idx].curve.points, MAX_POINTS, S8); + if (count > MAX_POINTS) { + printf("%s: Too many points (max points = %d\n", section, MAX_POINTS); + return 0; + } + return 1; + } + if (MATCH_KEY(MIXER_CURVE_SMOOTH)) { + CURVE_SET_SMOOTHING(&m->mixers[idx].curve, value_int); + return 1; + } + printf("%s: Couldn't parse key: %s\n", section, name); + return 0; + } + if (MATCH_START(section, SECTION_CHANNEL)) { + u8 idx = atoi(section + sizeof(SECTION_CHANNEL)-1); + if (idx == 0) { + printf("Unknown Channel: %s\n", section); + return 0; + } + if (idx > NUM_OUT_CHANNELS) { + printf("%s: Only %d channels are supported\n", section, NUM_OUT_CHANNELS); + return 1; + } + idx--; + if (MATCH_KEY(CHAN_LIMIT_REVERSE)) { + if (value_int) + m->limits[idx].flags |= CH_REVERSE; + else + m->limits[idx].flags &= ~CH_REVERSE; + return 1; + } + if (MATCH_KEY(CHAN_LIMIT_FAILSAFE)) { + if(strcasecmp("off", value) == 0) { + m->limits[idx].flags &= ~CH_FAILSAFE_EN; + } else { + m->limits[idx].failsafe = value_int; + m->limits[idx].flags |= CH_FAILSAFE_EN; + } + return 1; + } + if (MATCH_KEY(CHAN_DISPLAY_FORMAT)) { + strcpy(m->limits[idx].displayformat, value); + return 1; + } + + if(assign_int(&m->limits[idx], _seclimit, MAPSIZE(_seclimit))) + return 1; + if (MATCH_KEY(CHAN_LIMIT_MIN)) { + m->limits[idx].min = -value_int; + return 1; + } + if (MATCH_KEY(CHAN_TEMPLATE)) { + for (i = 0; i < NUM_STR_ELEMS(CHAN_TEMPLATE_VAL); i++) { + if (MATCH_VALUE(CHAN_TEMPLATE_VAL[i])) { + m->templates[idx] = i; + return 1; + } + } + printf("%s: Unknown template: %s\n", section, value); + return 1; + } + printf("%s: Unknown key: %s\n", section, name); + return 0; + } + if (MATCH_START(section, SECTION_VIRTCHAN)) { + u8 idx = atoi(section + sizeof(SECTION_VIRTCHAN)-1); + if (idx == 0) { + printf("Unknown Virtual Channel: %s\n", section); + return 0; + } + if (idx > NUM_VIRT_CHANNELS) { + printf("%s: Only %d virtual channels are supported\n", section, NUM_VIRT_CHANNELS); + return 1; + } + idx = idx + NUM_OUT_CHANNELS - 1; + if (MATCH_KEY(VCHAN_TEMPLATE)) { + for (i = 0; i < NUM_STR_ELEMS(VCHAN_TEMPLATE_VAL); i++) { + if (MATCH_VALUE(VCHAN_TEMPLATE_VAL[i])) { + m->templates[idx] = i; + return 1; + } + } + printf("%s: Unknown template: %s\n", section, value); + return 1; + } + if (MATCH_KEY(VCHAN_NAME)) { + strlcpy(m->virtname[idx - NUM_OUT_CHANNELS], value, sizeof(m->virtname[0])); + return 1; + } + printf("%s: Unknown key: %s\n", section, name); + return 0; + } + if (MATCH_START(section, SECTION_TRIM)) { + u8 idx = atoi(section + sizeof(SECTION_TRIM)-1); + if (idx == 0) { + printf("Unknown Trim: %s\n", section); + return 0; + } + if (idx > NUM_TRIMS) { + printf("%s: Only %d trims are supported\n", section, NUM_TRIMS); + return 1; + } + idx--; + if(assign_int(&m->trims[idx], _sectrim, MAPSIZE(_sectrim))) + return 1; + if (MATCH_KEY(TRIM_SWITCH)) { + for (int i = 0; i <= NUM_SOURCES; i++) { + char cmp[10]; + if(mapstrcasecmp(INPUT_SourceNameAbbrevSwitchReal(cmp, i), value) == 0) { + m->trims[idx].sw = i; + return 1; + } + } + return 1; + } + if (MATCH_KEY(TRIM_VALUE)) { + parse_int_list(value, m->trims[idx].value, 6, S8); + return 1; + } + printf("%s: Unknown trim setting: %s\n", section, name); + return 0; + } + if (MATCH_SECTION(SECTION_SWASH)) { + if (MATCH_KEY(SWASH_TYPE)) { + for (i = SWASH_TYPE_NONE; i <= SWASH_TYPE_90; i++) { + if(strcasecmp(MIXER_SwashType(i), value) == 0) { + m->swash_type = i; + return 1; + } + } + printf("%s: Unknown swash_type: %s\n", section, value); + return 1; + } + if (MATCH_KEY(SWASH_ELE_INV)) { + if (value_int) + m->swash_invert |= 0x01; + return 1; + } + if (MATCH_KEY(SWASH_AIL_INV)) { + if (value_int) + m->swash_invert |= 0x02; + return 1; + } + if (MATCH_KEY(SWASH_COL_INV)) { + if (value_int) + m->swash_invert |= 0x04; + return 1; + } + if(assign_int(m, _secswash, MAPSIZE(_secswash))) + return 1; + } + if (MATCH_START(section, SECTION_TIMER)) { + u8 idx = atoi(section + sizeof(SECTION_TIMER)-1); + if (idx == 0) { + printf("Unknown Timer: %s\n", section); + return 0; + } + if (idx > NUM_TIMERS) { + printf("%s: Only %d timers are supported\n", section, NUM_TIMERS); + return 1; + } + idx--; + if (MATCH_KEY(TIMER_TYPE)) { + for (i = 0; i < NUM_STR_ELEMS(TIMER_TYPE_VAL); i++) { + if (MATCH_VALUE(TIMER_TYPE_VAL[i])) { + m->timer[idx].type = i; + return 1; + } + } + printf("%s: Unknown timer type: %s\n", section, value); + return 1; + } + if(assign_int(&m->timer[idx], _sectimer, MAPSIZE(_sectimer))) + return 1; + } + if (MATCH_START(section, SECTION_TELEMALARM)) { + u8 idx = atoi(section + sizeof(SECTION_TELEMALARM)-1); + if (idx == 0) { + printf("Unknown Telem-alarm: %s\n", section); + return 0; + } + if (idx > TELEM_NUM_ALARMS) { + printf("%s: Only %d timers are supported\n", section, TELEM_NUM_ALARMS); + return 1; + } + struct TelemetryAlarm *alarm = &Model.alarms[idx - 1]; // idx is 1 based + if (MATCH_KEY(TELEM_SRC)) { + char str[20]; + unsigned last = TELEMETRY_GetNumTelemSrc(); + for(i = 1; i <= last; i++) { + if (strcasecmp(TELEMETRY_ShortName(str, i), value) == 0) { + alarm->src = i; + return 1; + } + } + printf("%s: Unknown telemetry src: %s\n", section, value); + return 0; + } + if (MATCH_KEY(TELEM_ABOVE)) { + if (atoi(value)) + alarm->above = 1; + else + alarm->above = 0; + return 1; + } + if (MATCH_KEY(TELEM_VALUE)) { + alarm->value = atoi(value); + return 1; + } + if (MATCH_KEY(TELEM_THRESHOLD)) { + alarm->threshold = atoi(value); + return 1; + } + } +#if HAS_DATALOG + if (MATCH_SECTION(SECTION_DATALOG)) { + if (MATCH_KEY(DATALOG_SWITCH)) { + m->datalog.enable = get_source(section, value); + } else if (MATCH_KEY(DATALOG_RATE)) { + for (i = 0; i < DLOG_RATE_LAST; i++) { + if(mapstrcasecmp(DATALOG_RateString(i), value) == 0) { + m->datalog.rate = i; + break; + } + } + } else if (MATCH_KEY(DATALOG_SOURCE)) { + char cmp[10]; + for (i = 0; i < DLOG_LAST; i++) { + if(mapstrcasecmp(DATALOG_Source(cmp, i), value) == 0) { + m->datalog.source[DATALOG_BYTE(i)] |= 1 << DATALOG_POS(i); + break; + } + } + } + return 1; + } +#endif //HAS_DATALOG + if (MATCH_START(section, SECTION_SAFETY)) { + int found = 0; + u8 src; + if (MATCH_KEY("auto")) { + src = 0; + found = 1; + } else { + src = get_source(section, name); + } + if(found || src) { + u32 i; + for (i = 0; i < NUM_STR_ELEMS(SAFETY_VAL); i++) { + if (MATCH_VALUE(SAFETY_VAL[i])) { + m->safety[src] = i; + return 1; + } + } + } + } + if (MATCH_START(section, "gui-")) { + return layout_ini_handler(user, section, name, value); + } + if (MATCH_SECTION(SECTION_PPMIN)) { + if (MATCH_KEY(PPMIN_NUM_CHANNELS)) { + m->num_ppmin_channels = atoi(value); + return 1; + } + if (MATCH_KEY(PPMIN_MODE)) { + for(i = 0; i < 4; i++) { + if(mapstrcasecmp(PPMIN_MODE_VALUE[i], value) == 0) { + m->ppmin_mode = i; + return 1; + } + } + return 1; + } + if(assign_int(m, _secppm, MAPSIZE(_secppm))) + return 1; + if (MATCH_START(name, PPMIN_MAP)) { + u8 idx = atoi(name + sizeof(PPMIN_MAP)-1) -1; + if (idx < MAX_PPM_IN_CHANNELS) { + m->ppm_map[idx] = get_source(section, value); + if (PPMin_Mode() == PPM_IN_TRAIN1) { + m->ppm_map[idx] = (m->ppm_map[idx] <= NUM_INPUTS) + ? -1 + : m->ppm_map[idx] - (NUM_INPUTS + 1); + } + } + return 1; + } + } +#if HAS_EXTENDED_AUDIO + char src_name[20]; + + if (MATCH_SECTION(SECTION_VOICE)) { + u16 val = atoi(value); + if(val>MAX_VOICEMAP_ENTRIES-1 || voice_map[val].duration == 0 || val < CUSTOM_ALARM_ID) { + printf("%s: Music %s not found in voice.ini or below ID %d\n", section, value, CUSTOM_ALARM_ID); + return 0; + } + for (int i = INP_HAS_CALIBRATION+1; i <= NUM_INPUTS; i++) { + INPUT_SourceName(src_name, i); + if (MATCH_KEY(src_name)) { + m->voice.switches[i - INP_HAS_CALIBRATION - 1].music = val; + return 1; + } + } +#if NUM_AUX_KNOBS + for (int i = 0; i < NUM_AUX_KNOBS; i++) { + INPUT_SourceName(src_name, i + NUM_STICKS + 1); + strcat(src_name, "_UP"); + if (MATCH_KEY(src_name)) { + m->voice.aux[i * 2 + 1].music = val; + return 1; + } + INPUT_SourceName(src_name, i + NUM_STICKS + 1); + strcat(src_name, "_DOWN"); + if (MATCH_KEY(src_name)) { + m->voice.aux[i * 2].music = val; + return 1; + } + } +#endif + for (int i = 0; i < NUM_TIMERS; i++) { + if (MATCH_KEY(VOICE_TIMER[i])) { + m->voice.timer[i].music = val; + return 1; + } + } + for (int i = 0; i < TELEM_NUM_ALARMS; i++) { + if (MATCH_KEY(VOICE_TELEMALARM[i])) { + m->voice.telemetry[i].music = val; + return 1; + } + } + for (int i = 0; i < (NUM_OUT_CHANNELS + NUM_VIRT_CHANNELS); i++) { + INPUT_SourceNameReal(src_name, i + NUM_INPUTS + 1); + if (MATCH_KEY(src_name)) { + m->voice.mixer[i].music = val; + return 1; + } + } + printf("%s: unknown source name '%s'\n", section, name); + return 0; + } +#endif + printf("Unknown Section: '%s'\n", section); + return 0; +} + +static void get_model_file(char *file, u8 model_num) +{ + if (model_num == 0) + sprintf(file, "models/default.ini"); + else + sprintf(file, "models/model%d.ini", model_num); +} + +static void write_int(FILE *fh, void* ptr, const struct struct_map *map, int map_size) +{ + char tmpstr[20]; + for(int i = 0; i < map_size; i++) { + int size = map[i].offset >> 13; + int offset = map[i].offset & 0x1FFF; + int value; + if (map[i].defval == 0xffff) + continue; + switch(size) { + case 0: + case 2: //SRC + case 6: //BUTTON + value = *((u8 *)((long)ptr + offset)); break; + case 1: value = *((u16 *)((long)ptr + offset)); break; + case 3: value = *((u32 *)((long)ptr + offset)); break; + case 4: value = *((s8 *)((long)ptr + offset)); break; + case 5: value = *((s16 *)((long)ptr + offset)); break; + case 7: value = *((s32 *)((long)ptr + offset)); break; + default: continue; + } + if(WRITE_FULL_MODEL || value != map[i].defval) { + if (2 == (size & 0x03)) //2, 6 + fprintf(fh, "%s=%s\n", map[i].str, size == 2 ? INPUT_SourceNameReal(tmpstr, value) : INPUT_ButtonName(value)); + else + fprintf(fh, "%s=%d\n", map[i].str, value); + } + } +} + +static u8 write_mixer(FILE *fh, struct Model *m, u8 channel) +{ + int idx; + int i; + char tmpstr[20]; + u8 changed = 0; + for(idx = 0; idx < NUM_MIXERS; idx++) { + if (! WRITE_FULL_MODEL && (m->mixers[idx].src == 0 || m->mixers[idx].dest != channel)) + continue; + changed = 1; + fprintf(fh, "[%s]\n", SECTION_MIXER); + fprintf(fh, "%s=%s\n", MIXER_SOURCE, INPUT_SourceNameReal(tmpstr, m->mixers[idx].src)); + fprintf(fh, "%s=%s\n", MIXER_DEST, INPUT_SourceNameReal(tmpstr, m->mixers[idx].dest + NUM_INPUTS + 1)); + write_int(fh, &m->mixers[idx], _secmixer, MAPSIZE(_secmixer)); + if(WRITE_FULL_MODEL || ! MIXER_APPLY_TRIM(&m->mixers[idx])) + fprintf(fh, "%s=%d\n", MIXER_USETRIM, MIXER_APPLY_TRIM(&m->mixers[idx]) ? 1 : 0); + if(WRITE_FULL_MODEL || MIXER_MUX(&m->mixers[idx])) + fprintf(fh, "%s=%s\n", MIXER_MUXTYPE, MIXER_MUXTYPE_VAL[MIXER_MUX(&m->mixers[idx])]); + if(WRITE_FULL_MODEL || CURVE_TYPE(&m->mixers[idx].curve)) { + fprintf(fh, "%s=%s\n", MIXER_CURVETYPE, MIXER_CURVETYPE_VAL[CURVE_TYPE(&m->mixers[idx].curve)]); + u8 num_points = CURVE_NumPoints(&m->mixers[idx].curve); + if (num_points > 0) { + fprintf(fh, "%s=", MIXER_CURVE_POINTS); + for (i = 0; i < num_points; i++) { + fprintf(fh, "%d", m->mixers[idx].curve.points[i]); + if (i != num_points - 1) + fprintf(fh, ","); + } + fprintf(fh, "\n"); + } + if (CURVE_SMOOTHING(&m->mixers[idx].curve)) + fprintf(fh, "%s=%d\n", MIXER_CURVE_SMOOTH, CURVE_SMOOTHING(&m->mixers[idx].curve) ? 1 : 0); + } + } + return changed; +} + +static void write_proto_opts(FILE *fh, struct Model *m) +{ + const char **opts = PROTOCOL_GetOptions(); + if (!opts || ! *opts) // bug fix: must check NULL ptr + return; + int idx = 0; + fprintf(fh, "[%s]\n", SECTION_PROTO_OPTS); + while(*opts) { + int start = exact_atoi(opts[1]); + int end = exact_atoi(opts[2]); + int is_num = ((start != 0 || end != 0) && (opts[3] == 0 || (opts[4] == 0 && exact_atoi(opts[3]) != 0))) ? 1 : 0; + if (is_num) { + fprintf(fh, "%s=%d\n",*opts, m->proto_opts[idx]); + } else { + fprintf(fh, "%s=%s\n",*opts, opts[m->proto_opts[idx]+1]); + } + opts++; + while(*opts) { + opts++; + } + opts++; + idx++; + } + fprintf(fh, "\n"); +} + +u8 CONFIG_WriteModel_old(u8 model_num) { + char file[20]; + FILE *fh; + u8 idx; + struct Model *m = &Model; + + + get_model_file(file, model_num); + fh = fopen(file, "w"); + if (! fh) { + printf("Couldn't open file: %s\n", file); + return 0; + } + CONFIG_EnableLanguage(0); + fprintf(fh, "%s=%s\n", MODEL_NAME, m->name); +#if HAS_PERMANENT_TIMER + write_int(fh, m, _secnone, MAPSIZE(_secnone)); +#endif + fprintf(fh, "%s=%s\n", MODEL_MIXERMODE, STDMIXER_ModeName(m->mixer_mode)); + if(m->icon[0] != 0) + fprintf(fh, "%s=%s\n", MODEL_ICON, m->icon + 9); + if(WRITE_FULL_MODEL || m->type != 0) + fprintf(fh, "%s=%s\n", MODEL_TYPE, MODEL_TYPE_VAL[m->type]); + fprintf(fh, "[%s]\n", SECTION_RADIO); + fprintf(fh, "%s=%s\n", RADIO_PROTOCOL, PROTOCOL_GetName(m->protocol)); + write_int(fh, m, _secradio, MAPSIZE(_secradio)); + fprintf(fh, "%s=%s\n", RADIO_TX_POWER, radio_tx_power_val(m->radio, m->tx_power)); + fprintf(fh, "\n"); + write_proto_opts(fh, m); + struct Limit default_limit; + memset(&default_limit, 0, sizeof(default_limit)); + MIXER_SetDefaultLimit(&default_limit); + for(idx = 0; idx < NUM_OUT_CHANNELS; idx++) { + if(!WRITE_FULL_MODEL + && memcmp(&m->limits[idx], &default_limit, sizeof(default_limit)) == 0 + && m->templates[idx] == 0) + { + if (write_mixer(fh, m, idx)) + fprintf(fh, "\n"); + continue; + } + fprintf(fh, "[%s%d]\n", SECTION_CHANNEL, idx+1); + if(WRITE_FULL_MODEL || (m->limits[idx].flags & CH_REVERSE)) + fprintf(fh, "%s=%d\n", CHAN_LIMIT_REVERSE, (m->limits[idx].flags & CH_REVERSE) ? 1 : 0); + write_int(fh, &m->limits[idx], _seclimit, MAPSIZE(_seclimit)); + if(WRITE_FULL_MODEL || (m->limits[idx].flags & CH_FAILSAFE_EN)) { + if(m->limits[idx].flags & CH_FAILSAFE_EN) { + fprintf(fh, "%s=%d\n", CHAN_LIMIT_FAILSAFE, m->limits[idx].failsafe); + } else { + fprintf(fh, "%s=Off\n", CHAN_LIMIT_FAILSAFE); + } + } + if(WRITE_FULL_MODEL || m->limits[idx].min != DEFAULT_SERVO_LIMIT) + fprintf(fh, "%s=%d\n", CHAN_LIMIT_MIN, -(int)m->limits[idx].min); + if(WRITE_FULL_MODEL || strcmp(m->limits[idx].displayformat, DEFAULT_DISPLAY_FORMAT) != 0) + fprintf(fh, "%s=%s\n", CHAN_DISPLAY_FORMAT, m->limits[idx].displayformat); + if(WRITE_FULL_MODEL || m->templates[idx] != 0) + fprintf(fh, "%s=%s\n", CHAN_TEMPLATE, CHAN_TEMPLATE_VAL[m->templates[idx]]); + write_mixer(fh, m, idx); + fprintf(fh, "\n"); + } + for(idx = 0; idx < NUM_VIRT_CHANNELS; idx++) { + if(WRITE_FULL_MODEL || m->templates[idx+NUM_OUT_CHANNELS] != 0 || m->virtname[idx][0]) { + fprintf(fh, "[%s%d]\n", SECTION_VIRTCHAN, idx+1); + if(m->virtname[idx][0]) + fprintf(fh, "%s=%s\n", VCHAN_NAME, m->virtname[idx]); + if(WRITE_FULL_MODEL || m->templates[idx+NUM_OUT_CHANNELS] != 0) + fprintf(fh, "%s=%s\n", VCHAN_TEMPLATE, VCHAN_TEMPLATE_VAL[m->templates[idx+NUM_OUT_CHANNELS]]); + } + if (write_mixer(fh, m, idx+NUM_OUT_CHANNELS)) + fprintf(fh, "\n"); + } + if (PPMin_Mode()) { + fprintf(fh, "[%s]\n", SECTION_PPMIN); + fprintf(fh, "%s=%s\n", PPMIN_MODE, PPMIN_MODE_VALUE[PPMin_Mode()]); + fprintf(fh, "%s=%d\n", PPMIN_NUM_CHANNELS, m->num_ppmin_channels); + if (PPMin_Mode() != PPM_IN_SOURCE) { + fprintf(fh, "%s=%s\n", PPMIN_SWITCH, INPUT_SourceNameReal(file, m->train_sw)); + } + write_int(fh, m, _secppm, MAPSIZE(_secppm)); + //fprintf(fh, "%s=%d\n", PPMIN_CENTERPW, m->ppmin_centerpw); + //fprintf(fh, "%s=%d\n", PPMIN_DELTAPW, m->ppmin_deltapw); + if (PPMin_Mode() != PPM_IN_SOURCE) { + int offset = (PPMin_Mode() == PPM_IN_TRAIN1) ? NUM_INPUTS + 1: 0; + for(idx = 0; idx < MAX_PPM_IN_CHANNELS; idx++) { + if (m->ppm_map[idx] == -1) + continue; + fprintf(fh, "%s%d=%s\n", PPMIN_MAP, idx + 1, INPUT_SourceNameReal(file, m->ppm_map[idx] + offset)); + } + } + fprintf(fh, "\n"); + } + for(idx = 0; idx < NUM_TRIMS; idx++) { + if (! WRITE_FULL_MODEL && m->trims[idx].src == 0) + continue; + fprintf(fh, "[%s%d]\n", SECTION_TRIM, idx+1); + fprintf(fh, "%s=%s\n", TRIM_SOURCE, + m->trims[idx].src >= 1 && m->trims[idx].src <= 4 + ? tx_stick_names[m->trims[idx].src-1] + : INPUT_SourceNameReal(file, m->trims[idx].src)); + write_int(fh, &m->trims[idx], _sectrim, MAPSIZE(_sectrim)); + if(WRITE_FULL_MODEL || m->trims[idx].sw) + fprintf(fh, "%s=%s\n", TRIM_SWITCH, INPUT_SourceNameAbbrevSwitchReal(file, m->trims[idx].sw)); + if(WRITE_FULL_MODEL || m->trims[idx].value[0] || m->trims[idx].value[1] || m->trims[idx].value[2] + || m->trims[idx].value[3] || m->trims[idx].value[4] || m->trims[idx].value[5]) + fprintf(fh, "%s=%d,%d,%d,%d,%d,%d\n", TRIM_VALUE, + m->trims[idx].value[0], m->trims[idx].value[1], m->trims[idx].value[2], + m->trims[idx].value[3], m->trims[idx].value[4], m->trims[idx].value[5]); + } + if (WRITE_FULL_MODEL || m->swash_type) { + fprintf(fh, "[%s]\n", SECTION_SWASH); + fprintf(fh, "%s=%s\n", SWASH_TYPE, MIXER_SwashType(m->swash_type)); + if (WRITE_FULL_MODEL || m->swash_invert & 0x01) + fprintf(fh, "%s=1\n", SWASH_ELE_INV); + if (WRITE_FULL_MODEL || m->swash_invert & 0x02) + fprintf(fh, "%s=1\n", SWASH_AIL_INV); + if (WRITE_FULL_MODEL || m->swash_invert & 0x04) + fprintf(fh, "%s=1\n", SWASH_COL_INV); + write_int(fh, m, _secswash, MAPSIZE(_secswash)); + } + for(idx = 0; idx < NUM_TIMERS; idx++) { + if (! WRITE_FULL_MODEL && m->timer[idx].src == 0 && m->timer[idx].type == TIMER_STOPWATCH) + continue; + fprintf(fh, "[%s%d]\n", SECTION_TIMER, idx+1); + if (WRITE_FULL_MODEL || m->timer[idx].type != TIMER_STOPWATCH) + fprintf(fh, "%s=%s\n", TIMER_TYPE, TIMER_TYPE_VAL[m->timer[idx].type]); + write_int(fh, &m->timer[idx], _sectimer, MAPSIZE(_sectimer)); + if (WRITE_FULL_MODEL || ((m->timer[idx].type == TIMER_COUNTDOWN || m->timer[idx].type == TIMER_COUNTDOWN_PROP) && m->timer[idx].timer)) + fprintf(fh, "%s=%d\n", TIMER_TIME, m->timer[idx].timer); + if (WRITE_FULL_MODEL || (m->timer[idx].val != 0 && m->timer[idx].type == TIMER_PERMANENT)) + fprintf(fh, "%s=%d\n", TIMER_VAL, m->timer[idx].val); + } + for(idx = 0; idx < TELEM_NUM_ALARMS; idx++) { + struct TelemetryAlarm *alarm = &m->alarms[idx]; + if (!WRITE_FULL_MODEL && !alarm->src) + continue; + fprintf(fh, "[%s%d]\n", SECTION_TELEMALARM, idx+1); + fprintf(fh, "%s=%s\n", TELEM_SRC, TELEMETRY_ShortName(file, alarm->src)); + if (WRITE_FULL_MODEL || alarm->above) + fprintf(fh, "%s=%d\n", TELEM_ABOVE, alarm->above); + fprintf(fh, "%s=%d\n", TELEM_VALUE, alarm->value); + fprintf(fh, "%s=%d\n", TELEM_THRESHOLD, alarm->threshold); + } +#if HAS_DATALOG + fprintf(fh, "[%s]\n", SECTION_DATALOG); + fprintf(fh, "%s=%s\n", DATALOG_SWITCH, INPUT_SourceNameReal(file, m->datalog.enable)); + fprintf(fh, "%s=%s\n", DATALOG_RATE, DATALOG_RateString(m->datalog.rate)); + for(idx = 0; idx < DLOG_LAST; idx++) { + if(m->datalog.source[DATALOG_BYTE(idx)] & (1 << DATALOG_POS(idx))) + fprintf(fh, "%s=%s\n", DATALOG_SOURCE, DATALOG_Source(file, idx)); + } +#endif //HAS_DATALOG + fprintf(fh, "[%s]\n", SECTION_SAFETY); + for(int i = 0; i < NUM_SOURCES + 1; i++) { + if (WRITE_FULL_MODEL || m->safety[i]) { + fprintf(fh, "%s=%s\n", i == 0 ? "Auto" : INPUT_SourceNameReal(file, i), SAFETY_VAL[m->safety[i]]); + } + } + fprintf(fh, "[%s]\n", SECTION_GUI); + for(idx = 0; idx < NUM_ELEMS; idx++) { + if (! ELEM_USED(Model.pagecfg2.elem[idx])) + break; + int src = Model.pagecfg2.elem[idx].src; + int x = ELEM_X(Model.pagecfg2.elem[idx]); + int y = ELEM_Y(Model.pagecfg2.elem[idx]); + int type = ELEM_TYPE(Model.pagecfg2.elem[idx]); + const char *elename = GetElemName(type); + switch(type) { + case ELEM_SMALLBOX: + case ELEM_BIGBOX: + fprintf(fh, "%s=%d,%d,%s\n", elename, x, y, GetBoxSourceReal(file, src)); + break; + case ELEM_BAR: + src += NUM_INPUTS; + fprintf(fh, "%s=%d,%d,%s\n", elename, x, y, INPUT_SourceNameReal(file, src)); + break; + case ELEM_TOGGLE: + fprintf(fh, "%s=%d,%d,%d,%d,%d,%s\n", elename, x, y, + Model.pagecfg2.elem[idx].extra[0], + Model.pagecfg2.elem[idx].extra[1], + INPUT_NumSwitchPos(src) == 2 ? 0 : Model.pagecfg2.elem[idx].extra[2], + INPUT_SourceNameAbbrevSwitchReal(file, src)); + break; + case ELEM_HTRIM: + case ELEM_VTRIM: + fprintf(fh, "%s=%d,%d,%d\n", elename, x, y, src); + break; + default: + fprintf(fh, "%s=%d,%d\n", elename, x, y); + break; + } + } + for(idx = 0; idx < NUM_QUICKPAGES; idx++) { + if (WRITE_FULL_MODEL || m->pagecfg2.quickpage[idx]) { + u8 val = m->pagecfg2.quickpage[idx]; + fprintf(fh, "%s%d=%s\n", GUI_QUICKPAGE, idx+1, PAGE_GetName(val)); + } + } +#if HAS_EXTENDED_AUDIO + fprintf(fh, "[%s]\n", SECTION_VOICE); + for (idx = 0; idx < NUM_SWITCHES; idx++) { + if (m->voice.switches[idx].music) + fprintf(fh, "%s=%d\n", INPUT_SourceName(file,idx + INP_HAS_CALIBRATION + 1), m->voice.switches[idx].music); + } +#if NUM_AUX_KNOBS + for (idx = 0; idx < NUM_AUX_KNOBS * 2; idx++) { + if (m->voice.aux[idx].music) { + if (idx % 2) + fprintf(fh, "%s_UP=%d\n", INPUT_SourceName(tempstring,(idx-1) / 2 + NUM_STICKS + 1), m->voice.aux[idx].music); + else + fprintf(fh, "%s_DOWN=%d\n", INPUT_SourceName(tempstring,idx / 2 + NUM_STICKS + 1), m->voice.aux[idx].music); + } + } +#endif + for (idx = 0; idx < NUM_TIMERS; idx++) { + if (m->voice.timer[idx].music) + fprintf(fh, "timer%d=%d\n", idx + 1, m->voice.timer[idx].music); + } + for (idx = 0; idx < TELEM_NUM_ALARMS; idx++) { + if (m->voice.telemetry[idx].music) + fprintf(fh, "telemalarm%d=%d\n", idx + 1, m->voice.telemetry[idx].music); + } + for (idx = 0; idx < (NUM_OUT_CHANNELS + NUM_VIRT_CHANNELS); idx++) { + if (m->voice.mixer[idx].music) + fprintf(fh, "%s=%d\n", INPUT_SourceNameReal(tempstring, idx + NUM_INPUTS + 1), m->voice.mixer[idx].music); + } +#endif + CONFIG_EnableLanguage(1); + fclose(fh); + return 1; +} + +static void clear_model(u8 full) +{ + u8 i; + if (full) { + memset(&Model, 0, sizeof(Model)); + } else { + memset(Model.mixers, 0, sizeof(Model.mixers)); + memset(Model.templates, 0, sizeof(Model.templates)); + memset(Model.trims, 0, sizeof(Model.trims)); + Model.swash_type = SWASH_TYPE_NONE; + Model.swash_invert = 0; + } + Model.mixer_mode = MIXER_ADVANCED; + Model.swashmix[0] = 60; + Model.swashmix[1] = 60; + Model.swashmix[2] = 60; + for(i = 0; i < NUM_MIXERS; i++) { + Model.mixers[i].scalar = 100; + MIXER_SET_APPLY_TRIM(&Model.mixers[i], 1); + } + for(i = 0; i < NUM_OUT_CHANNELS; i++) { + MIXER_SetDefaultLimit(&Model.limits[i]); + } + for (i = 0; i < NUM_TRIMS; i++) { + Model.trims[i].step = 1; + } + for (i = 0; i < MAX_PPM_IN_CHANNELS; i++) { + Model.ppm_map[i] = -1; + } + Model.ppmin_centerpw = 1500; + Model.ppmin_deltapw = 400; +} + +u8 CONFIG_ReadLayout_old(const char *filename) { + memset(&Model.pagecfg2, 0, sizeof(Model.pagecfg2)); + if (CONFIG_IniParse(filename, layout_ini_handler, &Model)) { + printf("Failed to parse Layout file: %s\n", filename); + return 0; + } + return 1; +} + +u8 CONFIG_ReadModel_old(const char* file) { + clear_model(1); + + auto_map = 0; + if (CONFIG_IniParse(file, ini_handler, &Model)) { + printf("Failed to parse Model file: %s\n", file); + } + if (! ELEM_USED(Model.pagecfg2.elem[0])) + CONFIG_ReadLayout_old("layout/default.ini"); + if(! PROTOCOL_HasPowerAmp(Model.protocol)) + Model.tx_power = TXPOWER_150mW; + MIXER_SetMixers(NULL, 0); + if(auto_map) + RemapChannelsForProtocol(EATRG0); + if(! Model.name[0]) + sprintf(Model.name, "Model%d", 1); + return 1; +} diff --git a/src/tests/models/280qav.ini b/src/tests/models/280qav.ini new file mode 100644 index 0000000000..060ff47855 --- /dev/null +++ b/src/tests/models/280qav.ini @@ -0,0 +1,100 @@ +name=280qav +mixermode=Advanced +type=multi +[radio] +protocol=DSMX +num_channels=7 +tx_power=150mW + +[channel1] +template=simple +[mixer] +src=THR +dest=Ch1 +curvetype=expo +points=0,0 + +[channel2] +reverse=1 +template=simple +[mixer] +src=AIL +dest=Ch2 +curvetype=expo +points=0,0 + +[channel3] +template=simple +[mixer] +src=ELE +dest=Ch3 +curvetype=expo +points=0,0 + +[channel4] +reverse=1 +template=simple +[mixer] +src=RUD +dest=Ch4 +curvetype=expo +points=0,0 + +[channel5] +template=expo_dr +[mixer] +src=AIL +dest=Ch5 +scalar=125 +curvetype=fixed +[mixer] +src=AIL +dest=Ch5 +switch=SW A1 +scalar=50 +curvetype=fixed +[mixer] +src=AIL +dest=Ch5 +switch=SW A2 +scalar=0 +curvetype=fixed + +[channel6] +template=expo_dr +[mixer] +src=SW B1 +dest=Ch6 +scalar=125 +curvetype=fixed +[mixer] +src=SW B1 +dest=Ch6 +switch=SW B1 +scalar=50 +curvetype=fixed +[mixer] +src=SW B1 +dest=Ch6 +switch=SW B0 +scalar=0 +curvetype=fixed + +[safety] +[gui-128x64] +V-trim=59,10,1 +H-trim=5,59,3 +V-trim=65,10,2 +H-trim=74,59,4 +Small-box=2,22,Ch1 +Small-box=2,31,Timer1 +Small-box=2,40,Timer2 +Model=75,20 +Battery=102,1 +Toggle=4,10,0,3,0,None +Toggle=13,10,0,5,0,None +Toggle=22,10,0,4,0,None +Toggle=31,10,0,0,0,None +Toggle=40,10,0,0,0,None +TxPower=102,7 +quickpage1=Telemetry monitor \ No newline at end of file diff --git a/src/tests/models/4g6s.ini b/src/tests/models/4g6s.ini new file mode 100644 index 0000000000..e8207b802c --- /dev/null +++ b/src/tests/models/4g6s.ini @@ -0,0 +1,169 @@ +name=4G6 +mixermode=Advanced +icon=4G6S.BMP +[radio] +protocol=WK2601 +num_channels=7 +tx_power=30mW + +[protocol_opts] +Chan mode=6+1 +COL Inv=Normal +COL Limit=0 + +[channel1] +template=cyclic1 + +[channel2] +reverse=1 +template=cyclic2 + +[channel3] +template=complex +[mixer] +src=THR +dest=Ch3 +usetrim=0 +curvetype=3point +points=-100,48,100 +[mixer] +src=THR +dest=Ch3 +switch=FMODE1 +usetrim=0 +curvetype=3point +points=100,50,100 +[mixer] +src=THR +dest=Ch3 +switch=FMODE2 +usetrim=0 +curvetype=3point +points=100,75,100 +[mixer] +src=THR +dest=Ch3 +switch=GEAR1 +usetrim=0 +curvetype=3point +points=-100,-100,-100 + +[channel4] +reverse=1 +template=simple +[mixer] +src=RUD +dest=Ch4 +usetrim=0 +curvetype=expo +points=0,0 + +[channel6] +reverse=1 +template=cyclic3 + +[channel7] +template=expo_dr +[mixer] +src=Ch7 +dest=Ch7 +scalar=82 +usetrim=0 +curvetype=fixed +[mixer] +src=Ch7 +dest=Ch7 +switch=FMODE1 +scalar=82 +usetrim=0 +curvetype=fixed +[mixer] +src=Ch7 +dest=Ch7 +switch=FMODE2 +scalar=82 +usetrim=0 +curvetype=fixed + +[virtchan1] +template=expo_dr +[mixer] +src=AIL +dest=Virt1 +usetrim=0 +curvetype=expo +points=40,40 + +[virtchan2] +template=expo_dr +[mixer] +src=ELE +dest=Virt2 +usetrim=0 +curvetype=expo +points=40,40 + +[virtchan3] +template=expo_dr +[mixer] +src=THR +dest=Virt3 +usetrim=0 +curvetype=3point +points=-25,0,100 +[mixer] +src=THR +dest=Virt3 +switch=FMODE1 +usetrim=0 +[mixer] +src=THR +dest=Virt3 +switch=FMODE2 +usetrim=0 + +[trim1] +src=LEFT_V +pos=TRIMLV+ +neg=TRIMLV- +value=-100 +[trim2] +src=RIGHT_V +pos=TRIMRV+ +neg=TRIMRV- +[trim3] +src=LEFT_H +pos=TRIMLH+ +neg=TRIMLH- +[trim4] +src=RIGHT_H +pos=TRIMRH+ +neg=TRIMRH- +[swash] +type=120 +ail_inv=1 +[timer2] +type=countdown +time=10 +[safety] +Auto=min +Ch3=min +[gui-qvga] +trim=4in +barsize=half +box1=Ch3 +box2=Timer1 +box3=Timer2 +bar1=Ch1 +bar2=Ch2 +bar3=Ch3 +bar4=Ch4 +toggle1=ELE DR +tglico1=0,1,0 +toggle2=AIL DR +tglico2=0,0,0 +toggle3=RUD DR +tglico3=0,2,0 +toggle4=GEAR +tglico4=0,4,0 +quickpage1=Telemetry monitor \ No newline at end of file diff --git a/src/tests/models/apm.ini b/src/tests/models/apm.ini new file mode 100644 index 0000000000..773e57fadc --- /dev/null +++ b/src/tests/models/apm.ini @@ -0,0 +1,185 @@ +name=APM +mixermode=Advanced +icon=V212.BMP +type=plane +[radio] +protocol=DSM2 +num_channels=8 +fixed_id=636699 +tx_power=100mW + +[protocol_opts] +Telemetry=Off + +[channel1] +max=100 +min=-100 +template=complex +[mixer] +src=THR +dest=Ch1 +switch=RUD DR1 +[mixer] +src=AUX4 +dest=Ch1 +switch=RUD DR0 +scalar=-100 +usetrim=0 +curvetype=fixed + +[channel2] +reverse=1 +template=simple +[mixer] +src=AIL +dest=Ch2 + +[channel3] +reverse=1 +template=simple +[mixer] +src=ELE +dest=Ch3 + +[channel4] +reverse=1 +template=complex +[mixer] +src=RUD +dest=Ch4 +switch=RUD DR1 + +[channel5] +max=100 +min=-100 +template=complex +[mixer] +src=AUX5 +dest=Ch5 +switch=FMODE0 +scalar=-125 +usetrim=0 +curvetype=fixed +[mixer] +src=AIL +dest=Ch5 +switch=FMODE1 +scalar=-40 +usetrim=0 +curvetype=fixed +[mixer] +src=AIL +dest=Ch5 +switch=FMODE2 +scalar=-20 +usetrim=0 +curvetype=fixed +[mixer] +src=AIL +dest=Ch5 +switch=GEAR1 +scalar=10 +usetrim=0 +curvetype=fixed +[mixer] +src=AIL +dest=Ch5 +switch=MIX1 +scalar=40 +usetrim=0 +curvetype=fixed +[mixer] +src=AIL +dest=Ch5 +switch=MIX2 +usetrim=0 +curvetype=fixed +[mixer] +src=AIL +dest=Ch5 +switch=RUD DR0 +scalar=-100 +usetrim=0 +curvetype=fixed + +[channel6] +max=100 +min=-100 +template=complex +[mixer] +src=AUX5 +dest=Ch6 +usetrim=0 +curvetype=expo +points=0,0 + +[channel7] +max=100 +min=-100 +template=complex +[mixer] +src=ELE DR0 +dest=Ch7 +switch=ELE DR0 +scalar=-100 +usetrim=0 +[mixer] +src=AIL +dest=Ch7 +switch=ELE DR1 +usetrim=0 +curvetype=fixed + +[channel8] +max=100 +min=-100 +template=complex +[mixer] +src=AUX4 +dest=Ch8 +usetrim=0 +curvetype=expo +points=0,0 + +[trim1] +src=LEFT_V +pos=TRIMLV+ +neg=TRIMLV- +[trim2] +src=RIGHT_V +pos=TRIMRV+ +neg=TRIMRV- +[trim3] +src=LEFT_H +pos=TRIMLH+ +neg=TRIMLH- +[trim4] +src=RIGHT_H +pos=TRIMRH+ +neg=TRIMRH- +[timer1] +type=countdown +src=Ch1 +resetsrc=AIL DR1 +time=420 +[datalog] +switch=None +rate=1 sec +[safety] +Auto=min +[gui-128x64] +V-trim=59,10,1 +H-trim=5,59,3 +V-trim=65,10,2 +H-trim=74,59,4 +Small-box=2,22,Ch1 +Small-box=2,31,Timer1 +Model=75,20 +Battery=102,1 +Toggle=42,10,72,0,0,RUD DR +Toggle=12,10,0,68,0,ELE DR +Toggle=2,10,0,193,194,FMODE +Toggle=32,10,0,196,197,MIX +Toggle=22,10,0,71,0,GEAR +TxPower=102,7 +quickpage1=Telemetry monitor \ No newline at end of file diff --git a/src/tests/models/ardrone2.ini b/src/tests/models/ardrone2.ini new file mode 100644 index 0000000000..e65157d97d --- /dev/null +++ b/src/tests/models/ardrone2.ini @@ -0,0 +1,108 @@ +name=ArDrone2 +mixermode=Advanced +icon=DRONE2-2.BMP +type=plane +[radio] +protocol=DSM2 +num_channels=7 +fixed_id=123456 +tx_power=150mW + +[protocol_opts] +Telemetry=Off + +[channel1] +template=simple +[mixer] +src=THR +dest=Ch1 + +[channel2] +reverse=1 +template=simple +[mixer] +src=AIL +dest=Ch2 +scalar=45 +curvetype=expo +points=0,0 + +[channel3] +reverse=1 +template=simple +[mixer] +src=ELE +dest=Ch3 +scalar=45 +curvetype=expo +points=0,0 + +[channel4] +reverse=1 +template=simple +[mixer] +src=RUD +dest=Ch4 + +[channel5] +reverse=1 +template=simple +[mixer] +src=GEAR0 +dest=Ch5 +curvetype=min/max +points=0 + +[virtchan1] +name=Virt1 +[trim1] +src=LEFT_V +pos=TRIMLV+ +neg=TRIMLV- +[trim2] +src=RIGHT_V +pos=TRIMRV+ +neg=TRIMRV- +[trim3] +src=LEFT_H +pos=TRIMLH+ +neg=TRIMLH- +[trim4] +src=RIGHT_H +pos=TRIMRH+ +neg=TRIMRH- +[trim5] +src=GEAR0 +pos=None +neg=None +switch=GEAR +[trim6] +src=MIX0 +pos=None +neg=None +[timer1] +type=countdown +src=GEAR1 +time=600 +[timer2] +type=stop-prop +src=THR +[datalog] +switch=None +rate=1 sec +[safety] +Auto=min +[gui-320x240] +V-trim=131,75,1 +H-trim=6,220,3 +V-trim=181,75,2 +H-trim=191,220,4 +Big-box=9,90,Timer1 +Small-box=9,150,Timer2 +Bargraph=205,150,Ch1 +Bargraph=235,150,Ch2 +Bargraph=265,150,Ch3 +Bargraph=295,150,Ch4 +Model=206,40 +Toggle=144,44,68,5,0,GEAR +Big-box=9,37,Ch1 \ No newline at end of file diff --git a/src/tests/models/bixler2.ini b/src/tests/models/bixler2.ini new file mode 100644 index 0000000000..55d514e79f --- /dev/null +++ b/src/tests/models/bixler2.ini @@ -0,0 +1,241 @@ +name=Bixler 2 +mixermode=Advanced +icon=BIXLER2.BMP +type=plane +[radio] +protocol=DEVO +num_channels=10 +fixed_id=870249 +tx_power=150mW + +[protocol_opts] +Telemetry=Off + +[channel1] +subtrim=-165 +template=complex +[mixer] +src=ELE +dest=Ch1 +switch=FMODE0 +scalar=80 +curvetype=expo +points=20,20 +[mixer] +src=ELE +dest=Ch1 +switch=FMODE1 +curvetype=expo +points=20,20 +[mixer] +src=AIL +dest=Ch1 +switch=MIX1 +scalar=20 +muxtype=add +curvetype=fixed +[mixer] +src=AIL +dest=Ch1 +switch=MIX2 +scalar=20 +muxtype=add +curvetype=fixed +[mixer] +src=ELE +dest=Ch1 +switch=FMODE2 +curvetype=expo +points=20,20 +[mixer] +src=ELE +dest=Ch1 +switch=FMODE2 +curvetype=expo +points=20,20 + +[channel2] +template=complex +[mixer] +src=AIL +dest=Ch2 +switch=FMODE0 +scalar=80 +curvetype=expo +points=20,20 +[mixer] +src=AIL +dest=Ch2 +switch=FMODE1 +curvetype=expo +points=20,20 +[mixer] +src=AIL +dest=Ch2 +switch=FMODE2 +curvetype=expo +points=20,20 + +[channel3] +safetysw=RUD DR0 +failsafe=-125 +safetyval=-150 +template=simple +[mixer] +src=THR +dest=Ch3 + +[channel4] +template=complex +[mixer] +src=RUD +dest=Ch4 +switch=FMODE0 +scalar=80 +curvetype=expo +points=20,20 +[mixer] +src=RUD +dest=Ch4 +switch=FMODE1 +curvetype=expo +points=20,20 +[mixer] +src=RUD +dest=Ch4 +switch=FMODE2 +curvetype=expo +points=20,20 +[mixer] +src=AIL +dest=Ch4 +switch=GEAR1 +curvetype=expo +points=20,20 + +[channel5] +template=complex +[mixer] +src=AIL +dest=Ch5 +switch=FMODE0 +scalar=80 +curvetype=expo +points=20,20 +[mixer] +src=AIL +dest=Ch5 +switch=FMODE1 +usetrim=0 +curvetype=expo +points=20,20 +[mixer] +src=AIL +dest=Ch5 +switch=FMODE2 +usetrim=0 +curvetype=expo +points=20,20 + +[channel6] +template=complex +[mixer] +src=ELE +dest=Ch6 +switch=MIX0 +scalar=0 +curvetype=fixed +[mixer] +src=AIL +dest=Ch6 +switch=MIX1 +scalar=40 +usetrim=0 +curvetype=fixed +[mixer] +src=AIL +dest=Ch6 +switch=MIX2 +scalar=60 +usetrim=0 +curvetype=fixed +[mixer] +src=!AIL +dest=Ch6 +switch=FMODE2 +usetrim=0 + +[channel7] +template=complex +[mixer] +src=MIX0 +dest=Ch7 +switch=MIX0 +scalar=0 +curvetype=fixed +[mixer] +src=AIL +dest=Ch7 +switch=MIX1 +scalar=-40 +usetrim=0 +curvetype=fixed +[mixer] +src=AIL +dest=Ch7 +switch=MIX2 +scalar=-60 +usetrim=0 +curvetype=fixed +[mixer] +src=!AIL +dest=Ch7 +switch=FMODE2 +usetrim=0 + +[trim1] +src=LEFT_V +pos=TRIMLV+ +neg=TRIMLV- +[trim2] +src=RIGHT_V +pos=TRIMRV+ +neg=TRIMRV- +step=10 +[trim3] +src=LEFT_H +pos=TRIMLH+ +neg=TRIMLH- +step=10 +[trim4] +src=RIGHT_H +pos=TRIMRH+ +neg=TRIMRH- +step=10 +[timer1] +type=countdown +src=Ch3 +resetsrc=AIL DR1 +time=630 +[timer2] +type=permanent +src=Ch3 +val=2709794 +[datalog] +switch=None +rate=1 sec +[safety] +Auto=min +[gui-128x64] +V-trim=59,10,1 +H-trim=5,59,3 +V-trim=65,10,2 +H-trim=74,59,4 +Small-box=2,22,Ch3 +Small-box=2,31,Timer1 +Small-box=2,39,Timer2 +Model=75,20 +Toggle=2,11,0,193,194,FMODE +Toggle=23,11,0,196,197,MIX +Toggle=42,11,72,0,0,RUD DR +quickpage1=Mixer \ No newline at end of file diff --git a/src/tests/models/blade130x.ini b/src/tests/models/blade130x.ini new file mode 100644 index 0000000000..d4968f48a5 --- /dev/null +++ b/src/tests/models/blade130x.ini @@ -0,0 +1,198 @@ +name=Blade130X +mixermode=Advanced +icon=HELI.BMP +[radio] +protocol=DSMX +num_channels=7 +tx_power=100mW + +[protocol_opts] +Telemetry=Off + +[channel1] +safetysw=RUD DR1 +safetyval=-100 +template=complex +[mixer] +src=THR +dest=Ch1 +curvetype=7point +points=-100,-20,22,41,48,50,50 +[mixer] +src=THR +dest=Ch1 +switch=FMODE1 +curvetype=7point +points=100,87,73,60,73,87,100 +[mixer] +src=THR +dest=Ch1 +switch=FMODE2 +curvetype=fixed + +[channel2] +template=expo_dr +[mixer] +src=AIL +dest=Ch2 +curvetype=expo +points=30,30 +[mixer] +src=AIL +dest=Ch2 +switch=AIL DR1 +scalar=125 +curvetype=expo +points=0,0 + +[channel3] +template=expo_dr +[mixer] +src=ELE +dest=Ch3 +curvetype=expo +points=30,30 +[mixer] +src=ELE +dest=Ch3 +switch=ELE DR1 +scalar=125 +curvetype=expo +points=0,0 + +[channel4] +template=simple +[mixer] +src=RUD +dest=Ch4 +curvetype=expo +points=-6,-6 + +[channel6] +template=complex +[mixer] +src=THR +dest=Ch6 +usetrim=0 +curvetype=7point +points=-40,-26,-13,0,33,66,100 +[mixer] +src=THR +dest=Ch6 +switch=FMODE1 +usetrim=0 +curvetype=7point +points=-100,-66,-33,0,33,66,100 +[mixer] +src=THR +dest=Ch6 +switch=FMODE2 +usetrim=0 +curvetype=7point +points=-100,-66,-33,0,33,66,100 +[mixer] +src=AIL +dest=Ch6 +switch=RUD DR1 +scalar=0 +usetrim=0 +curvetype=fixed + +[trim1] +src=LEFT_V +pos=TRIMLV+ +neg=TRIMLV- +[trim2] +src=RIGHT_V +pos=TRIMRV+ +neg=TRIMRV- +[trim3] +src=LEFT_H +pos=TRIMLH+ +neg=TRIMLH- +[trim4] +src=RIGHT_H +pos=TRIMRH+ +neg=TRIMRH- +[timer1] +type=countdown +[datalog] +switch=None +rate=1 sec +[safety] +Auto=min +[gui-480x272] +V-trim=213,91,1 +H-trim=86,236,3 +V-trim=263,91,2 +H-trim=271,236,4 +Big-box=89,56,Ch1 +Big-box=89,106,Timer1 +Small-box=89,166,Timer2 +Small-box=89,197,Clock +Bargraph=285,166,Ch1 +Bargraph=298,166,Ch2 +Bargraph=312,166,Ch3 +Bargraph=326,166,Ch4 +Bargraph=340,166,Ch5 +Bargraph=354,166,Ch6 +Bargraph=368,166,Ch7 +Bargraph=382,166,Ch8 +Toggle=210,54,1,64,128,FMODE +Toggle=248,54,2,65,129,MIX +Toggle=227,92,8,71,0,GEAR +Toggle=227,131,5,132,68,ELE DR +Toggle=227,169,4,131,67,AIL DR +Toggle=227,208,3,130,66,RUD DR +Model=290,56 + +[gui-320x240] +V-trim=129,75,1 +H-trim=4,220,3 +V-trim=181,75,2 +H-trim=191,220,4 +Big-box=8,40,Ch1 +Small-box=8,95,Timer1 +Small-box=8,133,Timer2 +Small-box=8,172,Timer3 +Bargraph=200,150,Ch1 +Bargraph=214,150,Ch2 +Bargraph=228,150,Ch3 +Bargraph=242,150,Ch4 +Bargraph=256,150,Ch5 +Bargraph=270,150,Ch6 +Bargraph=284,150,Ch7 +Bargraph=298,150,Ch8 +Toggle=144,36,1,64,128,FMODE +Toggle=144,69,2,65,129,MIX +Toggle=144,102,5,68,0,ELE DR +Toggle=144,135,4,67,0,AIL DR +Toggle=144,168,3,66,0,RUD DR +Toggle=144,201,8,71,0,GEAR +Model=207,40 + +[gui-128x64] +V-trim=55,10,1 +H-trim=1,59,3 +V-trim=69,10,2 +H-trim=78,59,4 +Big-box=2,12,Ch1 +Small-box=2,28,Timer1 +Small-box=2,38,Timer2 +Small-box=2,48,Timer3 +Bargraph=79,30,Ch1 +Bargraph=85,30,Ch2 +Bargraph=91,30,Ch3 +Bargraph=97,30,Ch4 +Bargraph=103,30,Ch5 +Bargraph=109,30,Ch6 +Bargraph=115,30,Ch7 +Bargraph=121,30,Ch8 +Toggle=75,13,1,64,128,FMODE +Toggle=84,13,2,65,129,MIX +Toggle=93,13,0,5,0,ELE DR +Toggle=102,13,0,4,0,AIL DR +Toggle=111,13,0,8,0,GEAR +Toggle=120,13,0,3,0,RUD DR +Battery=102,1 +TxPower=75,1 \ No newline at end of file diff --git a/src/tests/models/deltaray.ini b/src/tests/models/deltaray.ini new file mode 100644 index 0000000000..67fe54b38b --- /dev/null +++ b/src/tests/models/deltaray.ini @@ -0,0 +1,116 @@ +name=DeltaRay 1 +mixermode=Advanced +icon=DELTARAY.BMP +type=plane +[radio] +protocol=DSM2 +num_channels=7 +tx_power=150mW + +[protocol_opts] +Telemetry=Off + +[channel1] +template=simple +[mixer] +src=THR +dest=Ch1 + +[channel2] +template=simple +[mixer] +src=AIL +dest=Ch2 +curvetype=expo +points=0,0 + +[channel3] +template=simple +[mixer] +src=ELE +dest=Ch3 +curvetype=expo +points=0,0 + +[channel4] +template=simple +[mixer] +src=RUD +dest=Ch4 +curvetype=expo +points=40,40 + +[channel5] +max=100 +min=-100 +template=expo_dr +[mixer] +src=FMODE0 +dest=Ch5 +curvetype=fixed +[mixer] +src=FMODE0 +dest=Ch5 +switch=FMODE1 +scalar=0 +curvetype=fixed +[mixer] +src=FMODE0 +dest=Ch5 +switch=FMODE2 +scalar=-100 +curvetype=fixed + +[channel6] +reverse=1 +template=expo_dr +[mixer] +src=RUD DR0 +dest=Ch6 +curvetype=fixed +[mixer] +src=RUD DR0 +dest=Ch6 +switch=RUD DR1 +scalar=-100 +curvetype=fixed + +[trim1] +src=LEFT_V +pos=TRIMLV+ +neg=TRIMLV- +value=-18 +[trim2] +src=RIGHT_V +pos=TRIMRV+ +neg=TRIMRV- +[trim3] +src=LEFT_H +pos=TRIMLH+ +neg=TRIMLH- +[trim4] +src=RIGHT_H +pos=TRIMRH+ +neg=TRIMRH- +[timer1] +src=THR +[timer2] +type=countdown +src=THR +time=585 +[safety] +Ch1=min +[gui-qvga] +trim=4in +barsize=half +box1=Timer1 +box2=Timer2 +box3=Timer2 +bar1=Ch1 +bar2=Ch2 +bar3=Ch3 +bar4=Ch4 +toggle1=FMODE +tglico1=3,1,2 +toggle2=RUD DR +tglico2=3,2,0 \ No newline at end of file diff --git a/src/tests/models/fx071.ini b/src/tests/models/fx071.ini new file mode 100644 index 0000000000..9f116f199e --- /dev/null +++ b/src/tests/models/fx071.ini @@ -0,0 +1,219 @@ +name=FX071-Kiwi.Craig +mixermode=Advanced +icon=FX071.BMP +[radio] +protocol=KN +num_channels=10 +fixed_id=123456 +tx_power=100mW + +[protocol_opts] +Re-bind=No +1Mbps=No + +[channel1] +safetysw=RUD DR1 +safetyval=-100 +scalar-=100 +failsafe=-100 +template=simple +[mixer] +src=THR +dest=Ch1 +curvetype=5point +points=-100,-50,0,50,100 +smooth=1 + +[channel2] +scalar-=100 +template=complex +[mixer] +src=AIL +dest=Ch2 +scalar=30 +curvetype=expo +points=10,10 +[mixer] +src=AIL +dest=Ch2 +switch=MIX0 +scalar=5 +muxtype=add +curvetype=expo +points=0,0 +[mixer] +src=AIL +dest=Ch2 +switch=FMODE1 +scalar=55 +curvetype=expo +points=15,15 +[mixer] +src=AIL +dest=Ch2 +switch=MIX0 +scalar=5 +muxtype=add +curvetype=expo +points=0,0 +[mixer] +src=AIL +dest=Ch2 +switch=FMODE2 +scalar=70 +curvetype=expo +points=30,30 +[mixer] +src=AIL +dest=Ch2 +switch=MIX0 +scalar=10 +muxtype=add +curvetype=expo +points=0,0 + +[channel3] +scalar-=100 +template=complex +[mixer] +src=ELE +dest=Ch3 +scalar=30 +curvetype=expo +points=10,10 +[mixer] +src=ELE +dest=Ch3 +switch=MIX0 +scalar=5 +muxtype=add +curvetype=expo +points=0,0 +[mixer] +src=ELE +dest=Ch3 +switch=FMODE1 +scalar=55 +curvetype=expo +points=15,15 +[mixer] +src=ELE +dest=Ch3 +switch=MIX0 +scalar=5 +muxtype=add +curvetype=expo +points=0,0 +[mixer] +src=ELE +dest=Ch3 +switch=FMODE2 +scalar=70 +curvetype=expo +points=30,30 +[mixer] +src=ELE +dest=Ch3 +switch=MIX0 +scalar=10 +muxtype=add +curvetype=expo +points=0,0 + +[channel4] +safetysw=RUD DR1 +safetyval=-100 +failsafe=-100 +template=complex +[mixer] +src=RUD +dest=Ch4 +switch=FMODE0 +scalar=80 +[mixer] +src=RUD +dest=Ch4 +switch=FMODE1 +curvetype=expo +points=10,10 +[mixer] +src=RUD +dest=Ch4 +switch=FMODE2 +scalar=120 +curvetype=expo +points=20,20 + +[channel5] +template=simple +[mixer] +src=GEAR0 +dest=Ch5 +curvetype=expo +points=0,0 + +[channel6] +template=expo_dr +[mixer] +src=RUD DR1 +dest=Ch6 +curvetype=min/max +points=0 + +[channel8] +template=simple +[mixer] +src=MIX1 +dest=Ch8 +curvetype=expo +points=0,0 + +[trim1] +src=LEFT_V +pos=TRIMLV+ +neg=TRIMLV- +step=2 +[trim2] +src=RIGHT_V +pos=TRIMRV+ +neg=TRIMRV- +step=2 +[trim3] +src=LEFT_H +pos=TRIMLH+ +neg=TRIMLH- +step=2 +[trim4] +src=RIGHT_H +pos=TRIMRH+ +neg=TRIMRH- +step=2 +[timer1] +type=countdown +src=THR +resetsrc=AIL DR1 +time=390 +[timer2] +type=countdown +[datalog] +switch=None +rate=1 sec +[safety] +Auto=min +[gui-128x64] +V-trim=59,10,1 +H-trim=5,59,3 +V-trim=65,10,2 +H-trim=74,59,4 +Small-box=2,22,Ch1 +Small-box=2,31,Timer1 +Small-box=2,40,None +Model=75,20 +Battery=102,1 +Toggle=4,10,9,72,0,RUD DR +Toggle=13,10,8,71,0,MIX +Toggle=22,10,2,65,129,FMODE +Toggle=31,10,6,69,0,GEAR +Toggle=40,10,0,0,0,None +TxPower=102,7 +quickpage1=Telemetry monitor \ No newline at end of file diff --git a/src/tests/models/geniuscp.ini b/src/tests/models/geniuscp.ini new file mode 100644 index 0000000000..72e6d85caa --- /dev/null +++ b/src/tests/models/geniuscp.ini @@ -0,0 +1,149 @@ +name=Genius CP V2 +mixermode=Standard +[radio] +protocol=DEVO +num_channels=7 +tx_power=150mW + +[protocol_opts] +Telemetry=On + +[channel1] +template=cyclic2 + +[channel2] +template=cyclic1 + +[channel3] +safetysw=!HOLD0 +failsafe=-125 +safetyval=-110 +template=complex +[mixer] +src=THR +dest=Ch3 +curvetype=9point +points=-100,-50,-4,23,40,58,74,87,100 +[mixer] +src=THR +dest=Ch3 +switch=FMODE1 +curvetype=9point +points=100,84,69,58,50,58,69,84,100 + +[channel4] +template=expo_dr +[mixer] +src=RUD +dest=Ch4 +curvetype=expo +points=0,0 +[mixer] +src=RUD +dest=Ch4 +switch=FMODE1 +curvetype=expo +points=0,0 + +[channel6] +template=cyclic3 + +[channel7] +template=expo_dr +[mixer] +src=FMODE0 +dest=Ch7 +scalar=50 +curvetype=fixed +[mixer] +src=FMODE0 +dest=Ch7 +switch=FMODE1 +scalar=0 +curvetype=fixed + +[virtchan1] +template=expo_dr +[mixer] +src=AIL +dest=Virt1 +scalar=90 +curvetype=expo +points=0,0 +[mixer] +src=AIL +dest=Virt1 +switch=FMODE1 +curvetype=expo +points=0,0 + +[virtchan2] +template=expo_dr +[mixer] +src=ELE +dest=Virt2 +scalar=90 +curvetype=expo +points=0,0 +[mixer] +src=ELE +dest=Virt2 +switch=FMODE1 +curvetype=expo +points=0,0 + +[virtchan3] +template=complex +[mixer] +src=THR +dest=Virt3 +curvetype=9point +points=-20,-10,0,10,20,29,38,47,56 +[mixer] +src=THR +dest=Virt3 +switch=FMODE1 +curvetype=9point +points=-60,-45,-30,-15,0,15,30,45,60 + +[trim1] +src=LEFT_V +pos=TRIMLV+ +neg=TRIMLV- +[trim2] +src=RIGHT_V +pos=TRIMRV+ +neg=TRIMRV- +[trim3] +src=LEFT_H +pos=TRIMLH+ +neg=TRIMLH- +[trim4] +src=RIGHT_H +pos=TRIMRH+ +neg=TRIMRH- +[timer1] +type=countdown +src=Ch3 +time=180 +[timer2] +src=Ch3 +[safety] +Auto=min +[gui-128x64] +V-trim=59,10,1 +H-trim=5,59,3 +V-trim=65,10,2 +H-trim=74,59,4 +Small-box=2,22,Ch3 +Small-box=2,31,Timer1 +Small-box=2,39,Timer2 +Model=75,20 +Battery=102,1 +Toggle=4,10,0,3,0,None +Toggle=13,10,0,5,0,None +Toggle=22,10,0,4,0,None +Toggle=31,10,0,0,0,None +Toggle=40,10,0,0,0,None +TxPower=102,7 +quickpage1=Telemetry monitor \ No newline at end of file diff --git a/src/tests/models/nazath.ini b/src/tests/models/nazath.ini new file mode 100644 index 0000000000..4424cd4098 --- /dev/null +++ b/src/tests/models/nazath.ini @@ -0,0 +1,135 @@ +name=Naza TH +mixermode=Advanced +icon=HUBSANX4.BMP +[radio] +protocol=DEVO +num_channels=10 +fixed_id=611642 +tx_power=150mW + +[protocol_opts] +Telemetry=Off + +[channel1] +template=simple +[mixer] +src=ELE +dest=Ch1 +curvetype=expo +points=0,0 + +[channel2] +template=simple +[mixer] +src=AIL +dest=Ch2 +curvetype=expo +points=0,0 + +[channel3] +template=simple +[mixer] +src=THR +dest=Ch3 +curvetype=expo +points=0,0 + +[channel4] +template=simple +[mixer] +src=RUD +dest=Ch4 +curvetype=expo +points=0,0 + +[channel5] +template=expo_dr +[mixer] +src=MIX0 +dest=Ch5 +scalar=95 +curvetype=absval +points=0 +[mixer] +src=MIX0 +dest=Ch5 +switch=MIX1 +scalar=5 +curvetype=absval +points=0 +[mixer] +src=MIX0 +dest=Ch5 +switch=MIX2 +scalar=-80 +curvetype=absval +points=0 + +[channel6] +template=simple +[mixer] +src=PPM5 +dest=Ch6 +curvetype=expo +points=0,0 + +[channel7] +template=simple +[mixer] +src=PPM7 +dest=Ch7 +curvetype=expo +points=0,0 + +[channel8] +template=simple +[mixer] +src=PPM8 +dest=Ch8 +curvetype=expo +points=0,0 + +[ppm-in] +mode=extend +num_channels=8 +centerpw=1500 +deltapw=400 + +[trim1] +src=LEFT_V +pos=TRIMLV+ +neg=TRIMLV- +[trim2] +src=RIGHT_V +pos=TRIMRV+ +neg=TRIMRV- +[trim3] +src=LEFT_H +pos=TRIMLH+ +neg=TRIMLH- +[trim4] +src=RIGHT_H +pos=TRIMRH+ +neg=TRIMRH- +[datalog] +switch=None +rate=1 sec +[safety] +Auto=min +[gui-128x64] +V-trim=59,10,1 +H-trim=5,59,3 +V-trim=65,10,2 +H-trim=74,59,4 +Small-box=2,22,Ch3 +Small-box=2,31,Timer1 +Small-box=2,39,Timer2 +Model=75,20 +Battery=102,1 +Toggle=4,10,0,3,0,RUD DR +Toggle=13,10,0,5,0,ELE DR +Toggle=22,10,0,4,0,AIL DR +Toggle=31,10,0,0,0,None +Toggle=40,10,0,0,0,None +TxPower=102,7 +quickpage1=Telemetry monitor \ No newline at end of file diff --git a/src/tests/models/trex150dfc.ini b/src/tests/models/trex150dfc.ini new file mode 100644 index 0000000000..37842b8cb3 --- /dev/null +++ b/src/tests/models/trex150dfc.ini @@ -0,0 +1,242 @@ +name=TREX150DFC +mixermode=Advanced +icon=HELI.BMP +[radio] +protocol=DSMX +num_channels=7 +tx_power=100mW + +[protocol_opts] +Telemetry=Off + +[channel1] +safetysw=RUD DR1 +safetyval=-100 +template=complex +[mixer] +src=THR +dest=Ch1 +curvetype=7point +points=-100,-40,0,8,13,17,20 +[mixer] +src=THR +dest=Ch1 +switch=FMODE1 +curvetype=7point +points=40,36,33,28,33,36,40 +[mixer] +src=THR +dest=Ch1 +switch=FMODE2 +curvetype=7point +points=60,55,50,46,50,55,60 + +[channel2] +reverse=1 +template=expo_dr +[mixer] +src=AIL +dest=Ch2 +scalar=50 +curvetype=expo +points=30,30 +[mixer] +src=AIL +dest=Ch2 +switch=FMODE1 +scalar=70 +curvetype=expo +points=30,30 +[mixer] +src=AIL +dest=Ch2 +switch=FMODE2 +scalar=125 + +[channel3] +reverse=1 +template=expo_dr +[mixer] +src=ELE +dest=Ch3 +scalar=50 +curvetype=expo +points=30,30 +[mixer] +src=ELE +dest=Ch3 +switch=FMODE1 +scalar=70 +curvetype=expo +points=30,30 +[mixer] +src=ELE +dest=Ch3 +switch=FMODE2 +scalar=125 + +[channel4] +reverse=1 +template=expo_dr +[mixer] +src=RUD +dest=Ch4 +curvetype=expo +points=15,15 +[mixer] +src=RUD +dest=Ch4 +switch=FMODE1 +curvetype=expo +points=15,15 +[mixer] +src=RUD +dest=Ch4 +switch=FMODE2 + +[channel5] +template=complex +[mixer] +src=AIL +dest=Ch5 +switch=MIX0 +scalar=30 +curvetype=fixed +[mixer] +src=AIL +dest=Ch5 +switch=MIX1 +scalar=40 +curvetype=fixed +[mixer] +src=AIL +dest=Ch5 +switch=MIX2 +scalar=50 +curvetype=fixed + +[channel6] +reverse=1 +template=complex +[mixer] +src=THR +dest=Ch6 +scalar=60 +usetrim=0 +curvetype=7point +points=0,0,0,0,33,66,100 +[mixer] +src=THR +dest=Ch6 +switch=FMODE1 +scalar=70 +usetrim=0 +curvetype=7point +points=-30,-20,-10,0,33,66,100 +[mixer] +src=THR +dest=Ch6 +switch=FMODE2 +scalar=75 +usetrim=0 +curvetype=7point +points=-100,-66,-33,0,33,66,100 + +[trim1] +src=LEFT_V +pos=TRIMLV+ +neg=TRIMLV- +[trim2] +src=RIGHT_V +pos=TRIMRV+ +neg=TRIMRV- +[trim3] +src=LEFT_H +pos=TRIMLH+ +neg=TRIMLH- +[trim4] +src=RIGHT_H +pos=TRIMRH+ +neg=TRIMRH- +[timer1] +type=countdown +[datalog] +switch=None +rate=1 sec +[safety] +Auto=min +[gui-480x272] +V-trim=213,91,1 +H-trim=86,236,3 +V-trim=263,91,2 +H-trim=271,236,4 +Big-box=89,56,Ch1 +Big-box=89,106,Timer1 +Small-box=89,166,Timer2 +Small-box=89,197,Clock +Bargraph=285,166,Ch1 +Bargraph=298,166,Ch2 +Bargraph=312,166,Ch3 +Bargraph=326,166,Ch4 +Bargraph=340,166,Ch5 +Bargraph=354,166,Ch6 +Bargraph=368,166,Ch7 +Bargraph=382,166,Ch8 +Toggle=210,54,1,64,128,FMODE +Toggle=248,54,2,65,129,MIX +Toggle=227,92,8,71,0,GEAR +Toggle=227,131,5,132,68,ELE DR +Toggle=227,169,4,131,67,AIL DR +Toggle=227,208,3,130,66,RUD DR +Model=290,56 + +[gui-320x240] +V-trim=129,75,1 +H-trim=4,220,3 +V-trim=181,75,2 +H-trim=191,220,4 +Big-box=8,40,Ch1 +Small-box=8,95,Timer1 +Small-box=8,133,Timer2 +Small-box=8,172,Timer3 +Bargraph=200,150,Ch1 +Bargraph=214,150,Ch2 +Bargraph=228,150,Ch3 +Bargraph=242,150,Ch4 +Bargraph=256,150,Ch5 +Bargraph=270,150,Ch6 +Bargraph=284,150,Ch7 +Bargraph=298,150,Ch8 +Toggle=144,36,1,64,128,FMODE +Toggle=144,69,2,65,129,MIX +Toggle=144,102,5,68,0,ELE DR +Toggle=144,135,4,67,0,AIL DR +Toggle=144,168,3,66,0,RUD DR +Toggle=144,201,8,71,0,GEAR +Model=207,40 + +[gui-128x64] +V-trim=55,10,1 +H-trim=1,59,3 +V-trim=69,10,2 +H-trim=78,59,4 +Big-box=2,12,Ch1 +Small-box=2,28,Timer1 +Small-box=2,38,Timer2 +Small-box=2,48,Timer3 +Bargraph=79,30,Ch1 +Bargraph=85,30,Ch2 +Bargraph=91,30,Ch3 +Bargraph=97,30,Ch4 +Bargraph=103,30,Ch5 +Bargraph=109,30,Ch6 +Bargraph=115,30,Ch7 +Bargraph=121,30,Ch8 +Toggle=75,13,1,64,128,FMODE +Toggle=84,13,2,65,129,MIX +Toggle=93,13,0,5,0,ELE DR +Toggle=102,13,0,4,0,AIL DR +Toggle=111,13,0,8,0,GEAR +Toggle=120,13,0,3,0,RUD DR +Battery=102,1 +TxPower=75,1 \ No newline at end of file diff --git a/src/tests/models/wltoys931.ini b/src/tests/models/wltoys931.ini new file mode 100644 index 0000000000..26520b706c --- /dev/null +++ b/src/tests/models/wltoys931.ini @@ -0,0 +1,206 @@ +name=V931 +mixermode=Advanced +icon=V931.BMP +type=plane +[radio] +protocol=KN +num_channels=11 +tx_power=100mW + +[protocol_opts] +Re-bind=No +1Mbps=Yes +Format=WLToys + +[channel1] +template=complex +[mixer] +src=THR +dest=Ch1 +switch=FMODE1 +scalar=95 +[mixer] +src=THR +dest=Ch1 +switch=!FMODE1 +usetrim=0 + +[channel2] +template=expo_dr +[mixer] +src=AIL +dest=Ch2 +scalar=60 +curvetype=expo +points=50,50 +[mixer] +src=AIL +dest=Ch2 +switch=MIX1 +scalar=80 +curvetype=expo +points=25,25 +[mixer] +src=AIL +dest=Ch2 +switch=MIX2 +curvetype=expo +points=0,0 + +[channel3] +template=expo_dr +[mixer] +src=ELE +dest=Ch3 +scalar=60 +curvetype=expo +points=50,50 +[mixer] +src=ELE +dest=Ch3 +switch=MIX1 +scalar=80 +curvetype=expo +points=25,25 +[mixer] +src=ELE +dest=Ch3 +switch=MIX2 +curvetype=expo +points=0,0 + +[channel4] +template=simple +[mixer] +src=RUD +dest=Ch4 + +[channel5] +template=simple +[mixer] +src=!AIL DR0 +dest=Ch5 +curvetype=min/max +points=0 + +[channel6] +template=simple +[mixer] +src=RUD DR1 +dest=Ch6 +curvetype=min/max +points=0 + +[channel7] +template=complex +[mixer] +src=FMODE0 +dest=Ch7 +switch=FMODE0 +scalar=-100 +usetrim=0 +curvetype=fixed +[mixer] +src=AIL +dest=Ch7 +switch=!FMODE0 +usetrim=0 +curvetype=fixed + +[channel8] +template=simple +[mixer] +src=!GEAR0 +dest=Ch8 +curvetype=min/max +points=0 + +[channel9] +template=simple + +[channel10] +template=simple + +[channel11] +template=simple + +[virtchan1] +template=simple +[mixer] +src=Ch1 +dest=Virt1 +scalar=50 +offset=50 + +[virtchan2] +template=complex +[mixer] +src=THR +dest=Virt2 +curvetype=expo +points=0,0 +[mixer] +src=THR +dest=Virt2 +switch=RUD DR1 +scalar=-100 +curvetype=fixed + +[trim1] +src=Ch9 +pos=TRIMLV+ +neg=TRIMLV- +step=10 +switch=GEAR +value=52,-18,0 +[trim2] +src=Ch11 +pos=TRIMRV+ +neg=TRIMRV- +step=10 +switch=GEAR +value=26,0,0 +[trim3] +src=LEFT_H +pos=TRIMLH+ +neg=TRIMLH- +switch=GEAR +[trim4] +src=Ch10 +pos=TRIMRH+ +neg=TRIMRH- +step=10 +switch=GEAR +value=33,0,0 +[timer1] +src=Virt2 +[timer2] +type=permanent +src=Virt2 +val=1733598 +[datalog] +switch=None +rate=1 sec +[safety] +Auto=min +[gui-320x240] +V-trim=133,75,1 +H-trim=6,220,3 +V-trim=183,75,2 +H-trim=191,220,4 +Big-box=9,40,Ch1 +Big-box=9,90,Timer1 +Small-box=9,140,Timer2 +Bargraph=205,150,Ch2 +Bargraph=235,150,Ch3 +Bargraph=265,150,Ch1 +Bargraph=295,150,Ch4 +Model=206,40 +Toggle=130,38,1,64,128,None +Toggle=168,38,2,65,129,None +Toggle=147,76,0,66,0,RUD DR +Toggle=147,113,0,67,0,AIL DR +Toggle=147,153,0,68,0,ELE DR +Toggle=147,192,8,71,0,None +Big-box=9,177,Virt1 +quickpage1=Telemetry monitor \ No newline at end of file diff --git a/src/tests/models/yacht.ini b/src/tests/models/yacht.ini new file mode 100644 index 0000000000..385779e5d2 --- /dev/null +++ b/src/tests/models/yacht.ini @@ -0,0 +1,81 @@ +name=Joysway Orion +mixermode=Advanced +icon=YACHT.BMP +type=multi +[radio] +protocol=DSM2 +num_channels=2 +fixed_id=859231 +tx_power=100mW + +[protocol_opts] +Telemetry=On + +[channel1] +reverse=1 +template=complex +[mixer] +src=THR +dest=Ch1 +[mixer] +src=AIL +dest=Ch1 +switch=AIL DR1 +scalar=20 +usetrim=0 +muxtype=add +curvetype=fixed + +[channel2] +template=complex +[mixer] +src=RUD +dest=Ch2 +[mixer] +src=AIL +dest=Ch2 +usetrim=0 +muxtype=add + +[trim1] +src=LEFT_V +pos=TRIMLV+ +neg=TRIMLV- +[trim2] +src=RIGHT_V +pos=TRIMRV+ +neg=TRIMRV- +[trim3] +src=LEFT_H +pos=TRIMLH+ +neg=TRIMLH- +value=1,0,0 +[trim4] +src=RIGHT_H +pos=TRIMRH+ +neg=TRIMRH- +[timer1] +src=Ch3 +resetsrc=ELE DR1 +[timer2] +type=permanent +src=Ch3 +val=19187434 +[datalog] +switch=None +rate=1 sec +[safety] +Auto=min +[gui-128x64] +V-trim=59,10,1 +H-trim=5,59,3 +Small-box=2,35,Timer1 +Small-box=2,45,Timer2 +Model=75,20 +Battery=102,1 +Toggle=22,10 +Switch=198,AIL DR1 +TxPower=102,7 +Big-box=2,21,Volt1 +quickpage1=Channel monitor +quickpage2=Telemetry monitor \ No newline at end of file diff --git a/src/tests/test_config.c b/src/tests/test_config.c new file mode 100644 index 0000000000..a3a3224fd8 --- /dev/null +++ b/src/tests/test_config.c @@ -0,0 +1,110 @@ +#include "CuTest.h" + +struct config_values { + u8 u8_val; + u16 u16_val; + u32 u32_val; + + s8 s8_val; + s16 s16_val; + s32 s32_val; + + u16 color_val; + u8 str_index_val; + u8 font_val; + + u8 mixer; +} TestConfig; + +enum { + STR_NONE = 0, + STR_CENTER, + STR_LEFT, + STR_RIGHT +}; + +static const char * const ALIGN_VAL[] = { + [STR_CENTER] = "center", + [STR_LEFT] = "left", + [STR_RIGHT] = "right", + }; + +static const char *string_values(int i) { + if (i == 0) + return "standard"; + else + return "advanced"; +} + +static const struct struct_map _secgeneral[] = +{ + {"u8val", OFFSET(struct config_values, u8_val)}, + {"u16val", OFFSET(struct config_values, u16_val)}, + {"u32val", OFFSET(struct config_values, u32_val)}, + {"s8val", OFFSETS(struct config_values, s8_val)}, + {"s16val", OFFSETS(struct config_values, s16_val)}, + {"s32val", OFFSETS(struct config_values, s32_val)}, + {"index", OFFSET_STRLIST(struct config_values, str_index_val, ALIGN_VAL, ARRAYSIZE(ALIGN_VAL))}, + {"color", OFFSET_COL(struct config_values, color_val)}, + {"font", OFFSET_FON(struct config_values, font_val)}, + {"mixer", OFFSET_STRCALL(struct config_values, mixer, string_values, 2)}, +}; + +void TestConfigBasic(CuTest* t) { + memset(&TestConfig, 0, sizeof(TestConfig)); + + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "u8val", "5")); + CuAssertIntEquals(t, 5, TestConfig.u8_val); + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "s8val", "-5")); + CuAssertIntEquals(t, -5, TestConfig.s8_val); + + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "u16val", "5000")); + CuAssertIntEquals(t, 5000, TestConfig.u16_val); + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "s16val", "-6005")); + CuAssertIntEquals(t, -6005, TestConfig.s16_val); + + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "u32val", "755350")); + CuAssertIntEquals(t, 755350, TestConfig.u32_val); + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "s32val", "-655350")); + CuAssertIntEquals(t, -655350, TestConfig.s32_val); + + CuAssertTrue(t, !assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "notfound", "0")); + + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "color", "000000")); + CuAssertIntEquals(t, 0, TestConfig.color_val); + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "color", "FEDCBA")); + CuAssertIntEquals(t, 0xfef7, TestConfig.color_val); +} + +void TestConfigStringList(CuTest* t) +{ + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "index", "center")); + CuAssertIntEquals(t, STR_CENTER, TestConfig.str_index_val); + + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "index", "left")); + CuAssertIntEquals(t, STR_LEFT, TestConfig.str_index_val); + + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "index", "right")); + CuAssertIntEquals(t, STR_RIGHT, TestConfig.str_index_val); + + TestConfig.str_index_val = 254; + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "index", "invalid")); + CuAssertIntEquals(t, 254, TestConfig.str_index_val); +} + +void TestConfigFont(CuTest* t) +{ + int fontindex = FONT_GetFromString("font1"); + + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "font", "font1")); + CuAssertIntEquals(t, fontindex, TestConfig.font_val); +} + +void TestConfigStringCallback(CuTest* t) +{ + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "mixer", "standard")); + CuAssertIntEquals(t, 0, TestConfig.mixer); + + CuAssertTrue(t, assign_int(&TestConfig, _secgeneral, ARRAYSIZE(_secgeneral), "mixer", "advanced")); + CuAssertIntEquals(t, 1, TestConfig.mixer); +} diff --git a/src/tests/test_model.c b/src/tests/test_model.c index f5884c35db..a4ec040959 100644 --- a/src/tests/test_model.c +++ b/src/tests/test_model.c @@ -1,5 +1,72 @@ #include "CuTest.h" +extern u8 CONFIG_ReadModel_old(const char* file); +extern u8 CONFIG_WriteModel_old(u8 model_num); + +u8 CONFIG_ReadModel_new(const char* file) { + clear_model(1); + + auto_map = 0; + if (CONFIG_IniParse(file, ini_handler, &Model)) { + printf("Failed to parse Model file: %s\n", file); + } + if (! ELEM_USED(Model.pagecfg2.elem[0])) + CONFIG_ReadLayout("layout/default.ini"); + if(! PROTOCOL_HasPowerAmp(Model.protocol)) + Model.tx_power = TXPOWER_150mW; + Model.radio = PROTOCOL_GetRadio(Model.protocol); + MIXER_SetMixers(NULL, 0); + if(auto_map) + RemapChannelsForProtocol(EATRG0); + if(! Model.name[0]) + sprintf(Model.name, "Model%d", 1); + return 1; +} + +const char* const names[] = { +// "../../tests/models/geniuscp.ini", + +"../../tests/models/fx071.ini", + +"../../tests/models/280qav.ini", +"../../tests/models/bixler2.ini", + +"../../tests/models/yacht.ini", + +"../../tests/models/4g6s.ini", +"../../tests/models/blade130x.ini", +"../../tests/models/nazath.ini", + +"../../tests/models/apm.ini", +"../../tests/models/deltaray.ini", +"../../tests/models/trex150dfc.ini", + +"../../tests/models/ardrone2.ini", +"../../tests/models/wltoys931.ini", +}; + +void TestNewAndOld(CuTest *t) +{ + struct Model ValidateModel; + + for (unsigned i = 0; i < ARRAYSIZE(names); i++) { + const char *filename = names[i]; + printf("Test model: %s\n", filename); + CuAssertTrue(t, CONFIG_ReadModel_old(filename)); + memcpy(&ValidateModel, &Model, sizeof(Model)); + CuAssertTrue(t, CONFIG_ReadModel_new(filename)); + printf("\tRead successfully\n", filename); + + CuAssertTrue(t, memcmp(&ValidateModel, &Model, sizeof(Model)) == 0); + printf("\tRead result is identical\n", filename); + + CONFIG_WriteModel(1); + CONFIG_ReadModel(1); + CuAssertTrue(t, memcmp(&ValidateModel, &Model, sizeof(Model)) == 0); + printf("\tWrite result is identical\n", filename); + } +} + void TestModelLoadSave(CuTest *t) { struct Model ValidateModel;