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