838fbab3a6f6947844a97e692d58515b75093b44
[fw/altos] / ao-tools / ao-cal-freq / ao-cal-freq.c
1 /*
2  * Copyright © 2014 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <err.h>
20 #include <fcntl.h>
21 #include <gelf.h>
22 #include <stdio.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <sysexits.h>
26 #include <unistd.h>
27 #include <getopt.h>
28 #include <string.h>
29 #include <stdbool.h>
30 #include <termios.h>
31 #include <math.h>
32 #include "ao-elf.h"
33 #include "ccdbg.h"
34 #include "cc-usb.h"
35 #include "cc.h"
36 #include "ao-verbose.h"
37
38 static const struct option options[] = {
39         { .name = "tty", .has_arg = 1, .val = 'T' },
40         { .name = "device", .has_arg = 1, .val = 'D' },
41         { .name = "verbose", .has_arg = 0, .val = 'v' },
42         { .name = "output", .has_arg = 1, .val = 'o' },
43         { .name = "nosave", .has_arg = 0, .val = 'n' },
44         { 0, 0, 0, 0},
45 };
46
47 static void usage(char *program)
48 {
49         fprintf(stderr, "usage: %s [--verbose] [--nosave] [--device=<device>] [-tty=<tty>] [--output=<cal-value-file>]\n", program);
50         exit(1);
51 }
52
53 void
54 done(struct cc_usb *cc, int code)
55 {
56         cc_usb_close(cc);
57         exit (code);
58 }
59
60 static int
61 ends_with(char *whole, char *suffix)
62 {
63         int whole_len = strlen(whole);
64         int suffix_len = strlen(suffix);
65
66         if (suffix_len > whole_len)
67                 return 0;
68         return strcmp(whole + whole_len - suffix_len, suffix) == 0;
69 }
70
71 static int
72 starts_with(char *whole, char *prefix)
73 {
74         int whole_len = strlen(whole);
75         int prefix_len = strlen(prefix);
76
77         if (prefix_len > whole_len)
78                 return 0;
79         return strncmp(whole, prefix, prefix_len) == 0;
80 }
81
82 static char **
83 tok(char *line) {
84         char    **strs = malloc (sizeof (char *)), *str;
85         int     n = 0;
86
87         while ((str = strtok(line, " \t"))) {
88                 line = NULL;
89                 strs = realloc(strs, (n + 2) * sizeof (char *));
90                 strs[n] = strdup(str);
91                 n++;
92         }
93         strs[n] = '\0';
94         return strs;
95 }
96
97 static void
98 free_strs(char **strs) {
99         char    *str;
100         int     i;
101
102         for (i = 0; (str = strs[i]) != NULL; i++)
103                 free(str);
104         free(strs);
105 }
106
107 struct flash {
108         struct flash    *next;
109         char            line[512];
110         char            **strs;
111 };
112
113 static struct flash *
114 flash(struct cc_usb *usb)
115 {
116         struct flash    *head = NULL, **tail = &head;
117         cc_usb_printf(usb, "c s\nv\n");
118         for (;;) {
119                 char    line[512];
120                 struct flash    *b;
121
122                 cc_usb_getline(usb, line, sizeof (line));
123                 b = malloc (sizeof (struct flash));
124                 strcpy(b->line, line);
125                 b->strs = tok(line);
126                 b->next = NULL;
127                 *tail = b;
128                 tail = &b->next;
129                 if (strstr(line, "software-version"))
130                         break;
131         }
132         return head;
133 }
134
135 static void
136 free_flash(struct flash *b) {
137         struct flash *n;
138
139         while (b) {
140                 n = b->next;
141                 free_strs(b->strs);
142                 free(b);
143                 b = n;
144         }
145 }
146
147 char **
148 find_flash(struct flash *b, char *word0) {
149         int i;
150         for (;b; b = b->next) {
151                 if (strstr(b->line, word0))
152                         return b->strs;
153         }
154         return NULL;
155 }
156
157 void
158 await_key(void)
159 {
160         struct termios  termios, termios_save;
161         char    buf[512];
162
163         tcgetattr(0, &termios);
164         termios_save = termios;
165         cfmakeraw(&termios);
166         tcsetattr(0, TCSAFLUSH, &termios);
167         read(0, buf, sizeof (buf));
168         tcsetattr(0, TCSAFLUSH, &termios_save);
169 }
170
171 int
172 do_save(struct cc_usb *usb)
173 {
174         int ret = 0;
175
176         printf("Saving calibration to device\n");
177         cc_usb_printf(usb, "c w\nv\n");
178         for (;;) {
179                 char    line[512];
180
181                 cc_usb_getline(usb, line, sizeof (line));
182                 if (strstr(line, "Nothing to save"))
183                         ret = 1;
184                 if (strstr(line, "Saved"))
185                         ret = 1;
186                 if (strstr(line, "software-version"))
187                         break;
188         }
189         if (!ret) {
190                 printf("Calibration save failed\n");
191         }
192         return ret;
193 }
194
195 int
196 do_output(char *output, int cur_cal)
197 {
198         printf ("Saving calibration value to file \"%s\"\n", output);
199
200         FILE    *out = fopen(output, "w");
201         int     ret = 1;
202
203         if (!out) {
204                 perror(output);
205                 return 0;
206         }
207
208         if (fprintf(out, "%d\n", cur_cal) < 0) {
209                 perror("fprintf");
210                 ret = 0;
211         }
212         if (fflush(out) != 0) {
213                 perror("fflush");
214                 ret = 0;
215         }
216         if (fclose(out) != 0) {
217                 perror("fclose");
218                 ret = 0;
219         }
220         return ret;
221 }
222
223 int
224 do_cal(char *tty, int save, char *output)
225 {
226         struct cc_usb *usb = NULL;
227         struct flash    *b;
228         char    line[1024];
229         double  measured_freq;
230         char    **cur_freq_words;
231         char    **cur_cal_words;
232         char    *line_end;
233         int     cur_freq;
234         int     cur_cal;
235         int     new_cal;
236         int     ret = 1;
237         int     changed = 0;
238
239         for(;;) {
240                 usb = cc_usb_open(tty);
241
242                 if (!usb) {
243                         fprintf(stderr, "failed to open device\n");
244                         ret = 0;
245                         break;
246                 }
247
248                 cc_usb_printf(usb, "E 0\n");
249
250                 b = flash(usb);
251
252                 cur_cal_words = find_flash(b, "Radio cal:");
253                 cur_freq_words = find_flash(b, "Frequency:");
254
255                 if (!cur_cal_words || !cur_freq_words) {
256                         fprintf(stderr, "no response\n");
257                         ret = 0;
258                         break;
259                 }
260
261                 cur_cal = atoi(cur_cal_words[2]);
262                 cur_freq = atoi(cur_freq_words[1]);
263
264                 printf ("Current radio calibration %d\n", cur_cal);
265                 printf ("Current radio frequency: %7.3f\n", cur_freq / 1000.0);
266
267                 cc_usb_sync(usb);
268                 cc_usb_printf(usb, "C 1\n");
269                 cc_usb_sync(usb);
270
271                 printf("Generating RF carrier. Please enter measured frequency [enter for done]: ");
272                 fflush(stdout);
273                 fgets(line, sizeof (line) - 1, stdin);
274                 cc_usb_printf(usb, "C 0\n");
275                 cc_usb_sync(usb);
276
277                 measured_freq = strtod(line, &line_end);
278                 if (line_end == line)
279                         break;
280
281                 new_cal = floor ((((double) cur_freq / 1000.0) / measured_freq) * cur_cal + 0.5);
282
283                 if (new_cal == cur_cal) {
284                         printf("Calibration value %d unchanged\n", cur_cal);
285                 } else {
286                         printf ("Setting cal value %d\n", new_cal);
287
288                         cc_usb_printf (usb, "c f %d\n", new_cal);
289                         changed = 1;
290                         cc_usb_sync(usb);
291                 }
292                 cc_usb_close(usb);
293         }
294         if (usb) {
295                 if (ret && save) {
296                         if (changed) {
297                                 if (!do_save(usb))
298                                         ret = 0;
299                         } else {
300                                 printf("Calibration unchanged, not saving\n");
301                         }
302                 }
303                 if (ret && output) {
304                         if (!do_output(output, cur_cal))
305                                 ret = 0;
306                 }
307                 cc_usb_close(usb);
308         }
309         return ret;
310 }
311
312 int
313 main (int argc, char **argv)
314 {
315         char                    *device = NULL;
316         char                    *filename;
317         Elf                     *e;
318         unsigned int            s;
319         int                     i;
320         int                     c;
321         int                     tries;
322         char                    *tty = NULL;
323         int                     success;
324         int                     verbose = 0;
325         int                     save = 1;
326         int                     ret = 0;
327         int                     expected_size;
328         char                    *output = NULL;
329
330         while ((c = getopt_long(argc, argv, "vnT:D:o:", options, NULL)) != -1) {
331                 switch (c) {
332                 case 'T':
333                         tty = optarg;
334                         break;
335                 case 'D':
336                         device = optarg;
337                         break;
338                 case 'v':
339                         verbose++;
340                         break;
341                 case 'n':
342                         save = 0;
343                         break;
344                 case 'o':
345                         output = optarg;
346                         break;
347                 default:
348                         usage(argv[0]);
349                         break;
350                 }
351         }
352
353         ao_verbose = verbose;
354         if (verbose)
355                 ccdbg_add_debug(CC_DEBUG_BITBANG);
356
357         if (!tty)
358                 tty = cc_usbdevs_find_by_arg(device, "AltosFlash");
359         if (!tty)
360                 tty = cc_usbdevs_find_by_arg(device, "TeleMega");
361         if (!tty)
362                 tty = getenv("ALTOS_TTY");
363         if (!tty)
364                 tty="/dev/ttyACM0";
365
366         if (!do_cal(tty, save, output))
367                 ret = 1;
368         return ret;
369 }