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