]> sigrok.org Git - libsigrok.git/blob - src/lcr/vc4080.c
scpi-dmm: add support to get/set range on Agilent protocol using meters
[libsigrok.git] / src / lcr / vc4080.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <glib.h>
22 #include <libsigrok/libsigrok.h>
23 #include "libsigrok-internal.h"
24 #include <math.h>
25 #include <stdint.h>
26 #include <string.h>
27
28 #define LOG_PREFIX "vc4080"
29
30 #ifdef HAVE_SERIAL_COMM
31
32 /**
33  * @file Packet parser for Voltcraft 4080 LCR meters.
34  */
35
36 /*
37  * Developer notes on the protocol and the implementation:
38  *
39  * The LCR meter is connected to a serial port (1200/7e1). The protocol
40  * is text based (printables plus some line termination), is accessible
41  * to interactive exploration in a terminal. Requests differ in length
42  * (single character, or sequence of seven characters in brackets).
43  * Responses either have 14 (setup) or 39 (measurement) characters.
44  * Thus the protocol lends itself to integration with the serial-lcr
45  * driver. Setup is handled outside of the acquisition loop, and all
46  * measurement results are of equal length and end in a termination
47  * that we can synchronize to. Requesting packets from the meter is
48  * similar to serial-dmm operation.
49  *
50  * Quick notes for our parser's purposes:
51  *
52  *   pkt[0] 'L'/'C'/'R'
53  *   pkt[1] 'Q'/'D'/'R'
54  *   pkt[2] 'A'/'B' output frequency
55  *   pkt[3] 'P'/'S' circuit model
56  *   pkt[4] 'A'/'M' auto/manual
57  *
58  *   pkt[5:9] main display value in text format, '8' switching range, '9' OL
59  *   pkt[10] main display range, '0'-'6', depends on RLC and freq and ser/par
60  *
61  *   pkt[11:14] secondary display value in text format, '9' OL
62  *   pkt[15] secondary display range, '1'-'5', depends on QDR and Rs value
63  *
64  *   pkt[16] packet sequence counter, cycling through '0'-'9'
65  *
66  *   pkt[17:20] D value in text form, '9' OL
67  *   pkt[21] D range
68  *
69  *   pkt[22:25] Q value in text form, '9' OL
70  *   pkt[26] Q range
71  *
72  *   pkt[27] 'S'/'_', SETup(?)
73  *   pkt[28] 'F'/'_', FUSE
74  *   pkt[29] 'H'/'_', HOLD
75  *   pkt[30] 'R' (present value), 'M' (max), 'I' (min), 'A' (avg),
76  *           'X' (max - min), '_' (normal)
77  *   pkt[31] 'R' (REL), 'S' (REL SET), '_' (normal)
78  *   pkt[32] 'L' (LIMITS), '_' (normal)
79  *   pkt[33] 'T' (TOL), 'S' (TOL SET), '_' (normal)
80  *   pkt[34] 'B' (backlight), '_' (normal)
81  *   pkt[35] 'A' (adapter inserted(?)), '_' (normal)
82  *   pkt[36] 'B' (low battery), '_' (normal)
83  *
84  *   pkt[37] always CR (\r)
85  *   pkt[38] always LF (\n)
86  *
87  * Example packet, PeakTech 2165, 1200/8n1 and parity bit stripped:
88  *
89  *   L Q A P A 9 0 0 0 0 6 1 4 0 6 2 1 0 7 1 1 4 1 4 0 6 2 _ _ _ _ _ _ _ _ _ _ CR LF
90  *   0         5         10        15        20        25        30        35     38
91  *
92  * Another example, resistance mode, 1k probed:
93  *
94  *   52 5f 42 5f 41 30 39 39 33 30 32 30 30 30 30 39 33 37 34 35 36 31 30 30 31 33 34 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 0d 0a
95  *   R _ B _ A 09930 2 00009 3 7456 1 0013 4 __________  CR/LF
96  *
97  * Another example, C mode:
98  *
99  *   43 51 42 53 4d 30 39 38 39 31 35 30 30 31 33 34 31 37 35 38 33 31 30 30 31 33 34 5f 5f 5f 5f 5f 5f 5f 5f 5f 5f 0d 0a
100  *   C  Q  B  S  M  09891 5           00134 1           7583 1         0013 4         ____...
101  *   C, Q, 120, ser, man, 09891 @2000uF -> C = 989.1uF, 00134 -> Q = 13.4
102  *
103  *   43 51 42 53 4d 30 39 38 38 30 35 30 30 31 33 34 34 37 35 37 34 31 30 30 31 33 34 5f 5f 5f 5f 5f 5f 5f 42 5f 5f 0d 0a
104  *   900uF (main)
105  *
106  * For more details see Conrad's summary document and PeakTech's manual:
107  * http://www.produktinfo.conrad.com/datenblaetter/100000-124999/121064-da-01-en-Schnittstellenbeschr_LCR_4080_Handmessg.pdf
108  * http://peaktech.de/productdetail/kategorie/lcr-messer/produkt/p-2165.html?file=tl_files/downloads/2001%20-%203000/PeakTech_2165_USB.pdf
109  *
110  * TODO
111  * - Check response lengths. Are line terminators involved during setup?
112  * - Check parity. Does FT232R not handle parity correctly? Neither 7e1 (as
113  *   documented) nor 7o1 (for fun) worked. 8n1 provided data but contained
114  *   garbage (LCR driver needs to strip off the parity bit?).
115  * - Determine whether the D and Q channels are required. It seems that
116  *   every LCR packet has space to provide these values, but we may as well
117  *   get away with just two channels, since users can select D and Q to be
118  *   shown in the secondary display. It's yet uncertain whether the D and Q
119  *   values in the packets are meaningful when the meter is not in the D/Q
120  *   measurement mode.
121  */
122
123 /*
124  * Supported output frequencies and equivalent circuit models. A helper
125  * for the packet parser (accepting a "code" for the property, regardless
126  * of its position in the LCR packet), and a list for capability queries.
127  * Concentrated in a single spot to remain aware duing maintenance.
128  */
129
130 static const double frequencies[] = {
131         SR_HZ(120), SR_KHZ(1),
132 };
133
134 static uint64_t get_frequency(char code)
135 {
136         switch (code) {
137         case 'A': return SR_KHZ(1);
138         case 'B': return SR_HZ(120);
139         default: return 0;
140         }
141 }
142
143 enum equiv_model { MODEL_PAR, MODEL_SER, MODEL_NONE, };
144
145 static const char *const circuit_models[] = {
146         "PARALLEL", "SERIES", "NONE",
147 };
148
149 static enum equiv_model get_equiv_model(char lcr_code, char model_code)
150 {
151         switch (lcr_code) {
152         case 'L': /* EMPTY */ break;
153         case 'C': /* EMPTY */ break;
154         case 'R': return MODEL_NONE;
155         default: return MODEL_NONE;
156         }
157         switch (model_code) {
158         case 'P': return MODEL_PAR;
159         case 'S': return MODEL_SER;
160         default: return MODEL_NONE;
161         }
162 }
163
164 static const char *get_equiv_model_text(enum equiv_model model)
165 {
166         return circuit_models[model];
167 }
168
169 /*
170  * Packet parse routine and its helpers. Depending on the specific layout
171  * of the meter's packet which communicates measurement results. Some of
172  * them are also used outside of strict packet parsing for value extraction.
173  */
174
175 static uint64_t parse_freq(const uint8_t *pkt)
176 {
177         return get_frequency(pkt[2]);
178 }
179
180 static const char *parse_model(const uint8_t *pkt)
181 {
182         return get_equiv_model_text(get_equiv_model(pkt[0], pkt[3]));
183 }
184
185 static float parse_number(const uint8_t *digits, size_t length)
186 {
187         char value_text[8];
188         float number;
189         int ret;
190
191         memcpy(value_text, digits, length);
192         value_text[length] = '\0';
193         ret = sr_atof_ascii(value_text, &number);
194
195         return (ret == SR_OK) ? number : 0;
196 }
197
198 /*
199  * Conrad's protocol description suggests that:
200  * - The main display's LCR selection, output frequency, and range
201  *   result in an Rs value in the 100R to 100k range, in addition to
202  *   the main display's scale for the value.
203  * - The secondary display's DQR selection, the above determined Rs
204  *   value, and range result in the value's scale.
205  * - The D and Q values' range seems to follow the secondary display's
206  *   logic.
207  */
208
209 enum lcr_kind { LCR_NONE, LCR_IS_L, LCR_IS_C, LCR_IS_R, };
210 enum dqr_kind { DQR_NONE, DQR_IS_D, DQR_IS_Q, DQR_IS_R, };
211
212 static int get_main_scale_rs(int *digits, int *rs,
213         uint8_t range, enum lcr_kind lcr, uint64_t freq)
214 {
215         /*
216          * Scaling factors for values. Digits count for 20000 full scale.
217          * Full scale values for different modes are:
218          *   R: 20R, 200R, 2k, 20k, 200k, 2M, 10M
219          *   L 1kHz: 2mH, 20mH, 200mH, 2H, 20H, 200H, 1000H
220          *   L 120Hz: 20mH, 200mH, 2H, 20H, 200H, 2kH, 10kH
221          *   C 1kHz: 2nF, 20nF, 200nF, 2uF, 20uF, 200uF, 2mF
222          *   C 120Hz: 20nF, 200nF, 2unF, 20uF, 200uF, 2muF, 20mF
223          */
224         static const int dig_r[] = { -3, -2, -1, +0, +1, +2, +3, };
225         static const int dig_l_1k[] = { -7, -6, -5, -4, -3, -2, -1, };
226         static const int dig_l_120[] = { -6, -5, -4, -3, -2, -1, 0, };
227         static const int dig_c_1k[] = { -13, -12, -11, -10, -9, -8, -7, };
228         static const int dig_c_120[] = { -12, -11, -10, -9, -8, -7, -6, };
229         /*
230          * Rs values for the scale, depending on LCR mode.
231          * Values for R/L: 100R, 100R, 100R, 1k, 10k, 100k, 100k
232          * Values for C: 100k, 100k, 10k, 1k, 100R, 100R, 100R
233          */
234         static const int rs_r_l[] = {
235                 100, 100, 100, 1000, 10000, 100000, 100000,
236         };
237         static const int rs_c[] = {
238                 100000, 100000, 10000, 1000, 100, 100, 100,
239         };
240
241         const int *digits_table, *rs_table;
242
243         /* The 'range' input value is only valid between 0..6. */
244         if (range > 6)
245                 return SR_ERR_DATA;
246
247         if (lcr == LCR_IS_R) {
248                 digits_table = dig_r;
249                 rs_table = rs_r_l;
250         } else if (lcr == LCR_IS_L && freq == SR_KHZ(1)) {
251                 digits_table = dig_l_1k;
252                 rs_table = rs_r_l;
253         } else if (lcr == LCR_IS_L && freq == SR_HZ(120)) {
254                 digits_table = dig_l_120;
255                 rs_table = rs_r_l;
256         } else if (lcr == LCR_IS_C && freq == SR_KHZ(1)) {
257                 digits_table = dig_c_1k;
258                 rs_table = rs_c;
259         } else if (lcr == LCR_IS_C && freq == SR_HZ(120)) {
260                 digits_table = dig_c_120;
261                 rs_table = rs_c;
262         } else {
263                 return SR_ERR_DATA;
264         }
265
266         if (digits)
267                 *digits = digits_table[range];
268         if (rs)
269                 *rs = rs_table[range];
270
271         return SR_OK;
272 }
273
274 static int get_sec_scale(int *digits, uint8_t range, enum dqr_kind dqr, int rs)
275 {
276         static const int dig_d_q[] = { 0, -1, -2, -3, -4, 0, };
277         static const int dig_r_100[] = { 0, -2, -1, +0, +1, 0, };
278         static const int dig_r_1k_10k[] = { 0, -2, -1, +0, +1, +2, };
279         static const int dig_r_100k[] = { 0, 0, -1, +0, +1, +2, };
280
281         const int *digits_table;
282
283         /*
284          * Absolute 'range' limits are 1..5, some modes have additional
285          * invalid positions (these get checked below).
286          */
287         if (range < 1 || range > 5)
288                 return SR_ERR_DATA;
289
290         if (dqr == DQR_IS_D || dqr == DQR_IS_Q) {
291                 if (range > 4)
292                         return SR_ERR_DATA;
293                 digits_table = dig_d_q;
294         } else if (dqr == DQR_IS_R && rs == 100) {
295                 if (range > 4)
296                         return SR_ERR_DATA;
297                 digits_table = dig_r_100;
298         } else if (dqr == DQR_IS_R && (rs == 1000 || rs == 10000)) {
299                 digits_table = dig_r_1k_10k;
300         } else if (dqr == DQR_IS_R && rs == 100000) {
301                 if (range < 2)
302                         return SR_ERR_DATA;
303                 digits_table = dig_r_100k;
304         } else {
305                 return SR_ERR_DATA;
306         }
307
308         if (digits)
309                 *digits = digits_table[range];
310
311         return SR_OK;
312 }
313
314 static void parse_measurement(const uint8_t *pkt, float *floatval,
315         struct sr_datafeed_analog *analog, size_t disp_idx)
316 {
317         enum lcr_kind lcr;
318         enum dqr_kind dqr;
319         uint64_t freq;
320         enum equiv_model model;
321         gboolean is_auto, main_ranging, main_ol, sec_ol, d_ol, q_ol;
322         float main_value, sec_value, d_value, q_value;
323         char main_range, sec_range, d_range, q_range;
324         gboolean is_hold, is_relative, has_adapter, is_lowbatt;
325         enum minmax_kind {
326                 MINMAX_MAX, MINMAX_MIN, MINMAX_SPAN,
327                 MINMAX_AVG, MINMAX_CURR, MINMAX_NONE,
328         } minmax;
329         gboolean is_parallel;
330         int mq, mqflags, unit;
331         float value;
332         int digits, exponent;
333         gboolean ol, invalid;
334         int ret, rs, main_digits, sec_digits, d_digits, q_digits;
335         int main_invalid, sec_invalid, d_invalid, q_invalid;
336
337         /* Prepare void return values for error paths. */
338         analog->meaning->mq = 0;
339         analog->meaning->mqflags = 0;
340         if (disp_idx >= VC4080_CHANNEL_COUNT)
341                 return;
342
343         /*
344          * The interpretation of secondary displays may depend not only
345          * on the meter's status (indicator flags), but also on the main
346          * display's current value (ranges, scaling). Unconditionally
347          * inspect most of the packet's content, regardless of which
348          * display we are supposed to extract the value for in this
349          * invocation.
350          *
351          * While we are converting the input text, check a few "fatal"
352          * conditions early, cease further packet inspection when the
353          * value is unstable or not yet available, or when the meter's
354          * current mode/function is not supported by this LCR parser.
355          */
356         switch (pkt[0]) {
357         case 'L': lcr = LCR_IS_L; break;
358         case 'R': lcr = LCR_IS_R; break;
359         case 'C': lcr = LCR_IS_C; break;
360         default: return;
361         }
362         switch (pkt[1]) {
363         case 'D': dqr = DQR_IS_D; break;
364         case 'Q': dqr = DQR_IS_Q; break;
365         case 'R': dqr = DQR_IS_R; break;
366         case '_': dqr = DQR_NONE; break; /* Can be valid, like in R mode. */
367         default: return;
368         }
369         freq = get_frequency(pkt[2]);
370         model = get_equiv_model(pkt[0], pkt[3]);
371         is_auto = pkt[4] == 'A';
372         main_ranging = pkt[5] == '8';
373         if (main_ranging)       /* Switching ranges. */
374                 return;
375         main_ol = pkt[5] == '9';
376         main_value = parse_number(&pkt[5], 5);
377         main_range = pkt[10];
378         if (main_range < '0' || main_range > '6')
379                 main_range = '9';
380         main_range -= '0';
381         /*
382          * Contrary to the documentation, there have been valid four-digit
383          * values in the secondary display which start with '9'. Let's not
384          * consider these as overflown. Out-of-range 'range' specs for the
385          * secondary display will also invalidate these values.
386          */
387         sec_ol = 0 && pkt[11] == '9';
388         sec_value = parse_number(&pkt[11], 4);
389         sec_range = pkt[15];
390         if (sec_range < '0' || sec_range > '6')
391                 sec_range = '9';
392         sec_range -= '0';
393         d_ol = pkt[17] == '9';
394         d_value = parse_number(&pkt[17], 4);
395         d_range = pkt[21];
396         if (d_range < '0' || d_range > '6')
397                 d_range = '9';
398         d_range -= '0';
399         q_ol = pkt[22] == '9';
400         q_value = parse_number(&pkt[22], 4);
401         q_range = pkt[26];
402         if (q_range < '0' || q_range > '6')
403                 q_range = '9';
404         d_range -= '0';
405         switch (pkt[27]) {
406         case 'S': return;       /* Setup mode. Not supported. */
407         case '_': /* EMPTY */ break;
408         default: return;        /* Unknown. */
409         }
410         is_hold = pkt[29] == 'H';
411         switch (pkt[30]) {      /* Min/max modes. */
412         case 'R': minmax = MINMAX_CURR; break;  /* Live reading. */
413         case 'M': minmax = MINMAX_MAX; break;
414         case 'I': minmax = MINMAX_MIN; break;
415         case 'X': minmax = MINMAX_SPAN; break;  /* "Max - min" difference. */
416         case 'A': minmax = MINMAX_AVG; break;
417         case '_': minmax = MINMAX_NONE; break;
418         default: return;        /* Unknown. */
419         }
420         if (minmax == MINMAX_SPAN)      /* Not supported. */
421                 return;
422         if (minmax == MINMAX_CURR)      /* Normalize. */
423                 minmax = MINMAX_NONE;
424         switch (pkt[31]) {
425         case 'R': is_relative = TRUE; break;
426         case 'S': return;       /* Relative setup. Not supported. */
427                                 /* TODO Is this SR_MQFLAG_REFERENCE? */
428         case '_': is_relative = FALSE; break;
429         default: return;        /* Unknown. */
430         }
431         if (pkt[32] != '_')     /* Limits. Not supported. */
432                 return;
433         if (pkt[33] != '_')     /* Tolerance. Not supported. */
434                 return;
435         has_adapter = pkt[35] == 'A';
436         is_lowbatt = pkt[36] == 'B';
437
438         /*
439          * Always need to inspect the main display's properties, to
440          * determine how to interpret the secondary displays.
441          */
442         rs = main_digits = sec_digits = d_digits = q_digits = 0;
443         main_invalid = sec_invalid = d_invalid = q_invalid = 0;
444         ret = get_main_scale_rs(&main_digits, &rs, main_range, lcr, freq);
445         if (ret != SR_OK)
446                 main_invalid = 1;
447         ret = get_sec_scale(&sec_digits, sec_range, dqr, rs);
448         if (ret != SR_OK)
449                 sec_invalid = 1;
450         ret = get_sec_scale(&d_digits, d_range, dqr, rs);
451         if (ret != SR_OK)
452                 d_invalid = 1;
453         ret = get_sec_scale(&q_digits, q_range, dqr, rs);
454         if (ret != SR_OK)
455                 q_invalid = 1;
456
457         /* Determine the measurement value and its units. Apply scaling. */
458         is_parallel = model == MODEL_PAR;
459         mq = 0;
460         mqflags = 0;
461         unit = 0;
462         switch (disp_idx) {
463         case VC4080_DISPLAY_PRIMARY:
464                 invalid = main_invalid;
465                 if (invalid)
466                         break;
467                 if (lcr == LCR_IS_L) {
468                         mq = is_parallel
469                                 ? SR_MQ_PARALLEL_INDUCTANCE
470                                 : SR_MQ_SERIES_INDUCTANCE;
471                         unit = SR_UNIT_HENRY;
472                 } else if (lcr == LCR_IS_C) {
473                         mq = is_parallel
474                                 ? SR_MQ_PARALLEL_CAPACITANCE
475                                 : SR_MQ_SERIES_CAPACITANCE;
476                         unit = SR_UNIT_FARAD;
477                 } else if (lcr == LCR_IS_R) {
478                         mq = is_parallel
479                                 ? SR_MQ_PARALLEL_RESISTANCE
480                                 : SR_MQ_SERIES_RESISTANCE;
481                         unit = SR_UNIT_OHM;
482                 }
483                 value = main_value;
484                 ol = main_ol;
485                 digits = 0;
486                 exponent = main_digits;
487                 break;
488         case VC4080_DISPLAY_SECONDARY:
489                 invalid = sec_invalid;
490                 if (invalid)
491                         break;
492                 if (dqr == DQR_IS_D) {
493                         mq = SR_MQ_DISSIPATION_FACTOR;
494                         unit = SR_UNIT_UNITLESS;
495                 } else if (dqr == DQR_IS_Q) {
496                         mq = SR_MQ_QUALITY_FACTOR;
497                         unit = SR_UNIT_UNITLESS;
498                 } else if (dqr == DQR_IS_R) {
499                         mq = SR_MQ_RESISTANCE;
500                         unit = SR_UNIT_OHM;
501                 }
502                 value = sec_value;
503                 ol = sec_ol;
504                 digits = 0;
505                 exponent = sec_digits;
506                 break;
507 #if VC4080_WITH_DQ_CHANS
508         case VC4080_DISPLAY_D_VALUE:
509                 invalid = d_invalid;
510                 if (invalid)
511                         break;
512                 mq = SR_MQ_DISSIPATION_FACTOR;
513                 unit = SR_UNIT_UNITLESS;
514                 value = d_value;
515                 ol = d_ol;
516                 digits = 4;
517                 exponent = d_digits;
518                 break;
519         case VC4080_DISPLAY_Q_VALUE:
520                 invalid = q_invalid;
521                 if (invalid)
522                         break;
523                 mq = SR_MQ_QUALITY_FACTOR;
524                 unit = SR_UNIT_UNITLESS;
525                 value = q_value;
526                 ol = q_ol;
527                 digits = 4;
528                 exponent = q_digits;
529                 break;
530 #else
531         (void)d_invalid;
532         (void)d_value;
533         (void)d_ol;
534         (void)d_digits;
535         (void)q_invalid;
536         (void)q_value;
537         (void)q_ol;
538         (void)q_digits;
539 #endif
540         default:
541                 /* ShouldNotHappen(TM). Won't harm either. Silences warnings. */
542                 return;
543         }
544         if (invalid)
545                 return;
546         if (is_auto)
547                 mqflags |= SR_MQFLAG_AUTORANGE;
548         if (is_hold)
549                 mqflags |= SR_MQFLAG_HOLD;
550         if (is_relative)
551                 mqflags |= SR_MQFLAG_RELATIVE;
552         if (has_adapter)
553                 mqflags |= SR_MQFLAG_FOUR_WIRE;
554         switch (minmax) {
555         case MINMAX_MAX:
556                 mqflags |= SR_MQFLAG_MAX;
557                 break;
558         case MINMAX_MIN:
559                 mqflags |= SR_MQFLAG_MIN;
560                 break;
561         case MINMAX_SPAN:
562                 mqflags |= SR_MQFLAG_MAX | SR_MQFLAG_RELATIVE;
563                 break;
564         case MINMAX_AVG:
565                 mqflags |= SR_MQFLAG_AVG;
566                 break;
567         case MINMAX_CURR:
568         case MINMAX_NONE:
569         default:
570                 /* EMPTY */
571                 break;
572         }
573
574         /* "Commit" the resulting value. */
575         if (ol) {
576                 value = INFINITY;
577         } else {
578                 value *= powf(10, exponent);
579                 digits -= exponent;
580         }
581         *floatval = value;
582         analog->meaning->mq = mq;
583         analog->meaning->mqflags = mqflags;
584         analog->meaning->unit = unit;
585         analog->encoding->digits = digits;
586         analog->spec->spec_digits = digits;
587
588         /* Low battery is rather severe, the measurement could be invalid. */
589         if (is_lowbatt)
590                 sr_warn("Low battery.");
591 }
592
593 /*
594  * Workaround for cables' improper(?) parity handling.
595  * TODO Should this move to serial-lcr or even common libsigrok code?
596  *
597  * Implementor's note: Serial communication is documented to be 1200/7e1.
598  * But practial setups with the shipped FT232R cable received no response
599  * at all with these settings. The 8n1 configuration resulted in responses
600  * while the LCR meter's packet parser then needs to strip the parity bits.
601  *
602  * Let's run this slightly modified setup for now, until more cables and
603  * compatible devices got observed and the proper solution gets determined.
604  * This cheat lets us receive measurement data right now. Stripping the
605  * parity bits off the packet bytes here in the parser is an idempotent
606  * operation that happens to work during stream detect as well as in the
607  * acquisition loop. It helps in the 8n1 configuration, and keeps working
608  * transparently in the 7e1 configuration, too. No harm is done, and the
609  * initial device support is achieved.
610  *
611  * By coincidence, the 'N' command which requests the next measurement
612  * value happens to conform with the 7e1 frame format (0b_0100_1110
613  * byte value). When the SETUP commands are supposed to work with this
614  * LCR meter as well, then the serial-lcr driver's TX data and RX data
615  * probably needs to pass LCR chip specific transformation routines,
616  * if the above mentioned parity support in serial cables issue has not
617  * yet been resolved.
618  */
619
620 static void strip_parity_bit(uint8_t *p, size_t l)
621 {
622         while (l--)
623                 *p++ &= ~0x80;
624 }
625
626 /* LCR packet parser's public API. */
627
628 SR_PRIV const char *vc4080_channel_formats[VC4080_CHANNEL_COUNT] = {
629         "P1", "P2",
630 #if VC4080_WITH_DQ_CHANS
631         "D", "Q",
632 #endif
633 };
634
635 SR_PRIV int vc4080_packet_request(struct sr_serial_dev_inst *serial)
636 {
637         static const char *command = "N";
638
639         serial_write_blocking(serial, command, strlen(command), 0);
640
641         return SR_OK;
642 }
643
644 SR_PRIV gboolean vc4080_packet_valid(const uint8_t *pkt)
645 {
646         /* Workaround for funny serial cables. */
647         strip_parity_bit((void *)pkt, VC4080_PACKET_SIZE);
648
649         /* Fixed CR/LF terminator. */
650         if (pkt[37] != '\r' || pkt[38] != '\n')
651                 return FALSE;
652
653         return TRUE;
654 }
655
656 SR_PRIV int vc4080_packet_parse(const uint8_t *pkt, float *val,
657         struct sr_datafeed_analog *analog, void *info)
658 {
659         struct lcr_parse_info *parse_info;
660
661         /* Workaround for funny serial cables. */
662         strip_parity_bit((void *)pkt, VC4080_PACKET_SIZE);
663
664         parse_info = info;
665         if (!parse_info->ch_idx) {
666                 parse_info->output_freq = parse_freq(pkt);
667                 parse_info->circuit_model = parse_model(pkt);
668         }
669         if (val && analog)
670                 parse_measurement(pkt, val, analog, parse_info->ch_idx);
671
672         return SR_OK;
673 }
674
675 /*
676  * These are the get/set/list routines for the _chip_ specific parameters,
677  * the _device_ driver resides in src/hardware/serial-lcr/ instead.
678  */
679
680 SR_PRIV int vc4080_config_list(uint32_t key, GVariant **data,
681         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
682 {
683
684         (void)sdi;
685         (void)cg;
686
687         switch (key) {
688         case SR_CONF_OUTPUT_FREQUENCY:
689                 *data = g_variant_new_fixed_array(G_VARIANT_TYPE_DOUBLE,
690                         ARRAY_AND_SIZE(frequencies), sizeof(frequencies[0]));
691                 return SR_OK;
692         case SR_CONF_EQUIV_CIRCUIT_MODEL:
693                 *data = g_variant_new_strv(ARRAY_AND_SIZE(circuit_models));
694                 return SR_OK;
695         default:
696                 return SR_ERR_NA;
697         }
698         /* UNREACH */
699 }
700
701 #endif