ubloxcfg
u-blox 9 configuration helpers
test.c
1 // u-blox 9 positioning receivers configuration library test program
2 //
3 // Copyright (c) 2020 Philippe Kehl (flipflip at oinkzwurgl dot org),
4 // https://oinkzwurgl.org/hacking/ubloxcfg
5 //
6 // This program is free software: you can redistribute it and/or modify it under the terms of the
7 // GNU General Public License as published by the Free Software Foundation, either version 3 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 // See the GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along with this program.
15 // If not, see <https://www.gnu.org/licenses/>.
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdint.h>
20 #include <stdbool.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <float.h>
24 
25 #include "ubloxcfg.h"
26 
27 #define NUMOF(array) (sizeof(array)/sizeof(*(array)))
28 #define STRINGIFY(x) _STRINGIFY(x)
29 #define _STRINGIFY(x) #x
30 
31 static int gVerbosity = 0;
32 
33 // Hex dump
34 void HEXDUMP(const char *descr, const void *pkData, int size)
35 {
36  if (gVerbosity <= 0)
37  {
38  return;
39  }
40  const char i2hex[] = "0123456789abcdef";
41  const char *data = pkData;
42  for (int ix = 0; ix < size; )
43  {
44  char buf[128];
45  memset(buf, ' ', sizeof(buf));
46  for (int ix2 = 0; (ix2 < 16) && ((ix + ix2) < size); ix2++)
47  {
48  const uint8_t c = data[ix + ix2];
49  buf[3 * ix2 ] = i2hex[ (c >> 4) & 0xf ];
50  buf[3 * ix2 + 1] = i2hex[ c & 0xf ];
51  buf[3 * 16 + 2 + ix2] = isprint((int)c) ? c : '.';
52  buf[3 * 16 + 3 + ix2] = '\0';
53  }
54  printf("%s 0x%04x %s\n", descr ? descr : "", ix, buf);
55  ix += 16;
56  }
57 }
58 
59 // Assertion with result printing
60 #define TEST(descr, predicate) do { numTests++; \
61  if (predicate) \
62  { \
63  numPass++; \
64  if (gVerbosity > 0) { printf("%03d PASS %s: %s [%s:%d]\n", numTests, descr, # predicate, __FILE__, __LINE__); } \
65  } \
66  else \
67  { \
68  numFail++; \
69  printf("%03d FAIL %s: %s [%s:%d]\n", numTests, descr, # predicate, __FILE__, __LINE__); \
70  } \
71  } while (0)
72 
73 int main(int argc, char **argv)
74 {
75  for (int ix = 0; ix < argc; ix++)
76  {
77  if (strcmp(argv[ix], "-v") == 0)
78  {
79  gVerbosity++;
80  }
81  }
82 
83  int numTests = 0;
84  int numPass = 0;
85  int numFail = 0;
86 
87  // Data types
88  {
89  //TEST("bool size is 1", sizeof(bool) == 1);
90  //TEST("int size is 4", sizeof(int) == 4);
91  TEST("float size is 4", sizeof(float) == 4);
92  TEST("double size is 8", sizeof(double) == 8);
93  TEST("int8_t size is 1", sizeof(int8_t) == 1);
94  TEST("int16_t size is 2", sizeof(int16_t) == 2);
95  TEST("int32_t size is 4", sizeof(int32_t) == 4);
96  TEST("int64_t size is 8", sizeof(int64_t) == 8);
97  TEST("uint8_t size is 1", sizeof(uint8_t) == 1);
98  TEST("uint16_t size is 2", sizeof(uint16_t) == 2);
99  TEST("uint32_t size is 4", sizeof(uint32_t) == 4);
100  TEST("uint64_t size is 8", sizeof(uint64_t) == 8);
101  TEST("UBLOXCFG_VALUE_t size is 8", sizeof(UBLOXCFG_VALUE_t) == 8);
102  }
103 
104  // We (curently) need little endian
105  {
106  volatile uint32_t test = 0xdeadbeef;
107  volatile uint8_t *byteorder = (volatile uint8_t *)&test;
108  TEST("byte order is little-endian", (byteorder[0] == 0xef) && (byteorder[1] == 0xbe) && (byteorder[2] == 0xad) && (byteorder[3] == 0xde) );
109  }
110 
111  // Lookup item by name
112  {
113  const UBLOXCFG_ITEM_t *item = ubloxcfg_getItemByName(UBLOXCFG_CFG_UBLOXCFGTEST_U1_STR);
114  TEST("lookup item by name", (item != NULL) && (item->id == UBLOXCFG_CFG_UBLOXCFGTEST_U1_ID));
115  const UBLOXCFG_ITEM_t *fail = ubloxcfg_getItemByName("nope-nope-nope");
116  TEST("lookup item by name", (fail == NULL));
117  const UBLOXCFG_ITEM_t *item2 = ubloxcfg_getItemByName(STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_U2_ID));
118  TEST("lookup item by hex id", (item2 != NULL) && (item2->id == UBLOXCFG_CFG_UBLOXCFGTEST_U2_ID));
119  }
120 
121  // Lookup item by ID
122  {
123  const UBLOXCFG_ITEM_t *item = ubloxcfg_getItemById(UBLOXCFG_CFG_UBLOXCFGTEST_U1_ID);
124  TEST("lookup item by ID", (item != NULL) && (item->id == UBLOXCFG_CFG_UBLOXCFGTEST_U1_ID) );
125  const UBLOXCFG_ITEM_t *fail = ubloxcfg_getItemById(0xffffffff);
126  TEST("lookup item by ID", (fail == NULL));
127  }
128 
129  // Message output rate configs
130  {
131  const UBLOXCFG_MSGRATE_t *rates = ubloxcfg_getMsgRateCfg(UBLOXCFG_UBX_NAV_PVT_STR);
132  TEST("lookup msg rate cfg by name", (rates != NULL));
133  if (rates != NULL)
134  {
135  TEST("msgrate cfg UART1", (rates->itemUart1->id == UBLOXCFG_CFG_MSGOUT_UBX_NAV_PVT_UART1_ID));
136  TEST("msgrate cfg UART2", (rates->itemUart2->id == UBLOXCFG_CFG_MSGOUT_UBX_NAV_PVT_UART2_ID));
137  TEST("msgrate cfg SPI", (rates->itemSpi->id == UBLOXCFG_CFG_MSGOUT_UBX_NAV_PVT_SPI_ID));
138  TEST("msgrate cfg I2C", (rates->itemI2c->id == UBLOXCFG_CFG_MSGOUT_UBX_NAV_PVT_I2C_ID));
139  TEST("msgrate cfg USB", (rates->itemUsb->id == UBLOXCFG_CFG_MSGOUT_UBX_NAV_PVT_USB_ID));
140  }
141  }
142 
143  // Test vectors for the encode/decode data tests below
144  const UBLOXCFG_KEYVAL_t testKeyVal[] =
145  {
146  UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_L, true ), // L 0x10fe0001 0x01
147  UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_U1, 42 ), // U1 0x20fe0011 0x2a
148  UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_I2, -42 ), // I2 0x30fe0022 0xffd6
149  UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X4, 0xdeadbeef ), // X4 0x40fe0033 0xdeadbeef
150  UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X8, 0xdeadbeef ), // X8 0x50fe0034 0x00000000deadbeef
151  UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, 1.0f/3.0f ), // R4 0x40fe0041 0x3eaaaaab
152  UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, 1e-3/3.0 ), // R8 0x50fe0042 0xef35d867c3ece2a5
153  UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E1, ONE ), // E1 0x20fe0041 0x01
154  UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E2, MINUS_ONE ), // E2 0x30fe0042 0xffff
155  UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E4, FOUR_HEX ), // E4 0x40fe0043 0x00000004
156  };
157  //HEXDUMP("testKeyVal", testKeyVal, sizeof(testKeyVal));
158  const uint8_t testData[4+1 + 4+1 + 4+2 + 4+4 + 4+8 + 4+4 + 4+8 + 4+1 + 4+2 + 4+4] =
159  {
160  0x01, 0x00, 0xfe, 0x10, 0x01,
161  0x11, 0x00, 0xfe, 0x20, 0x2a,
162  0x22, 0x00, 0xfe, 0x30, 0xd6, 0xff,
163  0x33, 0x00, 0xfe, 0x40, 0xef, 0xbe, 0xad, 0xde,
164  0x34, 0x00, 0xfe, 0x50, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
165  0x41, 0x00, 0xfe, 0x40, 0xab, 0xaa, 0xaa, 0x3e,
166  0x42, 0x00, 0xfe, 0x50, 0xa5, 0xe2, 0xec, 0xc3, 0x67, 0xd8, 0x35, 0x3f,
167  0x41, 0x00, 0xfe, 0x20, 0x01,
168  0x42, 0x00, 0xfe, 0x30, 0xff, 0xff,
169  0x43, 0x00, 0xfe, 0x40, 0x04, 0x00, 0x00, 0x00
170  };
171  //HEXDUMP("testData", testData, sizeof(testData));
172 
173  // Encode key-value pairs to data
174  {
175  int dataSize = 0;
176  uint8_t data[NUMOF(testKeyVal) * 8];
177  const bool makeDataRes = ubloxcfg_makeData(data, sizeof(data), testKeyVal, NUMOF(testKeyVal), &dataSize);
178  TEST("encode config data", (makeDataRes) && (dataSize == sizeof(testData)) && (memcmp(testData, data, sizeof(testData)) == 0) );
179  //HEXDUMP("data", testData, sizeof(testData));
180  //HEXDUMP("test", data, dataSize);
181  }
182 
183  // Decode data to key-value pairs
184  {
185  int nKeyVal = 0;
186  UBLOXCFG_KEYVAL_t keyVal[NUMOF(testKeyVal)];
187  const bool parseDataRes = ubloxcfg_parseData(testData, sizeof(testData), keyVal, NUMOF(keyVal), &nKeyVal);
188  TEST("decode config data", (parseDataRes) && (nKeyVal == NUMOF(testKeyVal)) && (memcmp(testKeyVal, keyVal, sizeof(testKeyVal)) == 0) );
189  //HEXDUMP("data", testKeyVal, sizeof(testKeyVal));
190  //HEXDUMP("test", keyVal, sizeof(keyVal));
191  }
192 
193  // Stringify values
194  {
195  typedef struct TEST_VAL_STR_s
196  {
197  const UBLOXCFG_KEYVAL_t keyVal;
198  const char *expTypeStr;
199  const char *expValStr;
200  const bool expRes;
201  } TEST_VAL_STR_t;
202  const TEST_VAL_STR_t valStr[] =
203  {
204  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_L, false ), .expValStr = "0 (false)",
205  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_L_TYPE), .expRes = true },
206  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_L, true ), .expValStr = "1 (true)",
207  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_L_TYPE), .expRes = true },
208 
209  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_U1, 0 ), .expValStr = "0",
210  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_U1_TYPE), .expRes = true },
211  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_U1, UINT8_MAX ), .expValStr = "255",
212  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_U1_TYPE), .expRes = true },
213  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_U2, 0 ), .expValStr = "0",
214  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_U2_TYPE), .expRes = true },
215  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_U2, UINT16_MAX ), .expValStr = "65535",
216  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_U2_TYPE), .expRes = true },
217  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_U4, 0 ), .expValStr = "0",
218  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_U4_TYPE), .expRes = true },
219  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_U4, UINT32_MAX ), .expValStr = "4294967295",
220  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_U4_TYPE), .expRes = true },
221  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_U8, 0), .expValStr = "0",
222  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_U8_TYPE), .expRes = true },
223  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_U8, UINT64_MAX ), .expValStr = "18446744073709551615",
224  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_U8_TYPE), .expRes = true },
225 
226  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_I1, INT8_MIN ), .expValStr = "-128",
227  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_I1_TYPE), .expRes = true },
228  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_I1, INT8_MAX ), .expValStr = "127",
229  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_I1_TYPE), .expRes = true },
230  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_I2, INT16_MIN ), .expValStr = "-32768",
231  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_I2_TYPE), .expRes = true },
232  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_I2, INT16_MAX ), .expValStr = "32767",
233  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_I2_TYPE), .expRes = true },
234  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_I4, INT32_MIN ), .expValStr = "-2147483648",
235  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_I4_TYPE), .expRes = true },
236  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_I4, INT32_MAX ), .expValStr = "2147483647",
237  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_I4_TYPE), .expRes = true },
238  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_I8, INT64_MIN ), .expValStr = "-9223372036854775808",
239  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_I8_TYPE), .expRes = true },
240  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_I8, INT64_MAX ), .expValStr = "9223372036854775807",
241  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_I8_TYPE), .expRes = true },
242 
243  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, -FLT_MIN ), .expValStr = NULL, // .expValStr = "-1.17549435082228750796874e-38",
244  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
245  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, FLT_MAX ), .expValStr = NULL, // .expValStr = "3.40282346638528859811704e+38",
246  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
247  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, FLT_EPSILON), .expValStr = NULL, // .expValStr = "1.1920928955078125e-07",
248  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
249  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, 0.0 ), .expValStr = NULL, // .expValStr = "0",
250  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
251  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, 0.5 ), .expValStr = NULL, // .expValStr = "0.5",
252  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
253  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, 1.0 ), .expValStr = NULL, // .expValStr = "1",
254  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
255  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, 1.0/3.0 ), .expValStr = NULL, // .expValStr = "0.333333343267440795898438",
256  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
257  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, 1e-1/3.0 ), .expValStr = NULL, // .expValStr = "0.0333333350718021392822266",
258  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
259  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, 1e-2/3.0 ), .expValStr = NULL, // .expValStr = "0.0033333334140479564666748",
260  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
261  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, 1e-3/3.0 ), .expValStr = NULL, // .expValStr = "0.000333333329763263463973999",
262  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
263  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, 1e-4/3.0 ), .expValStr = NULL, // .expValStr = "3.33333337039221078157425e-05",
264  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
265  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, 1e-5/3.0 ), .expValStr = NULL, // .expValStr = "3.33333332491747569292784e-06",
266  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
267  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, 1e-6/3.0 ), .expValStr = NULL, // .expValStr = "3.33333332491747569292784e-07",
268  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
269  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R4, 1e-12/3.0 ), .expValStr = NULL, // .expValStr = "3.33333322966380962704136e-13",
270  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R4_TYPE), .expRes = true },
271 
272  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, -DBL_MIN ), .expValStr = NULL, // .expValStr = "-2.22507385850720138309023271733240406421921598046233183e-308",
273  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
274  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, DBL_MAX ), .expValStr = NULL, // .expValStr = "1.79769313486231570814527423731704356798070567525844997e+308",
275  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
276  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, DBL_EPSILON), .expValStr = NULL, // .expValStr = "2.220446049250313080847263336181640625e-16",
277  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
278  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, 0.0 ), .expValStr = NULL, // .expValStr = "0",
279  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
280  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, 0.5 ), .expValStr = NULL, // .expValStr = "0.5",
281  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
282  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, 1.0 ), .expValStr = NULL, // .expValStr = "1",
283  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
284  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, 1.0/3.0 ), .expValStr = NULL, // .expValStr = "0.333333333333333314829616256247390992939472198486328125",
285  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
286  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, 1e-1/3.0 ), .expValStr = NULL, // .expValStr = "0.0333333333333333328707404064061847748234868049621582031",
287  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
288  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, 1e-2/3.0 ), .expValStr = NULL, // .expValStr = "0.00333333333333333354728256203713954164413735270500183105",
289  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
290  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, 1e-3/3.0 ), .expValStr = NULL, // .expValStr = "0.000333333333333333322202191029148821144190151244401931763",
291  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
292  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, 1e-4/3.0 ), .expValStr = NULL, // .expValStr = "3.33333333333333349307245341286431994376471266150474548e-05",
293  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
294  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, 1e-5/3.0 ), .expValStr = NULL, // .expValStr = "3.33333333333333332366586396200425213010021252557635307e-06",
295  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
296  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, 1e-6/3.0 ), .expValStr = NULL, // .expValStr = "3.33333333333333353542410077557933689718083769548684359e-07",
297  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
298  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_R8, 1e-12/3.0 ), .expValStr = NULL, // .expValStr = "3.33333333333333343457915187800123637867823894742613788e-13",
299  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_R8_TYPE), .expRes = true },
300 
301  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E1, ONE ), .expValStr = "1 (ONE)",
302  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E1_TYPE), .expRes = true },
303  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E1, TWO ), .expValStr = "2 (TWO)",
304  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E1_TYPE), .expRes = true },
305  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E1, THREE ), .expValStr = "3 (THREE)",
306  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E1_TYPE), .expRes = true },
307  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E1, MINUS_ONE ), .expValStr = "-1 (MINUS_ONE)",
308  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E1_TYPE), .expRes = true },
309  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E1, FOUR_HEX ), .expValStr = "0x04 (FOUR_HEX)",
310  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E1_TYPE), .expRes = true },
311  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_E1, 42 ), .expValStr = "42 (n/a)",
312  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E1_TYPE), .expRes = true },
313 
314  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E2, ONE ), .expValStr = "1 (ONE)",
315  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E2_TYPE), .expRes = true },
316  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E2, TWO ), .expValStr = "2 (TWO)",
317  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E2_TYPE), .expRes = true },
318  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E2, THREE ), .expValStr = "3 (THREE)",
319  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E2_TYPE), .expRes = true },
320  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E2, MINUS_ONE ), .expValStr = "-1 (MINUS_ONE)",
321  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E2_TYPE), .expRes = true },
322  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E2, FOUR_HEX ), .expValStr = "0x0004 (FOUR_HEX)",
323  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E2_TYPE), .expRes = true },
324  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_E2, 42 ), .expValStr = "42 (n/a)",
325  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E2_TYPE), .expRes = true },
326 
327  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E4, ONE ), .expValStr = "1 (ONE)",
328  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E4_TYPE), .expRes = true },
329  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E4, TWO ), .expValStr = "2 (TWO)",
330  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E4_TYPE), .expRes = true },
331  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E4, THREE ), .expValStr = "3 (THREE)",
332  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E4_TYPE), .expRes = true },
333  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E4, MINUS_ONE ), .expValStr = "-1 (MINUS_ONE)",
334  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E4_TYPE), .expRes = true },
335  { .keyVal = UBLOXCFG_KEYVAL_ENU( CFG_UBLOXCFGTEST_E4, FOUR_HEX ), .expValStr = "0x00000004 (FOUR_HEX)",
336  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E4_TYPE), .expRes = true },
337  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_E4, 42 ), .expValStr = "42 (n/a)",
338  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_E4_TYPE), .expRes = true },
339 
340  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X1, UBLOXCFG_CFG_UBLOXCFGTEST_X1_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X1_LAST ), .expValStr = "0x81 (FIRST|LAST)",
341  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X1_TYPE), .expRes = true },
342  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X1, 0xff ), .expValStr = "0xff (FIRST|SECOND|LAST|0x7c)",
343  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X1_TYPE), .expRes = true },
344  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X1, 0x7c ), .expValStr = "0x7c (n/a)",
345  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X1_TYPE), .expRes = true },
346  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X1, 0x00 ), .expValStr = "0x00 (n/a)",
347  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X1_TYPE), .expRes = true },
348 
349  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X2, UBLOXCFG_CFG_UBLOXCFGTEST_X2_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X2_LAST ), .expValStr = "0x8001 (FIRST|LAST)",
350  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X2_TYPE), .expRes = true },
351  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X2, 0xffff ), .expValStr = "0xffff (FIRST|SECOND|LAST|0x7ffc)",
352  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X2_TYPE), .expRes = true },
353  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X2, 0x7ffc ), .expValStr = "0x7ffc (n/a)",
354  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X2_TYPE), .expRes = true },
355  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X2, 0x0000 ), .expValStr = "0x0000 (n/a)",
356  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X2_TYPE), .expRes = true },
357 
358  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X4, UBLOXCFG_CFG_UBLOXCFGTEST_X4_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X4_LAST ), .expValStr = "0x80000001 (FIRST|LAST)",
359  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X4_TYPE), .expRes = true },
360  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X4, 0xffffffff ), .expValStr = "0xffffffff (FIRST|SECOND|LAST|0x7ffffffc)",
361  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X4_TYPE), .expRes = true },
362  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X4, 0x7ffffffc ), .expValStr = "0x7ffffffc (n/a)",
363  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X4_TYPE), .expRes = true },
364  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X4, 0x00000000 ), .expValStr = "0x00000000 (n/a)",
365  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X4_TYPE), .expRes = true },
366 
367  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X8, UBLOXCFG_CFG_UBLOXCFGTEST_X8_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X8_LAST ), .expValStr = "0x8000000000000001 (FIRST|LAST)",
368  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X8_TYPE), .expRes = true },
369  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X8, 0xffffffffffffffff ), .expValStr = "0xffffffffffffffff (FIRST|SECOND|LAST|0x7ffffffffffffffc)",
370  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X8_TYPE), .expRes = true },
371  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X8, 0x7ffffffffffffffc ), .expValStr = "0x7ffffffffffffffc (n/a)",
372  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X8_TYPE), .expRes = true },
373  { .keyVal = UBLOXCFG_KEYVAL_ANY( CFG_UBLOXCFGTEST_X8, 0x0000000000000000 ), .expValStr = "0x0000000000000000 (n/a)",
374  .expTypeStr = STRINGIFY(UBLOXCFG_CFG_UBLOXCFGTEST_X8_TYPE), .expRes = true }
375  };
376  for (int ix = 0; ix < (int)NUMOF(valStr); ix++)
377  {
378  char valueStr[200];
379  const UBLOXCFG_ITEM_t *item = ubloxcfg_getItemById(valStr[ix].keyVal.id);
380  TEST("must not fail", (item != NULL));
381  if (item != NULL)
382  {
383  const bool stringifyRes = ubloxcfg_stringifyValue(valueStr, sizeof(valueStr), item->type, item, &valStr[ix].keyVal.val);
384  const char *typeStr = ubloxcfg_typeStr(item->type);
385 
386  char descr[100];
387  snprintf(descr, sizeof(descr), "stringify item %d (%s)", ix + 1, item->name);
388  TEST(descr, (stringifyRes == valStr[ix].expRes));
389  if (valStr[ix].expValStr != NULL)
390  {
391  TEST(descr, (strcmp(valStr[ix].expValStr, valueStr) == 0));
392  }
393  TEST(descr, (typeStr != NULL) && (strcmp(valStr[ix].expTypeStr, typeStr) == 0));
394  }
395  }
396  }
397 
398  // Stringify unknown item
399  {
400  const struct { UBLOXCFG_KEYVAL_t kv; const char *str; } kvStr[] =
401  {
402  { .kv = { .id = 0x10fe0ff1, .val = { .L = true } }, .str = "CFG-?-? (0x10fe0ff1, ?0) = 0x1" },
403  { .kv = { .id = 0x20fe0ff2, .val = { .X1 = 0x12 } }, .str = "CFG-?-? (0x20fe0ff2, ?1) = 0x12" },
404  { .kv = { .id = 0x30fe0ff3, .val = { .X2 = 0x1234 } }, .str = "CFG-?-? (0x30fe0ff3, ?2) = 0x1234" },
405  { .kv = { .id = 0x40fe0ff4, .val = { .X4 = 0x12345678 } }, .str = "CFG-?-? (0x40fe0ff4, ?4) = 0x12345678" },
406  { .kv = { .id = 0x50fe0ff5, .val = { .X8 = 0x1234567890abcdef } }, .str = "CFG-?-? (0x50fe0ff5, ?8) = 0x1234567890abcdef" },
407  };
408  for (int ix = 0; ix < (int)NUMOF(kvStr); ix++)
409  {
410  char str[200];
411  const bool res = ubloxcfg_stringifyKeyVal(str, sizeof(str), &kvStr[ix].kv);
412  TEST("stringify unkn kv res", (res));
413  TEST("stringify unkn kv str", (strcmp(kvStr[ix].str, str) == 0));
414  }
415  }
416 
417  // Split stringified value
418  {
419  char str[100];
420  char *vStr;
421  char *pStr;
422  strcpy(str, "value (pretty)");
423  TEST("splitValueStr: value (pretty)", ubloxcfg_splitValueStr(str, &vStr, &pStr) && (strcmp(vStr, "value") == 0) && (strcmp(pStr, "pretty") == 0));
424  strcpy(str, "value");
425  TEST("splitValueStr: value", ubloxcfg_splitValueStr(str, &vStr, &pStr) && (strcmp(vStr, "value") == 0) && (pStr == NULL));
426  strcpy(str, "value (n/a)");
427  TEST("splitValueStr: value (n/a)", ubloxcfg_splitValueStr(str, &vStr, &pStr) && (strcmp(vStr, "value") == 0) && (pStr == NULL));
428  }
429 
430  // Output message rate config aliases
431  {
432  const UBLOXCFG_KEYVAL_t keyVal[] =
433  {
434  UBLOXCFG_KEYVAL_ANY( UBX_NAV_PVT_UART1, 1 ),
435  UBLOXCFG_KEYVAL_ANY( UBX_NAV_PVT_UART2, 2 ),
436  UBLOXCFG_KEYVAL_ANY( UBX_NAV_PVT_SPI, 3 ),
437  UBLOXCFG_KEYVAL_ANY( UBX_NAV_PVT_I2C, 4 ),
438  UBLOXCFG_KEYVAL_ANY( UBX_NAV_PVT_USB, 5 ),
439  };
440  const uint8_t expected[5 * (4+1)] =
441  {
442  0x07, 0x00, 0x91, 0x20, 0x01,
443  0x08, 0x00, 0x91, 0x20, 0x02,
444  0x0a, 0x00, 0x91, 0x20, 0x03,
445  0x06, 0x00, 0x91, 0x20, 0x04,
446  0x09, 0x00, 0x91, 0x20, 0x05
447  };
448  uint8_t data[100];
449  int dataSize = 0;
450  const bool makeDataRes = ubloxcfg_makeData(data, sizeof(data), keyVal, NUMOF(keyVal), &dataSize);
451  TEST("msg rate config data", (makeDataRes) && (dataSize == sizeof(expected)) && (memcmp(data, expected, sizeof(expected)) == 0) );
452  }
453 
454  // Output message rate config aliases and helper
455  {
456  const UBLOXCFG_KEYVAL_t keyVal[] =
457  {
458  UBLOXCFG_KEYVAL_MSG( UBX_NAV_PVT, UART1, 1 ),
459  UBLOXCFG_KEYVAL_MSG( UBX_NAV_PVT, UART2, 2 ),
460  UBLOXCFG_KEYVAL_MSG( UBX_NAV_PVT, SPI, 3 ),
461  UBLOXCFG_KEYVAL_MSG( UBX_NAV_PVT, I2C, 4 ),
462  UBLOXCFG_KEYVAL_MSG( UBX_NAV_PVT, USB, 5 ),
463  };
464  const uint8_t expected[5 * (4+1)] =
465  {
466  0x07, 0x00, 0x91, 0x20, 0x01,
467  0x08, 0x00, 0x91, 0x20, 0x02,
468  0x0a, 0x00, 0x91, 0x20, 0x03,
469  0x06, 0x00, 0x91, 0x20, 0x04,
470  0x09, 0x00, 0x91, 0x20, 0x05
471  };
472  uint8_t data[100];
473  int dataSize = 0;
474  const bool makeDataRes = ubloxcfg_makeData(data, sizeof(data), keyVal, NUMOF(keyVal), &dataSize);
475  TEST("msg rate config data", (makeDataRes) && (dataSize == sizeof(expected)) && (memcmp(data, expected, sizeof(expected)) == 0) );
476  }
477 
478  // String to value
479  {
480  typedef struct TEST_STR_VAL_s
481  {
482  const char *str;
483  const UBLOXCFG_TYPE_t type;
484  const UBLOXCFG_VALUE_t val;
485  const bool expectedRes;
486  const uint32_t id;
487  } TEST_STR_VAL_t;
488  const TEST_STR_VAL_t strVal[] =
489  {
490  { .str = "0", .type = UBLOXCFG_TYPE_L, .val = { .L = false }, .expectedRes = true },
491  { .str = "0x0", .type = UBLOXCFG_TYPE_L, .val = { .L = false }, .expectedRes = true },
492  { .str = "1", .type = UBLOXCFG_TYPE_L, .val = { .L = true }, .expectedRes = true },
493  { .str = "01", .type = UBLOXCFG_TYPE_L, .val = { .L = true }, .expectedRes = true },
494  { .str = "0x1", .type = UBLOXCFG_TYPE_L, .val = { .L = true }, .expectedRes = true },
495  { .str = "0x2", .type = UBLOXCFG_TYPE_L, .val = { ._raw = 0 }, .expectedRes = false },
496  { .str = "42", .type = UBLOXCFG_TYPE_L, .val = { ._raw = 0 }, .expectedRes = false },
497 
498  { .str = "42", .type = UBLOXCFG_TYPE_U1, .val = { .U1 = 42 }, .expectedRes = true },
499  { .str = "255", .type = UBLOXCFG_TYPE_U1, .val = { .U1 = UINT8_MAX }, .expectedRes = true },
500  { .str = "0xff", .type = UBLOXCFG_TYPE_U1, .val = { .U1 = UINT8_MAX }, .expectedRes = true },
501  { .str = "256", .type = UBLOXCFG_TYPE_U1, .val = { ._raw = 0 }, .expectedRes = false },
502 
503  { .str = "42", .type = UBLOXCFG_TYPE_U2, .val = { .U2 = 42 }, .expectedRes = true },
504  { .str = "65534", .type = UBLOXCFG_TYPE_U2, .val = { .U2 = (UINT16_MAX-1) }, .expectedRes = true },
505  { .str = "65535", .type = UBLOXCFG_TYPE_U2, .val = { .U2 = UINT16_MAX }, .expectedRes = true },
506  { .str = "0xffff", .type = UBLOXCFG_TYPE_U2, .val = { .U2 = UINT16_MAX }, .expectedRes = true },
507  { .str = "65536", .type = UBLOXCFG_TYPE_U2, .val = { ._raw = 0 }, .expectedRes = false },
508 
509  { .str = "42", .type = UBLOXCFG_TYPE_U4, .val = { .U4 = 42 }, .expectedRes = true },
510  { .str = "4294967294", .type = UBLOXCFG_TYPE_U4, .val = { .U4 = (UINT32_MAX-1) }, .expectedRes = true },
511  { .str = "4294967295", .type = UBLOXCFG_TYPE_U4, .val = { .U4 = UINT32_MAX }, .expectedRes = true },
512  { .str = "0xffffffff", .type = UBLOXCFG_TYPE_U4, .val = { .U4 = UINT32_MAX }, .expectedRes = true },
513  { .str = "4294967296", .type = UBLOXCFG_TYPE_U4, .val = { ._raw = 0 }, .expectedRes = false },
514 
515  { .str = "42", .type = UBLOXCFG_TYPE_U8, .val = { .U8 = 42 }, .expectedRes = true },
516  { .str = "18446744073709551614", .type = UBLOXCFG_TYPE_U8, .val = { .U8 = (UINT64_MAX-1) }, .expectedRes = true },
517  { .str = "18446744073709551615", .type = UBLOXCFG_TYPE_U8, .val = { .U8 = UINT64_MAX }, .expectedRes = true },
518  { .str = "0xffffffffffffffff", .type = UBLOXCFG_TYPE_U8, .val = { .U8 = UINT64_MAX }, .expectedRes = true },
519  //{ .str = "18446744073709551616", .type = UBLOXCFG_TYPE_U8, .val = { ._raw = 0 }, .expectedRes = false }, // FIXME: why does this test fail?!
520 
521  { .str = "-128", .type = UBLOXCFG_TYPE_I1, .val = { .I1 = INT8_MIN }, .expectedRes = true },
522  { .str = "127", .type = UBLOXCFG_TYPE_I1, .val = { .I1 = INT8_MAX }, .expectedRes = true },
523  { .str = "+126", .type = UBLOXCFG_TYPE_I1, .val = { .I1 = (INT8_MAX-1) }, .expectedRes = true },
524  { .str = "128", .type = UBLOXCFG_TYPE_I1, .val = { ._raw = 0 }, .expectedRes = false },
525  { .str = "0xff", .type = UBLOXCFG_TYPE_I1, .val = { .I1 = -1 }, .expectedRes = true },
526 
527  { .str = "-32768", .type = UBLOXCFG_TYPE_I2, .val = { .I2 = INT16_MIN }, .expectedRes = true },
528  { .str = "32767", .type = UBLOXCFG_TYPE_I2, .val = { .I2 = INT16_MAX }, .expectedRes = true },
529  { .str = "+32766", .type = UBLOXCFG_TYPE_I2, .val = { .I2 = (INT16_MAX-1) }, .expectedRes = true },
530  { .str = "32768", .type = UBLOXCFG_TYPE_I2, .val = { ._raw = 0 }, .expectedRes = false },
531  { .str = "0xffff", .type = UBLOXCFG_TYPE_I2, .val = { .I2 = -1 }, .expectedRes = true },
532 
533  { .str = "-2147483648", .type = UBLOXCFG_TYPE_I4, .val = { .I4 = INT32_MIN }, .expectedRes = true },
534  { .str = "2147483647", .type = UBLOXCFG_TYPE_I4, .val = { .I4 = INT32_MAX }, .expectedRes = true },
535  { .str = "+2147483646", .type = UBLOXCFG_TYPE_I4, .val = { .I4 = (INT32_MAX-1) }, .expectedRes = true },
536  { .str = "2147483648", .type = UBLOXCFG_TYPE_I4, .val = { ._raw = 0 }, .expectedRes = false },
537  { .str = "0xffffffff", .type = UBLOXCFG_TYPE_I4, .val = { .I4 = -1 }, .expectedRes = true },
538 
539  { .str = "-9223372036854775808", .type = UBLOXCFG_TYPE_I8, .val = { .I8 = INT64_MIN }, .expectedRes = true },
540  { .str = "9223372036854775807", .type = UBLOXCFG_TYPE_I8, .val = { .I8 = INT64_MAX }, .expectedRes = true },
541  { .str = "+9223372036854775806", .type = UBLOXCFG_TYPE_I8, .val = { .I8 = (INT64_MAX-1) }, .expectedRes = true },
542  //{ .str = "9223372036854775808", .type = UBLOXCFG_TYPE_I8, .val = { ._raw = 0 }, .expectedRes = false }, // FIXME: why does this test fail?
543  { .str = "0xffffffffffffffff", .type = UBLOXCFG_TYPE_I8, .val = { .I8 = -1 }, .expectedRes = true },
544 
545  { .str = "0", .type = UBLOXCFG_TYPE_R4, .val = { .R4 = 0.0 }, .expectedRes = true },
546  { .str = "1.0", .type = UBLOXCFG_TYPE_R4, .val = { .R4 = 1.0 }, .expectedRes = true },
547  { .str = "-1.17549435082228750796874e-38", .type = UBLOXCFG_TYPE_R4, .val = { .R4 = -FLT_MIN }, .expectedRes = true },
548  { .str = "3.40282346638528859811704e+38", .type = UBLOXCFG_TYPE_R4, .val = { .R4 = FLT_MAX }, .expectedRes = true },
549  { .str = "1.1920928955078125e-07", .type = UBLOXCFG_TYPE_R4, .val = { .R4 = FLT_EPSILON }, .expectedRes = true },
550  { .str = "0.333333343267440795898438", .type = UBLOXCFG_TYPE_R4, .val = { .R4 = (1.0/3.0) }, .expectedRes = true },
551  // FIXME: what about +inf/-inf/nan etc.?
552 
553  { .str = "0", .type = UBLOXCFG_TYPE_R8, .val = { .R8 = 0.0 }, .expectedRes = true },
554  { .str = "1.0", .type = UBLOXCFG_TYPE_R8, .val = { .R8 = 1.0 }, .expectedRes = true },
555  { .str = "-2.22507385850720138309023271733240406421921598046233183e-308", .type = UBLOXCFG_TYPE_R8, .val = { .R8 = -DBL_MIN }, .expectedRes = true },
556  { .str = "1.79769313486231570814527423731704356798070567525844997e+308", .type = UBLOXCFG_TYPE_R8, .val = { .R8 = DBL_MAX }, .expectedRes = true },
557  { .str = "2.220446049250313080847263336181640625e-16", .type = UBLOXCFG_TYPE_R8, .val = { .R8 = DBL_EPSILON }, .expectedRes = true },
558  { .str = "0.333333333333333314829616256247390992939472198486328125", .type = UBLOXCFG_TYPE_R8, .val = { .R8 = (1.0/3.0) }, .expectedRes = true },
559 
560  { .str = "42", .type = UBLOXCFG_TYPE_E1, .val = { .E1 = 42 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_E1_ID, .expectedRes = true },
561  { .str = "ONE", .type = UBLOXCFG_TYPE_E1, .val = { .E1 = 1 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_E1_ID, .expectedRes = true },
562  { .str = "NOPE", .type = UBLOXCFG_TYPE_E1, .val = { ._raw = 0 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_E1_ID, .expectedRes = false },
563 
564  { .str = "42", .type = UBLOXCFG_TYPE_E2, .val = { .E2 = 42 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_E2_ID, .expectedRes = true },
565  { .str = "ONE", .type = UBLOXCFG_TYPE_E2, .val = { .E2 = 1 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_E2_ID, .expectedRes = true },
566  { .str = "NOPE", .type = UBLOXCFG_TYPE_E2, .val = { ._raw = 0 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_E2_ID, .expectedRes = false },
567 
568  { .str = "42", .type = UBLOXCFG_TYPE_E4, .val = { .E4 = 42 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_E4_ID, .expectedRes = true },
569  { .str = "ONE", .type = UBLOXCFG_TYPE_E4, .val = { .E4 = 1 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_E4_ID, .expectedRes = true },
570  { .str = "NOPE", .type = UBLOXCFG_TYPE_E4, .val = { ._raw = 0 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_E4_ID, .expectedRes = false },
571 
572  { .str = "FIRST|LAST", .type = UBLOXCFG_TYPE_X1, .val = { .X1 = UBLOXCFG_CFG_UBLOXCFGTEST_X1_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X1_LAST }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X1_ID, .expectedRes = true },
573  { .str = "0x81", .type = UBLOXCFG_TYPE_X1, .val = { .X1 = UBLOXCFG_CFG_UBLOXCFGTEST_X1_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X1_LAST }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X1_ID, .expectedRes = true },
574  { .str = "0x01|LAST", .type = UBLOXCFG_TYPE_X1, .val = { .X1 = UBLOXCFG_CFG_UBLOXCFGTEST_X1_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X1_LAST }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X1_ID, .expectedRes = true },
575  { .str = "FIRST|SECOND|LAST|0x7c", .type = UBLOXCFG_TYPE_X1, .val = { .X1 = 0xff }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X1_ID, .expectedRes = true },
576  { .str = "SECOND|0x7c|FIRST|LAST", .type = UBLOXCFG_TYPE_X1, .val = { .X1 = 0xff }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X1_ID, .expectedRes = true },
577  { .str = "255", .type = UBLOXCFG_TYPE_X1, .val = { .X1 = 0xff }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X1_ID, .expectedRes = true },
578 
579  { .str = "FIRST|LAST", .type = UBLOXCFG_TYPE_X2, .val = { .X2 = UBLOXCFG_CFG_UBLOXCFGTEST_X2_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X2_LAST }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X2_ID, .expectedRes = true },
580  { .str = "0x8001", .type = UBLOXCFG_TYPE_X2, .val = { .X2 = UBLOXCFG_CFG_UBLOXCFGTEST_X2_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X2_LAST }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X2_ID, .expectedRes = true },
581  { .str = "0x0001|LAST", .type = UBLOXCFG_TYPE_X2, .val = { .X2 = UBLOXCFG_CFG_UBLOXCFGTEST_X2_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X2_LAST }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X2_ID, .expectedRes = true },
582  { .str = "FIRST|SECOND|LAST|0x7ffc", .type = UBLOXCFG_TYPE_X2, .val = { .X2 = 0xffff }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X2_ID, .expectedRes = true },
583  { .str = "SECOND|0x7ffc|FIRST|LAST", .type = UBLOXCFG_TYPE_X2, .val = { .X2 = 0xffff }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X2_ID, .expectedRes = true },
584  { .str = "65535", .type = UBLOXCFG_TYPE_X2, .val = { .X2 = 0xffff }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X2_ID, .expectedRes = true },
585 
586  { .str = "FIRST|LAST", .type = UBLOXCFG_TYPE_X4, .val = { .X4 = UBLOXCFG_CFG_UBLOXCFGTEST_X4_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X4_LAST }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X4_ID, .expectedRes = true },
587  { .str = "0x80000001", .type = UBLOXCFG_TYPE_X4, .val = { .X4 = UBLOXCFG_CFG_UBLOXCFGTEST_X4_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X4_LAST }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X4_ID, .expectedRes = true },
588  { .str = "0x00000001|LAST", .type = UBLOXCFG_TYPE_X4, .val = { .X4 = UBLOXCFG_CFG_UBLOXCFGTEST_X4_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X4_LAST }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X4_ID, .expectedRes = true },
589  { .str = "FIRST|SECOND|LAST|0x7ffffffc", .type = UBLOXCFG_TYPE_X4, .val = { .X4 = 0xffffffff }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X4_ID, .expectedRes = true },
590  { .str = "SECOND|0x7ffffffc|FIRST|LAST", .type = UBLOXCFG_TYPE_X4, .val = { .X4 = 0xffffffff }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X4_ID, .expectedRes = true },
591  { .str = "4294967295", .type = UBLOXCFG_TYPE_X4, .val = { .X4 = 0xffffffff }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X4_ID, .expectedRes = true },
592 
593 
594  { .str = "FIRST|LAST", .type = UBLOXCFG_TYPE_X8, .val = { .X8 = UBLOXCFG_CFG_UBLOXCFGTEST_X8_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X8_LAST }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X8_ID, .expectedRes = true },
595  { .str = "0x8000000000000001", .type = UBLOXCFG_TYPE_X8, .val = { .X8 = UBLOXCFG_CFG_UBLOXCFGTEST_X8_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X8_LAST }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X8_ID, .expectedRes = true },
596  { .str = "0x0000000000000001|LAST", .type = UBLOXCFG_TYPE_X8, .val = { .X8 = UBLOXCFG_CFG_UBLOXCFGTEST_X8_FIRST|UBLOXCFG_CFG_UBLOXCFGTEST_X8_LAST }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X8_ID, .expectedRes = true },
597  { .str = "FIRST|SECOND|LAST|0x7ffffffffffffffc", .type = UBLOXCFG_TYPE_X8, .val = { .X8 = 0xffffffffffffffff }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X8_ID, .expectedRes = true },
598  { .str = "SECOND|0x7ffffffffffffffc|FIRST|LAST", .type = UBLOXCFG_TYPE_X8, .val = { .X8 = 0xffffffffffffffff }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X8_ID, .expectedRes = true },
599  { .str = "18446744073709551615", .type = UBLOXCFG_TYPE_X8, .val = { .X8 = 0xffffffffffffffff }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X8_ID, .expectedRes = true },
600 
601  // Bad input
602  { .str = "NOPE", .type = UBLOXCFG_TYPE_E4, .val = { ._raw = 0 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_E4_ID, .expectedRes = false },
603 
604  { .str = "666 ", .type = UBLOXCFG_TYPE_I4, .val = { ._raw = 0 }, .expectedRes = false },
605  { .str = " 666", .type = UBLOXCFG_TYPE_I4, .val = { ._raw = 0 }, .expectedRes = false },
606  { .str = " 666 ", .type = UBLOXCFG_TYPE_I4, .val = { ._raw = 0 }, .expectedRes = false },
607  { .str = "bad666", .type = UBLOXCFG_TYPE_I4, .val = { ._raw = 0 }, .expectedRes = false },
608  { .str = "666bad", .type = UBLOXCFG_TYPE_I4, .val = { ._raw = 0 }, .expectedRes = false },
609 
610  { .str = "666 ", .type = UBLOXCFG_TYPE_U4, .val = { ._raw = 0 }, .expectedRes = false },
611  { .str = " 666", .type = UBLOXCFG_TYPE_U4, .val = { ._raw = 0 }, .expectedRes = false },
612  { .str = " 666 ", .type = UBLOXCFG_TYPE_U4, .val = { ._raw = 0 }, .expectedRes = false },
613  { .str = "bad666", .type = UBLOXCFG_TYPE_U4, .val = { ._raw = 0 }, .expectedRes = false },
614  { .str = "666bad", .type = UBLOXCFG_TYPE_U4, .val = { ._raw = 0 }, .expectedRes = false },
615 
616  { .str = "666 ", .type = UBLOXCFG_TYPE_X4, .val = { ._raw = 0 }, .expectedRes = false },
617  { .str = " 666", .type = UBLOXCFG_TYPE_X4, .val = { ._raw = 0 }, .expectedRes = false },
618  { .str = " 666 ", .type = UBLOXCFG_TYPE_X4, .val = { ._raw = 0 }, .expectedRes = false },
619  { .str = "bad666", .type = UBLOXCFG_TYPE_X4, .val = { ._raw = 0 }, .expectedRes = false },
620  { .str = "666bad", .type = UBLOXCFG_TYPE_X4, .val = { ._raw = 0 }, .expectedRes = false },
621 
622  { .str = "0xbadhex00", .type = UBLOXCFG_TYPE_I4, .val = { ._raw = 0 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_I4_ID, .expectedRes = false },
623  { .str = "0xbadhex00", .type = UBLOXCFG_TYPE_U4, .val = { ._raw = 0 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_U4_ID, .expectedRes = false },
624  { .str = "0xbadhex00", .type = UBLOXCFG_TYPE_X4, .val = { ._raw = 0 }, .id = UBLOXCFG_CFG_UBLOXCFGTEST_X4_ID, .expectedRes = false }
625  };
626  for (int ix = 0; ix < (int)NUMOF(strVal); ix++)
627  {
628  UBLOXCFG_VALUE_t val = { ._raw = 0 };
629  const UBLOXCFG_ITEM_t *item = strVal[ix].id != 0 ? ubloxcfg_getItemById(strVal[ix].id) : NULL;
630  const bool res = ubloxcfg_valueFromString(strVal[ix].str, strVal[ix].type, item, &val);
631  char descr[100];
632  snprintf(descr, sizeof(descr), "str to value %d (%s)", ix + 1, strVal[ix].str);
633  TEST(descr, (res == strVal[ix].expectedRes));
634  TEST(descr, (val._raw == strVal[ix].val._raw));
635  }
636  }
637 
638  // Configuration layers
639  {
640  TEST("layer name RAM", strcmp(ubloxcfg_layerName(UBLOXCFG_LAYER_RAM), "RAM") == 0);
641  TEST("layer name BBR", strcmp(ubloxcfg_layerName(UBLOXCFG_LAYER_BBR), "BBR") == 0);
642  TEST("layer name Flash", strcmp(ubloxcfg_layerName(UBLOXCFG_LAYER_FLASH), "Flash") == 0);
643  TEST("layer name Default", strcmp(ubloxcfg_layerName(UBLOXCFG_LAYER_DEFAULT), "Default") == 0);
644  TEST("bad layer name", strcmp(ubloxcfg_layerName((UBLOXCFG_LAYER_t)99), "?") == 0);
645 
646  UBLOXCFG_LAYER_t layer;
647  TEST("layer val 'RAM'", ubloxcfg_layerFromName("RAM", &layer) && (layer == UBLOXCFG_LAYER_RAM));
648  TEST("layer val 'ram'", ubloxcfg_layerFromName("ram", &layer) && (layer == UBLOXCFG_LAYER_RAM));
649  TEST("layer val 'Ram'", ubloxcfg_layerFromName("Ram", &layer) && (layer == UBLOXCFG_LAYER_RAM));
650  TEST("layer val 'rAm'", ubloxcfg_layerFromName("rAm", &layer) && (layer == UBLOXCFG_LAYER_RAM));
651  TEST("layer val 'BBR'", ubloxcfg_layerFromName("BBR", &layer) && (layer == UBLOXCFG_LAYER_BBR));
652  TEST("layer val 'Flash'", ubloxcfg_layerFromName("Flash", &layer) && (layer == UBLOXCFG_LAYER_FLASH));
653  TEST("layer val 'Default'", ubloxcfg_layerFromName("Default", &layer) && (layer == UBLOXCFG_LAYER_DEFAULT));
654  TEST("layer val 'meier'", !ubloxcfg_layerFromName("meier", &layer));
655  }
656 
657  // Examples from ubloxcfg.h
658  {
659  const UBLOXCFG_KEYVAL_t keyVal[] =
660  {
661  UBLOXCFG_KEYVAL_ANY( CFG_NAVSPG_INIFIX3D, true ),
662  UBLOXCFG_KEYVAL_ANY( CFG_NAVSPG_WKNROLLOVER, 2099 ),
663  UBLOXCFG_KEYVAL_ENU( CFG_NAVSPG_FIXMODE, AUTO ),
664  UBLOXCFG_KEYVAL_MSG( UBX_NAV_PVT, UART1, 1),
665  UBLOXCFG_KEYVAL_MSG( UBX_MON_COMMS, UART1, 5)
666  };
667  const int numKeyVal = sizeof(keyVal) / sizeof(*keyVal);
668  TEST("makeData example numKeys", (numKeyVal == 5));
669  uint8_t data[100];
670  int size;
671  const bool res = ubloxcfg_makeData(data, sizeof(data), keyVal, numKeyVal, &size);
672  TEST("makeData example size", (res) && (size == 26));
673  }
674  {
675  const uint8_t data[26] =
676  {
677  0x13, 0x00, 0x11, 0x10, 0x01, // CFG-NAVSPG-INIFIX3D = 1 (true)
678  0x17, 0x00, 0x11, 0x30, 0x33, 0x08, // CFG-NAVSPG-WKNROLLOVER = 2099 (0x0833)
679  0x11, 0x00, 0x11, 0x20, 0x03, // CFG-NAVSPG-FIXMODE = 3 (AUTO)
680  0x07, 0x00, 0x91, 0x20, 0x01, // CFG-MSGOUT-UBX_NAV_PVT_UART1 = 1
681  0x50, 0x03, 0x91, 0x20, 0x05 // CFG-MSGOUT-UBX_MON_COMMS_UART1 = 1
682  };
683  int numKeyVal = 0;
684  UBLOXCFG_KEYVAL_t keyVal[20];
685  const bool res = ubloxcfg_parseData(data, sizeof(data), keyVal, sizeof(keyVal)/sizeof(*keyVal), &numKeyVal);
686  TEST("parseData example numKeyVal", (res) && (numKeyVal == 5));
687  TEST("parseData example keyVal[0]", (keyVal[0].id == UBLOXCFG_CFG_NAVSPG_INIFIX3D_ID) && (keyVal[0].val.L));
688  TEST("parseData example keyVal[1]", (keyVal[1].id == UBLOXCFG_CFG_NAVSPG_WKNROLLOVER_ID) && (keyVal[1].val.U2 == 2099));
689  }
690  {
691  const UBLOXCFG_KEYVAL_t keyVal[] =
692  {
693  UBLOXCFG_KEYVAL_ANY( CFG_NAVSPG_INIFIX3D, true ),
694  UBLOXCFG_KEYVAL_ANY( CFG_NAVSPG_WKNROLLOVER, 2099 ),
695  UBLOXCFG_KEYVAL_ENU( CFG_NAVSPG_FIXMODE, AUTO ),
696  UBLOXCFG_KEYVAL_ANY( CFG_NAVSPG_INFIL_MINCNO, 30 ),
697  UBLOXCFG_KEYVAL_ANY( CFG_NAVSPG_OUTFIL_PDOP, 20 ),
698  UBLOXCFG_KEYVAL_ANY( CFG_NAVSPG_CONSTR_ALT, 234 ),
699  UBLOXCFG_KEYVAL_MSG( UBX_NAV_PVT, UART1, 1 ),
700  { .id = 0x30fe0ff3, .val = { .U2 = 48879 } }
701  };
702  const int numKeyVal = sizeof(keyVal) / sizeof(*keyVal);
703  TEST("stringifyKeyVal exmaple numKeyVal", (numKeyVal == 8));
704  for (int ix = 0; ix < numKeyVal; ix++)
705  {
706  char str[200];
707  const bool res = ubloxcfg_stringifyKeyVal(str, sizeof(str), &keyVal[ix]);
708  TEST("stringifyKeyVal example", (res));
709  }
710  }
711 
712  // Analyse results
713  printf("%d tests: %d passed, %d failed\n", numTests, numPass, numFail);
714  if (numFail != 0)
715  {
716  printf("%d/%d tests failed!\n", numFail, numTests);
717  return(EXIT_FAILURE);
718  }
719  else
720  {
721  return(EXIT_SUCCESS);
722  }
723 }