]>
Commit | Line | Data |
---|---|---|
e5d953b5 UH |
1 | /* |
2 | * This file is part of the libsigrok project. | |
3 | * | |
4 | * Copyright (C) 2015 Uwe Hermann <uwe@hermann-uwe.de> | |
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 2 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, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /* | |
22 | * KERN scale protocol parser. | |
23 | */ | |
24 | ||
6ec6c43b | 25 | #include <config.h> |
e5d953b5 UH |
26 | #include <string.h> |
27 | #include <ctype.h> | |
28 | #include <math.h> | |
29 | #include <glib.h> | |
30 | #include <libsigrok/libsigrok.h> | |
31 | #include "libsigrok-internal.h" | |
32 | ||
33 | #define LOG_PREFIX "kern" | |
34 | ||
35 | static int get_buflen(const uint8_t *buf) | |
36 | { | |
37 | /* Find out whether it's a 14-byte or 15-byte packet. */ | |
38 | if (buf[12] == '\r' && buf[13] == '\n') | |
39 | return 14; | |
40 | else if (buf[13] == '\r' && buf[14] == '\n') | |
41 | return 15; | |
42 | else | |
43 | return -1; | |
44 | } | |
45 | ||
46 | static int parse_value(const uint8_t *buf, float *result, | |
0b820550 | 47 | int *digits, const struct kern_info *info) |
e5d953b5 | 48 | { |
0b820550 | 49 | char *strval, *ptrdot; |
e5d953b5 UH |
50 | float floatval; |
51 | int s2, len; | |
52 | ||
53 | s2 = (info->buflen == 14) ? 11 : 12; | |
54 | len = (info->buflen == 14) ? 8 : 9; | |
55 | ||
56 | if (buf[s2] == 'E') { | |
57 | /* Display: "o-Err" or "u-Err", but protocol only has 'E'. */ | |
58 | sr_spew("Over/under limit."); | |
59 | *result = INFINITY; | |
60 | return SR_OK; | |
61 | } | |
62 | ||
63 | strval = g_strndup((const char *)buf, len); | |
64 | floatval = g_ascii_strtod(strval, NULL); | |
0b820550 AJ |
65 | ptrdot = strchr(strval, '.'); |
66 | if (ptrdot) | |
67 | *digits = len - (ptrdot - strval + 1); | |
e5d953b5 UH |
68 | g_free(strval); |
69 | *result = floatval; | |
70 | ||
71 | return SR_OK; | |
72 | } | |
73 | ||
74 | static void parse_flags(const uint8_t *buf, struct kern_info *info) | |
75 | { | |
76 | int u1, u2, s2; | |
77 | ||
78 | u1 = (info->buflen == 14) ? 8 : 9; | |
79 | u2 = (info->buflen == 14) ? 9 : 10; | |
80 | s2 = (info->buflen == 14) ? 11 : 12; | |
81 | ||
82 | /* Bytes U1, U2: Unit */ | |
83 | info->is_gram = (buf[u1] == ' ' && buf[u2] == 'G'); | |
84 | info->is_carat = (buf[u1] == 'C' && buf[u2] == 'T'); | |
85 | info->is_ounce = (buf[u1] == 'O' && buf[u2] == 'Z'); | |
86 | info->is_pound = (buf[u1] == 'L' && buf[u2] == 'B'); | |
87 | info->is_troy_ounce = (buf[u1] == 'O' && buf[u2] == 'T'); | |
88 | info->is_pennyweight = (buf[u1] == 'D' && buf[u2] == 'W'); | |
89 | info->is_grain = (buf[u1] == 'G' && buf[u2] == 'R'); | |
90 | info->is_tael = (buf[u1] == 'T' && buf[u2] == 'L'); | |
91 | info->is_momme = (buf[u1] == 'M' && buf[u2] == 'O'); | |
92 | info->is_tola = (buf[u1] == 't' && buf[u2] == 'o'); | |
93 | info->is_percentage = (buf[u1] == ' ' && buf[u2] == '%'); | |
94 | info->is_piece = (buf[u1] == 'P' && buf[u2] == 'C'); | |
95 | ||
96 | /* | |
97 | * Note: The display can show 3 different variants for Tael: | |
98 | * "Hong Kong", "Singapore, Malaysia", and "Taiwan". However, | |
99 | * in the protocol only one Tael value ('T', 'L') is used, thus | |
100 | * we cannot distinguish between them. | |
101 | */ | |
102 | ||
103 | /* Byte S1: Result / data type (currently unused) */ | |
104 | ||
105 | /* Byte S2: Status of the data */ | |
106 | info->is_unstable = (buf[s2] == 'U'); | |
107 | info->is_stable = (buf[s2] == 'S'); | |
108 | info->is_error = (buf[s2] == 'E'); | |
109 | /* Space: no special status. */ | |
110 | ||
111 | /* Byte CR: Always '\r' (carriage return, 0x0d, 13) */ | |
112 | ||
113 | /* Byte LF: Always '\n' (newline, 0x0a, 10) */ | |
114 | } | |
115 | ||
c6d52747 | 116 | static void handle_flags(struct sr_datafeed_analog *analog, float *floatval, |
e5d953b5 UH |
117 | const struct kern_info *info) |
118 | { | |
119 | (void)floatval; | |
120 | ||
121 | /* Measured quantity: mass. */ | |
c6d52747 | 122 | analog->meaning->mq = SR_MQ_MASS; |
e5d953b5 UH |
123 | |
124 | /* Unit */ | |
125 | if (info->is_gram) | |
c6d52747 | 126 | analog->meaning->unit = SR_UNIT_GRAM; |
e5d953b5 | 127 | if (info->is_carat) |
c6d52747 | 128 | analog->meaning->unit = SR_UNIT_CARAT; |
e5d953b5 | 129 | if (info->is_ounce) |
c6d52747 | 130 | analog->meaning->unit = SR_UNIT_OUNCE; |
e5d953b5 | 131 | if (info->is_pound) |
c6d52747 | 132 | analog->meaning->unit = SR_UNIT_POUND; |
e5d953b5 | 133 | if (info->is_troy_ounce) |
c6d52747 | 134 | analog->meaning->unit = SR_UNIT_TROY_OUNCE; |
e5d953b5 | 135 | if (info->is_pennyweight) |
c6d52747 | 136 | analog->meaning->unit = SR_UNIT_PENNYWEIGHT; |
e5d953b5 | 137 | if (info->is_grain) |
c6d52747 | 138 | analog->meaning->unit = SR_UNIT_GRAIN; |
e5d953b5 | 139 | if (info->is_tael) |
c6d52747 | 140 | analog->meaning->unit = SR_UNIT_TAEL; |
e5d953b5 | 141 | if (info->is_momme) |
c6d52747 | 142 | analog->meaning->unit = SR_UNIT_MOMME; |
e5d953b5 | 143 | if (info->is_tola) |
c6d52747 | 144 | analog->meaning->unit = SR_UNIT_TOLA; |
e5d953b5 | 145 | if (info->is_percentage) |
c6d52747 | 146 | analog->meaning->unit = SR_UNIT_PERCENTAGE; |
e5d953b5 | 147 | if (info->is_piece) |
c6d52747 | 148 | analog->meaning->unit = SR_UNIT_PIECE; |
e5d953b5 UH |
149 | |
150 | /* Measurement related flags */ | |
151 | if (info->is_unstable) | |
c6d52747 | 152 | analog->meaning->mqflags |= SR_MQFLAG_UNSTABLE; |
e5d953b5 UH |
153 | } |
154 | ||
155 | SR_PRIV gboolean sr_kern_packet_valid(const uint8_t *buf) | |
156 | { | |
157 | int buflen, s1, s2, cr, lf; | |
158 | ||
159 | if ((buflen = get_buflen(buf)) < 0) | |
160 | return FALSE; | |
161 | ||
162 | s1 = (buflen == 14) ? 10 : 11; | |
163 | s2 = (buflen == 14) ? 11 : 12; | |
164 | cr = (buflen == 14) ? 12 : 13; | |
165 | lf = (buflen == 14) ? 13 : 14; | |
166 | ||
167 | /* Byte 0: Sign (must be '+' or '-' or ' '). */ | |
168 | if (buf[0] != '+' && buf[0] != '-' && buf[0] != ' ') | |
169 | return FALSE; | |
170 | ||
171 | /* Byte S1: Must be 'L' or 'G' or 'H' or ' '. */ | |
172 | if (buf[s1] != 'L' && buf[s1] != 'G' && buf[s1] != 'H' && buf[s1] != ' ') | |
173 | return FALSE; | |
174 | ||
175 | /* Byte S2: Must be 'U' or 'S' or 'E' or ' '. */ | |
176 | if (buf[s2] != 'U' && buf[s2] != 'S' && buf[s2] != 'E' && buf[s2] != ' ') | |
177 | return FALSE; | |
178 | ||
179 | /* Byte CR: Always '\r' (carriage return, 0x0d, 13) */ | |
180 | /* Byte LF: Always '\n' (newline, 0x0a, 10) */ | |
181 | if (buf[cr] != '\r' || buf[lf] != '\n') | |
182 | return FALSE; | |
183 | ||
184 | return TRUE; | |
185 | } | |
186 | ||
187 | /** | |
188 | * Parse a protocol packet. | |
189 | * | |
190 | * @param buf Buffer containing the protocol packet. Must not be NULL. | |
191 | * @param floatval Pointer to a float variable. That variable will contain the | |
192 | * result value upon parsing success. Must not be NULL. | |
c6d52747 | 193 | * @param analog Pointer to a struct sr_datafeed_analog. The struct will be |
e5d953b5 UH |
194 | * filled with data according to the protocol packet. |
195 | * Must not be NULL. | |
196 | * @param info Pointer to a struct kern_info. The struct will be filled | |
197 | * with data according to the protocol packet. Must not be NULL. | |
198 | * | |
199 | * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the | |
200 | * 'analog' variable contents are undefined and should not be used. | |
201 | */ | |
202 | SR_PRIV int sr_kern_parse(const uint8_t *buf, float *floatval, | |
c6d52747 | 203 | struct sr_datafeed_analog *analog, void *info) |
e5d953b5 | 204 | { |
0b820550 | 205 | int ret, digits = 0; |
e5d953b5 UH |
206 | struct kern_info *info_local; |
207 | ||
208 | info_local = (struct kern_info *)info; | |
209 | ||
210 | info_local->buflen = get_buflen(buf); | |
211 | ||
0b820550 | 212 | if ((ret = parse_value(buf, floatval, &digits, info_local)) != SR_OK) { |
e5d953b5 UH |
213 | sr_dbg("Error parsing value: %d.", ret); |
214 | return ret; | |
215 | } | |
216 | ||
0b820550 AJ |
217 | analog->encoding->digits = digits; |
218 | analog->spec->spec_digits = digits; | |
219 | ||
e5d953b5 UH |
220 | parse_flags(buf, info_local); |
221 | handle_flags(analog, floatval, info_local); | |
222 | ||
223 | return SR_OK; | |
224 | } |