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