]>
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 | ||
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 | const struct kern_info *info) | |
47 | { | |
48 | char *strval; | |
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 | g_free(strval); | |
65 | *result = floatval; | |
66 | ||
67 | return SR_OK; | |
68 | } | |
69 | ||
70 | static void parse_flags(const uint8_t *buf, struct kern_info *info) | |
71 | { | |
72 | int u1, u2, s2; | |
73 | ||
74 | u1 = (info->buflen == 14) ? 8 : 9; | |
75 | u2 = (info->buflen == 14) ? 9 : 10; | |
76 | s2 = (info->buflen == 14) ? 11 : 12; | |
77 | ||
78 | /* Bytes U1, U2: Unit */ | |
79 | info->is_gram = (buf[u1] == ' ' && buf[u2] == 'G'); | |
80 | info->is_carat = (buf[u1] == 'C' && buf[u2] == 'T'); | |
81 | info->is_ounce = (buf[u1] == 'O' && buf[u2] == 'Z'); | |
82 | info->is_pound = (buf[u1] == 'L' && buf[u2] == 'B'); | |
83 | info->is_troy_ounce = (buf[u1] == 'O' && buf[u2] == 'T'); | |
84 | info->is_pennyweight = (buf[u1] == 'D' && buf[u2] == 'W'); | |
85 | info->is_grain = (buf[u1] == 'G' && buf[u2] == 'R'); | |
86 | info->is_tael = (buf[u1] == 'T' && buf[u2] == 'L'); | |
87 | info->is_momme = (buf[u1] == 'M' && buf[u2] == 'O'); | |
88 | info->is_tola = (buf[u1] == 't' && buf[u2] == 'o'); | |
89 | info->is_percentage = (buf[u1] == ' ' && buf[u2] == '%'); | |
90 | info->is_piece = (buf[u1] == 'P' && buf[u2] == 'C'); | |
91 | ||
92 | /* | |
93 | * Note: The display can show 3 different variants for Tael: | |
94 | * "Hong Kong", "Singapore, Malaysia", and "Taiwan". However, | |
95 | * in the protocol only one Tael value ('T', 'L') is used, thus | |
96 | * we cannot distinguish between them. | |
97 | */ | |
98 | ||
99 | /* Byte S1: Result / data type (currently unused) */ | |
100 | ||
101 | /* Byte S2: Status of the data */ | |
102 | info->is_unstable = (buf[s2] == 'U'); | |
103 | info->is_stable = (buf[s2] == 'S'); | |
104 | info->is_error = (buf[s2] == 'E'); | |
105 | /* Space: no special status. */ | |
106 | ||
107 | /* Byte CR: Always '\r' (carriage return, 0x0d, 13) */ | |
108 | ||
109 | /* Byte LF: Always '\n' (newline, 0x0a, 10) */ | |
110 | } | |
111 | ||
112 | static void handle_flags(struct sr_datafeed_analog *analog, float *floatval, | |
113 | const struct kern_info *info) | |
114 | { | |
115 | (void)floatval; | |
116 | ||
117 | /* Measured quantity: mass. */ | |
118 | analog->mq = SR_MQ_MASS; | |
119 | ||
120 | /* Unit */ | |
121 | if (info->is_gram) | |
122 | analog->unit = SR_UNIT_GRAM; | |
123 | if (info->is_carat) | |
124 | analog->unit = SR_UNIT_CARAT; | |
125 | if (info->is_ounce) | |
126 | analog->unit = SR_UNIT_OUNCE; | |
127 | if (info->is_pound) | |
128 | analog->unit = SR_UNIT_POUND; | |
129 | if (info->is_troy_ounce) | |
130 | analog->unit = SR_UNIT_TROY_OUNCE; | |
131 | if (info->is_pennyweight) | |
132 | analog->unit = SR_UNIT_PENNYWEIGHT; | |
133 | if (info->is_grain) | |
134 | analog->unit = SR_UNIT_GRAIN; | |
135 | if (info->is_tael) | |
136 | analog->unit = SR_UNIT_TAEL; | |
137 | if (info->is_momme) | |
138 | analog->unit = SR_UNIT_MOMME; | |
139 | if (info->is_tola) | |
140 | analog->unit = SR_UNIT_TOLA; | |
141 | if (info->is_percentage) | |
142 | analog->unit = SR_UNIT_PERCENTAGE; | |
143 | if (info->is_piece) | |
144 | analog->unit = SR_UNIT_PIECE; | |
145 | ||
146 | /* Measurement related flags */ | |
147 | if (info->is_unstable) | |
148 | analog->mqflags |= SR_MQFLAG_UNSTABLE; | |
149 | } | |
150 | ||
151 | SR_PRIV gboolean sr_kern_packet_valid(const uint8_t *buf) | |
152 | { | |
153 | int buflen, s1, s2, cr, lf; | |
154 | ||
155 | if ((buflen = get_buflen(buf)) < 0) | |
156 | return FALSE; | |
157 | ||
158 | s1 = (buflen == 14) ? 10 : 11; | |
159 | s2 = (buflen == 14) ? 11 : 12; | |
160 | cr = (buflen == 14) ? 12 : 13; | |
161 | lf = (buflen == 14) ? 13 : 14; | |
162 | ||
163 | /* Byte 0: Sign (must be '+' or '-' or ' '). */ | |
164 | if (buf[0] != '+' && buf[0] != '-' && buf[0] != ' ') | |
165 | return FALSE; | |
166 | ||
167 | /* Byte S1: Must be 'L' or 'G' or 'H' or ' '. */ | |
168 | if (buf[s1] != 'L' && buf[s1] != 'G' && buf[s1] != 'H' && buf[s1] != ' ') | |
169 | return FALSE; | |
170 | ||
171 | /* Byte S2: Must be 'U' or 'S' or 'E' or ' '. */ | |
172 | if (buf[s2] != 'U' && buf[s2] != 'S' && buf[s2] != 'E' && buf[s2] != ' ') | |
173 | return FALSE; | |
174 | ||
175 | /* Byte CR: Always '\r' (carriage return, 0x0d, 13) */ | |
176 | /* Byte LF: Always '\n' (newline, 0x0a, 10) */ | |
177 | if (buf[cr] != '\r' || buf[lf] != '\n') | |
178 | return FALSE; | |
179 | ||
180 | return TRUE; | |
181 | } | |
182 | ||
183 | /** | |
184 | * Parse a protocol packet. | |
185 | * | |
186 | * @param buf Buffer containing the protocol packet. Must not be NULL. | |
187 | * @param floatval Pointer to a float variable. That variable will contain the | |
188 | * result value upon parsing success. Must not be NULL. | |
189 | * @param analog Pointer to a struct sr_datafeed_analog. The struct will be | |
190 | * filled with data according to the protocol packet. | |
191 | * Must not be NULL. | |
192 | * @param info Pointer to a struct kern_info. The struct will be filled | |
193 | * with data according to the protocol packet. Must not be NULL. | |
194 | * | |
195 | * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the | |
196 | * 'analog' variable contents are undefined and should not be used. | |
197 | */ | |
198 | SR_PRIV int sr_kern_parse(const uint8_t *buf, float *floatval, | |
199 | struct sr_datafeed_analog *analog, void *info) | |
200 | { | |
201 | int ret; | |
202 | struct kern_info *info_local; | |
203 | ||
204 | info_local = (struct kern_info *)info; | |
205 | ||
206 | info_local->buflen = get_buflen(buf); | |
207 | ||
208 | if ((ret = parse_value(buf, floatval, info_local)) != SR_OK) { | |
209 | sr_dbg("Error parsing value: %d.", ret); | |
210 | return ret; | |
211 | } | |
212 | ||
213 | parse_flags(buf, info_local); | |
214 | handle_flags(analog, floatval, info_local); | |
215 | ||
216 | return SR_OK; | |
217 | } |