]> sigrok.org Git - libsigrok.git/blob - src/hardware/colead-slm/protocol.c
2f6957abe2d0170770eb86694812abcc566090f2
[libsigrok.git] / src / hardware / colead-slm / protocol.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
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 <stdlib.h>
22 #include <glib.h>
23 #include <string.h>
24 #include <libsigrok/libsigrok.h>
25 #include "libsigrok-internal.h"
26 #include "protocol.h"
27
28 static void process_packet(const struct sr_dev_inst *sdi)
29 {
30         struct dev_context *devc;
31         struct sr_datafeed_packet packet;
32         struct sr_datafeed_analog analog;
33         struct sr_analog_encoding encoding;
34         struct sr_analog_meaning meaning;
35         struct sr_analog_spec spec;
36         GString *dbg;
37         float fvalue;
38         int checksum, mode, i;
39
40         devc = sdi->priv;
41         if (sr_log_loglevel_get() >= SR_LOG_SPEW) {
42                 dbg = g_string_sized_new(128);
43                 g_string_printf(dbg, "received packet:");
44                 for (i = 0; i < 10; i++)
45                         g_string_append_printf(dbg, " %.2x", (devc->buf)[i]);
46                 sr_spew("%s", dbg->str);
47                 g_string_free(dbg, TRUE);
48         }
49
50         if (devc->buf[0] != 0x08 || devc->buf[1] != 0x04) {
51                 sr_dbg("invalid packet header.");
52                 return;
53         }
54
55         if (devc->buf[8] != 0x01) {
56                 sr_dbg("invalid measurement.");
57                 return;
58         }
59
60         checksum = 0;
61         for (i = 0; i < 9; i++)
62                 checksum += devc->buf[i];
63         if ((checksum & 0xff) != devc->buf[9]) {
64                 sr_dbg("invalid packet checksum.");
65                 return;
66         }
67
68         fvalue = 0.0;
69         for (i = 3; i < 8; i++) {
70                 if (devc->buf[i] > 0x09)
71                         continue;
72                 fvalue *= 10;
73                 fvalue += devc->buf[i];
74         }
75         fvalue /= 10;
76
77         sr_analog_init(&analog, &encoding, &meaning, &spec, 1);
78         analog.meaning->mq = SR_MQ_SOUND_PRESSURE_LEVEL;
79         analog.meaning->unit = SR_UNIT_DECIBEL_SPL;
80         analog.meaning->channels = sdi->channels;
81         analog.num_samples = 1;
82         analog.data = &fvalue;
83
84         /* High nibble should only have 0x01 or 0x02. */
85         mode = (devc->buf[2] >> 4) & 0x0f;
86         if (mode == 0x02)
87                 analog.meaning->mqflags |= SR_MQFLAG_HOLD;
88         else if (mode != 0x01) {
89                 sr_dbg("unknown measurement mode 0x%.2x", mode);
90                 return;
91         }
92
93         /* Low nibble has 14 combinations of direct/long-term average,
94          * time scale of that average, frequency weighting, and time
95          * weighting. */
96         mode = devc->buf[2] & 0x0f;
97         switch (mode) {
98         case 0x0:
99                 analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
100                                 | SR_MQFLAG_SPL_TIME_WEIGHT_F;
101                 break;
102         case 0x1:
103                 analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
104                                 | SR_MQFLAG_SPL_TIME_WEIGHT_S;
105                 break;
106         case 0x2:
107                 analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
108                                 | SR_MQFLAG_SPL_TIME_WEIGHT_F;
109                 break;
110         case 0x3:
111                 analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
112                                 | SR_MQFLAG_SPL_TIME_WEIGHT_S;
113                 break;
114         case 0x4:
115                 analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
116                                 | SR_MQFLAG_SPL_TIME_WEIGHT_F;
117                 break;
118         case 0x5:
119                 analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
120                                 | SR_MQFLAG_SPL_TIME_WEIGHT_S;
121                 break;
122         case 0x6:
123                 analog.meaning->mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
124                                 | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
125                                 | SR_MQFLAG_SPL_TIME_WEIGHT_F;
126                 break;
127         case 0x7:
128                 analog.meaning->mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
129                                 | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
130                                 | SR_MQFLAG_SPL_TIME_WEIGHT_S;
131                 break;
132         case 0x8:
133                 /* 10-second mean, but we don't have MQ flags to express it. */
134                 analog.meaning->mqflags |= SR_MQFLAG_SPL_LAT \
135                                 | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
136                                 | SR_MQFLAG_SPL_TIME_WEIGHT_F;
137                 break;
138         case 0x9:
139                 /* Mean over a time period between 11 seconds and 24 hours.
140                  * Which is so silly that there's no point in expressing
141                  * either this or the previous case. */
142                 analog.meaning->mqflags |= SR_MQFLAG_SPL_LAT \
143                                 | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
144                                 | SR_MQFLAG_SPL_TIME_WEIGHT_F;
145                 break;
146         case 0xa:
147                 /* 10-second mean. */
148                 analog.meaning->mqflags |= SR_MQFLAG_SPL_LAT \
149                                 | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
150                                 | SR_MQFLAG_SPL_TIME_WEIGHT_S;
151                 break;
152         case 0xb:
153                 /* Mean over a time period between 11 seconds and 24 hours. */
154                 analog.meaning->mqflags |= SR_MQFLAG_SPL_LAT \
155                                 | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
156                                 | SR_MQFLAG_SPL_TIME_WEIGHT_S;
157                 break;
158         case 0xc:
159                 /* Internal calibration on 1kHz sine at 94dB, not useful
160                  * to anything but the device. */
161                 analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
162                 break;
163         case 0xd:
164                 /* Internal calibration on 1kHz sine at 94dB, not useful
165                  * to anything but the device. */
166                 analog.meaning->mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
167                 break;
168         default:
169                 sr_dbg("unknown configuration 0x%.2x", mode);
170                 return;
171         }
172
173         packet.type = SR_DF_ANALOG;
174         packet.payload = &analog;
175         sr_session_send(sdi, &packet);
176
177         sr_sw_limits_update_samples_read(&devc->limits, 1);
178
179         if (sr_sw_limits_check(&devc->limits))
180                 sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi);
181 }
182
183 SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
184 {
185         const struct sr_dev_inst *sdi;
186         struct dev_context *devc;
187         struct sr_serial_dev_inst *serial;
188         int delay_ms, len;
189         char buf[128];
190
191         (void)fd;
192
193         if (!(sdi = cb_data))
194                 return TRUE;
195
196         if (!(devc = sdi->priv))
197                 return TRUE;
198
199         if (revents != G_IO_IN)
200                 /* Timeout event. */
201                 return TRUE;
202
203         serial = sdi->conn;
204         if (devc->state == IDLE) {
205                 if (serial_read_nonblocking(serial, buf, 128) != 1 || buf[0] != 0x10)
206                         /* Nothing there, or caught the tail end of a previous packet,
207                          * or some garbage. Unless it's a single "data ready" byte,
208                          * we don't want it. */
209                         return TRUE;
210                 /* Got 0x10, "measurement ready". */
211                 delay_ms = serial_timeout(serial, 1);
212                 if (serial_write_blocking(serial, "\x20", 1, delay_ms) < 1)
213                         sr_err("unable to send command");
214                 else {
215                         devc->state = COMMAND_SENT;
216                         devc->buflen = 0;
217                 }
218         } else {
219                 len = serial_read_nonblocking(serial, devc->buf + devc->buflen,
220                                 10 - devc->buflen);
221                 if (len < 1)
222                         return TRUE;
223                 devc->buflen += len;
224                 if (devc->buflen > 10) {
225                         sr_dbg("buffer overrun");
226                         devc->state = IDLE;
227                         return TRUE;
228                 }
229                 if (devc->buflen == 10) {
230                         /* If we got here, we're sure the device sent a "data ready"
231                          * notification, we asked for data, and got it. */
232                         process_packet(sdi);
233                         devc->state = IDLE;
234                 }
235         }
236
237         return TRUE;
238 }