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