]> sigrok.org Git - libsigrok.git/blob - hardware/colead-slm/protocol.c
d8cd2159136dcc7bcb6f29d97fadfbef8132439f
[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.num_samples = 1;
79         analog.data = &fvalue;
80
81         /* High nibble should only have 0x01 or 0x02. */
82         mode = (devc->buf[2] >> 4) & 0x0f;
83         if (mode == 0x02)
84                 analog.mqflags |= SR_MQFLAG_HOLD;
85         else if (mode != 0x01) {
86                 sr_dbg("unknown measurement mode 0x%.2x", mode);
87                 return;
88         }
89
90         /* Low nibble has 14 combinations of direct/long-term average,
91          * time scale of that average, frequency weighting, and time
92          * weighting. */
93         mode = devc->buf[2] & 0x0f;
94         switch (mode) {
95                 case 0x0:
96                         analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
97                                         | SR_MQFLAG_SPL_TIME_WEIGHT_F;
98                         break;
99                 case 0x1:
100                         analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A \
101                                         | SR_MQFLAG_SPL_TIME_WEIGHT_S;
102                         break;
103                 case 0x2:
104                         analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
105                                         | SR_MQFLAG_SPL_TIME_WEIGHT_F;
106                         break;
107                 case 0x3:
108                         analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C \
109                                         | SR_MQFLAG_SPL_TIME_WEIGHT_S;
110                         break;
111                 case 0x4:
112                         analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
113                                         | SR_MQFLAG_SPL_TIME_WEIGHT_F;
114                         break;
115                 case 0x5:
116                         analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT \
117                                         | SR_MQFLAG_SPL_TIME_WEIGHT_S;
118                         break;
119                 case 0x6:
120                         analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
121                                         | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
122                                         | SR_MQFLAG_SPL_TIME_WEIGHT_F;
123                         break;
124                 case 0x7:
125                         analog.mqflags |= SR_MQFLAG_SPL_PCT_OVER_ALARM \
126                                         | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
127                                         | SR_MQFLAG_SPL_TIME_WEIGHT_S;
128                         break;
129                 case 0x8:
130                         /* 10-second mean, but we don't have MQ flags to express it. */
131                         analog.mqflags |= SR_MQFLAG_SPL_LAT \
132                                         | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
133                                         | SR_MQFLAG_SPL_TIME_WEIGHT_F;
134                         break;
135                 case 0x9:
136                         /* Mean over a time period between 11 seconds and 24 hours.
137                          * Which is so silly that there's no point in expressing
138                          * either this or the previous case.  */
139                         analog.mqflags |= SR_MQFLAG_SPL_LAT \
140                                         | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
141                                         | SR_MQFLAG_SPL_TIME_WEIGHT_F;
142                         break;
143                 case 0xa:
144                         /* 10-second mean. */
145                         analog.mqflags |= SR_MQFLAG_SPL_LAT \
146                                         | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
147                                         | SR_MQFLAG_SPL_TIME_WEIGHT_S;
148                         break;
149                 case 0xb:
150                         /* Mean over a time period between 11 seconds and 24 hours. */
151                         analog.mqflags |= SR_MQFLAG_SPL_LAT \
152                                         | SR_MQFLAG_SPL_FREQ_WEIGHT_A \
153                                         | SR_MQFLAG_SPL_TIME_WEIGHT_S;
154                         break;
155                 case 0xc:
156                         /* Internal calibration on 1kHz sine at 94dB, not useful
157                          * to anything but the device. */
158                         analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
159                         break;
160                 case 0xd:
161                         /* Internal calibration on 1kHz sine at 94dB, not useful
162                          * to anything but the device. */
163                         analog.mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_FLAT;
164                         break;
165                 default:
166                         sr_dbg("unknown configuration 0x%.2x", mode);
167                         return;
168                         break;
169         }
170
171         packet.type = SR_DF_ANALOG;
172         packet.payload = &analog;
173         sr_session_send(devc->cb_data, &packet);
174
175         devc->num_samples++;
176
177 }
178
179 SR_PRIV int colead_slm_receive_data(int fd, int revents, void *cb_data)
180 {
181         const struct sr_dev_inst *sdi;
182         struct dev_context *devc;
183         int len;
184         char buf[128];
185
186         (void)fd;
187
188         if (!(sdi = cb_data))
189                 return TRUE;
190
191         if (!(devc = sdi->priv))
192                 return TRUE;
193
194         if (revents != G_IO_IN)
195                 /* Timeout event. */
196                 return TRUE;
197
198         if (devc->state == IDLE) {
199                 if (serial_read(devc->serial, buf, 128) != 1 || buf[0] != 0x10)
200                         /* Nothing there, or caught the tail end of a previous packet,
201                          * or some garbage. Unless it's a single "data ready" byte,
202                          * we don't want it. */
203                         return TRUE;
204                 /* Got 0x10, "measurement ready". */
205                 if (serial_write(devc->serial, "\x20", 1) == -1)
206                         sr_err("unable to send command: %s", strerror(errno));
207                 else {
208                         devc->state = COMMAND_SENT;
209                         devc->buflen = 0;
210                 }
211         } else {
212                 len = serial_read(devc->serial, devc->buf + devc->buflen,
213                                 10 - devc->buflen);
214                 if (len < 1)
215                         return TRUE;
216                 devc->buflen += len;
217                 if (devc->buflen > 10) {
218                         sr_dbg("buffer overrun");
219                         devc->state = IDLE;
220                         return TRUE;
221                 }
222                 if (devc->buflen == 10) {
223                         /* If we got here, we're sure the device sent a "data ready"
224                          * notification, we asked for data, and got it. */
225                         process_packet(sdi);
226                         devc->state = IDLE;
227                 }
228         }
229
230         return TRUE;
231 }
232