layer1/monitor: Adding spectrum analyzer to monitor app
[osmocom-bb.git] / src / target / firmware / apps / monitor / main.c
blob5d247958aa22a8f195797146fbdf97c6fff3bff9
1 /* Cell Monitor of Free Software for Calypso Phone */
3 /* (C) 2012 by Andreas Eversberg <jolly@eversberg.eu>
5 * All Rights Reserved
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <errno.h>
28 #include <debug.h>
29 #include <memory.h>
30 #include <delay.h>
31 #include <byteorder.h>
32 #include <rffe.h>
33 #include <keypad.h>
34 #include <board.h>
35 #include <abb/twl3025.h>
36 #include <rf/trf6151.h>
37 #include <calypso/clock.h>
38 #include <calypso/tpu.h>
39 #include <calypso/tsp.h>
40 #include <calypso/dsp.h>
41 #include <calypso/irq.h>
42 #include <calypso/misc.h>
43 #include <calypso/buzzer.h>
44 #include <comm/sercomm.h>
45 #include <comm/timer.h>
46 #include <fb/framebuffer.h>
47 #include <layer1/sync.h>
48 #include <layer1/async.h>
49 #include <layer1/l23_api.h>
51 enum key_codes key_code = KEY_INV;
52 int key_pressed = 0;
53 enum key_codes key_pressed_code;
54 unsigned long key_pressed_when;
55 unsigned int key_pressed_delay;
57 enum mode {
58 MODE_MAIN,
59 MODE_SPECTRUM,
60 MODE_ARFCN,
61 } mode = MODE_MAIN;
62 enum mode last_mode; /* where to return after entering ARFCN */
64 static uint16_t arfcn = 0;
65 int pcs = 0;
66 int uplink = 0;
67 int max = 0;
68 uint8_t power, max_power;
69 char input[5];
70 int cursor;
72 static struct band {
73 int min, max, prev, next, freq_ul, freq_dl;
74 } bands[] = {
75 { 128, 251, 124, 512, 8242, 8692 }, /* GSM 850 */
76 { 955, 124, 885, 128, 8762, 9212 }, /* P,E,R GSM */
77 { 512, 885, 251, 955, 17102, 18052 }, /* DCS 1800 */
78 { 0, 0, 0, 0, 0, 0},
81 struct band *band;
83 #define PCS_MIN 512
84 #define PCS_MAX 810
85 #define DCS_MIN 512
86 #define DCS_MAX 885
87 #define PCS_UL 18502
88 #define PCS_DL 19302
90 enum pm_mode {
91 PM_IDLE,
92 PM_SENT,
93 PM_RANGE_SENT,
94 PM_RANGE_RESULT,
95 PM_RESULT,
96 } pm_mode = PM_IDLE;
98 #define NUM_PM_DL 2
99 #define NUM_PM_UL 10
100 int pm_meas[NUM_PM_UL];
101 int pm_count = 0;
102 int pm_max = 2;
103 uint8_t pm_spectrum[1024];
104 int pm_scale = 1; /* scale measured power level */
106 #define TONE_JIFFIES 4
107 int tone = 0;
108 unsigned long tone_time;
109 int tone_on = 0;
111 /* UI */
113 static void refresh_display(void)
115 char text[16];
117 fb_clear();
119 /* header */
120 fb_setbg(FB_COLOR_WHITE);
121 if (mode != MODE_SPECTRUM) {
122 fb_setfg(FB_COLOR_BLUE);
123 fb_setfont(FB_FONT_HELVR08);
124 fb_gotoxy(0,6);
125 fb_putstr("Osmocom Monitor Tool",-1);
126 fb_gotoxy(0,10);
127 fb_setfg(FB_COLOR_BLACK);
128 fb_boxto(framebuffer->width-1,10);
130 fb_setfg(FB_COLOR_BLACK);
131 fb_setfont(FB_FONT_C64);
133 /* ARFCN */
134 if (mode == MODE_MAIN || mode == MODE_ARFCN) {
135 fb_gotoxy(0,20);
136 if (mode == MODE_ARFCN)
137 sprintf(text, "ARFCN %s", input);
138 else if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX)
139 sprintf(text, "ARFCN %dPCS", arfcn);
140 else if (arfcn >= DCS_MIN && arfcn <= DCS_MAX)
141 sprintf(text, "ARFCN %dDCS", arfcn);
142 else
143 sprintf(text, "ARFCN %d", arfcn);
144 fb_putstr(text,framebuffer->width);
147 /* cursor */
148 if (mode == MODE_ARFCN) {
149 fb_setfg(FB_COLOR_WHITE);
150 fb_setbg(FB_COLOR_BLUE);
151 fb_putstr(" ", framebuffer->width);
152 fb_setfg(FB_COLOR_BLACK);
153 fb_setbg(FB_COLOR_WHITE);
156 /* Frequency / power */
157 if (mode == MODE_MAIN) {
158 int f;
160 if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX) {
161 if (uplink)
162 f = PCS_UL;
163 else
164 f = PCS_DL;
165 } else if (uplink)
166 f = band->freq_ul;
167 else
168 f = band->freq_dl;
169 f += ((arfcn - band->min) & 1023) << 1;
171 fb_gotoxy(0,30);
172 sprintf(text, "Freq. %d.%d", f / 10, f % 10);
173 fb_putstr(text,framebuffer->width);
175 fb_gotoxy(0,40);
176 sprintf(text, "Power %d", ((max) ? max_power : power) - 110);
177 fb_putstr(text,framebuffer->width);
178 if (max) {
179 fb_setfont(FB_FONT_HELVR08);
180 fb_gotoxy(80,39);
181 fb_putstr("max",framebuffer->width);
182 fb_setfont(FB_FONT_C64);
184 fb_setbg(FB_COLOR_BLACK);
185 fb_gotoxy(0,45);
186 fb_boxto(framebuffer->width * power / 64, 50);
187 if (max) {
188 fb_gotoxy(framebuffer->width * max_power / 64 ,45);
189 fb_boxto(framebuffer->width * max_power / 64, 50);
191 fb_setbg(FB_COLOR_WHITE);
194 /* spectrum */
195 if (mode == MODE_SPECTRUM) {
196 int i;
197 uint16_t a, e, p;
199 fb_gotoxy(0,8);
200 if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX)
201 sprintf(text, "%4dP", arfcn);
202 else if (arfcn >= DCS_MIN && arfcn <= DCS_MAX)
203 sprintf(text, "%4dD", arfcn);
204 else
205 sprintf(text, "%4d ", arfcn);
206 sprintf(text + 5, " %d", pm_spectrum[arfcn & 1023] - 110);
207 fb_putstr(text,framebuffer->width);
208 if (max) {
209 fb_setfont(FB_FONT_HELVR08);
210 fb_gotoxy(80,15);
211 fb_putstr("max",framebuffer->width);
212 fb_setfont(FB_FONT_C64);
214 if (pm_scale != 1) {
215 fb_setfont(FB_FONT_HELVR08);
216 fb_gotoxy(1,15);
217 sprintf(text, "x%d", pm_scale);
218 fb_putstr(text,framebuffer->width);
219 fb_setfont(FB_FONT_C64);
221 if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX) {
222 a = PCS_MIN;
223 e = PCS_MAX;
224 } else {
225 a = band->min;
226 e = band->max;
228 for (i = 0; i < framebuffer->width - 1; i++) {
229 p = (arfcn + i - (framebuffer->width >> 1)) & 1023;
230 if ((((p - a) & 1023) & 512))
231 continue;
232 if ((((e - p) & 1023) & 512))
233 continue;
234 p = (pm_spectrum[p] * pm_scale * 40 / 64);
235 if (p > 40)
236 p = 40;
237 fb_gotoxy(i, 50 - p);
238 fb_boxto(i, 50);
240 i = framebuffer->width >> 1;
241 fb_gotoxy(i, 0);
242 fb_boxto(i, 4);
243 fb_gotoxy(i, 50);
244 fb_boxto(i, 54);
247 /* footer */
248 fb_gotoxy(0,55);
249 fb_boxto(framebuffer->width-1,55);
250 fb_gotoxy(0,64);
251 if (mode == MODE_ARFCN)
252 sprintf(text, "%s %s", (cursor) ? "del " : "back",
253 (cursor) ? "enter" : " ");
254 else
255 sprintf(text, "%s %s", (pcs) ? "PCS" : "DCS",
256 (uplink) ? "UL" : "DL");
257 fb_putstr(text,framebuffer->width);
258 fb_setfont(FB_FONT_HELVR08);
259 fb_gotoxy(0,63);
260 sprintf(text, "%d", tone / 25);
261 fb_putstr(text,-1);
263 fb_flush();
266 static void exit_arfcn(void)
268 mode = last_mode;
269 refresh_display();
272 static void enter_arfcn(enum key_codes code)
274 /* enter mode */
275 if (mode != MODE_ARFCN) {
276 last_mode = mode;
277 mode = MODE_ARFCN;
278 input[0] = code - KEY_0 + '0';
279 input[1] = '\0';
280 cursor = 1;
281 refresh_display();
282 return;
285 if (code == KEY_LEFT_SB) {
286 /* back */
287 if (cursor == 0) {
288 exit_arfcn();
289 return;
291 /* delete */
292 cursor--;
293 input[cursor] = '\0';
294 refresh_display();
295 return;
298 if (code == KEY_RIGHT_SB) {
299 int check = 0;
300 int i;
301 struct band *temp = NULL;
303 /* nothing entered */
304 if (cursor == 0) {
305 return;
307 for (i = 0; i < cursor; i++)
308 check = (check << 3) + (check << 1) + input[i] - '0';
310 /* check */
311 for (i = 0; bands[i].max; i++) {
312 temp = &bands[i];
313 if (temp->min < temp->max) {
314 if (check >= temp->min && check <= temp->max)
315 break;
316 } else {
317 if (check >= temp->min || check <= temp->max)
318 break;
321 if (!bands[i].max)
322 return;
323 if (check > 1023)
324 return;
325 arfcn = check;
326 band = temp;
327 mode = last_mode;
328 refresh_display();
329 return;
332 if (cursor == 4)
333 return;
335 input[cursor] = code - KEY_0 + '0';
336 cursor++;
337 input[cursor] = '\0';
338 refresh_display();
341 static int inc_dec_arfcn(int inc)
343 int i;
345 /* select current band */
346 for (i = 0; bands[i].max; i++) {
347 band = &bands[i];
348 if (band->min < band->max) {
349 if (arfcn >= band->min && arfcn <= band->max)
350 break;
351 } else {
352 if (arfcn >= band->min || arfcn <= band->max)
353 break;
356 if (!bands[i].max)
357 return -EINVAL;
359 if (inc) {
360 if (arfcn == band->max)
361 arfcn = band->next;
362 else if (arfcn == 1023)
363 arfcn = 0;
364 else
365 arfcn++;
366 } else {
367 if (arfcn == band->min)
368 arfcn = band->prev;
369 else if (arfcn == 0)
370 arfcn = 1023;
371 else
372 arfcn--;
374 /* select next band */
375 for (i = 0; bands[i].max; i++) {
376 band = &bands[i];
377 if (band->min < band->max) {
378 if (arfcn >= band->min && arfcn <= band->max)
379 break;
380 } else {
381 if (arfcn >= band->min || arfcn <= band->max)
382 break;
385 if (!bands[i].max)
386 return -EINVAL;
388 refresh_display();
390 return 0;
393 static void toggle_dcs_pcs(void)
395 pcs = !pcs;
396 refresh_display();
399 static void toggle_up_down(void)
401 uplink = !uplink;
402 refresh_display();
405 static void toggle_spectrum(void)
407 if (mode == MODE_MAIN) {
408 mode = MODE_SPECTRUM;
409 pm_mode = PM_IDLE;
410 } else if (mode == MODE_SPECTRUM) {
411 mode = MODE_MAIN;
412 pm_mode = PM_IDLE;
414 l1s_reset();
415 l1s_reset_hw();
416 pm_count = 0;
417 refresh_display();
420 static void tone_inc_dec(int inc)
422 if (inc) {
423 if (tone + 25 <= 255)
424 tone += 25;
425 } else {
426 if (tone - 25 >= 0)
427 tone -= 25;
430 refresh_display();
433 static void hold_max(void)
435 max = !max;
436 max_power = power;
437 refresh_display();
440 static int inc_dec_spectrum(int inc)
442 if (inc) {
443 pm_scale <<= 1;
444 if (pm_scale > 8)
445 pm_scale = 8;
446 } else {
447 pm_scale >>= 1;
448 if (pm_scale < 1)
449 pm_scale = 1;
452 refresh_display();
454 return 0;
457 static void handle_key_code()
459 /* key repeat */
460 if (key_pressed) {
461 unsigned long elapsed = jiffies - key_pressed_when;
462 if (elapsed > key_pressed_delay) {
463 key_pressed_when = jiffies;
464 key_pressed_delay = 10;
465 /* only repeat these keys */
466 if (key_pressed_code == KEY_LEFT
467 || key_pressed_code == KEY_RIGHT)
468 key_code = key_pressed_code;
472 if (key_code == KEY_INV)
473 return;
475 /* do later, do not disturb tone */
476 if (tone_on)
477 return;
479 switch (key_code) {
480 case KEY_0:
481 case KEY_1:
482 case KEY_2:
483 case KEY_3:
484 case KEY_4:
485 case KEY_5:
486 case KEY_6:
487 case KEY_7:
488 case KEY_8:
489 case KEY_9:
490 if (mode == MODE_MAIN || mode == MODE_SPECTRUM || mode == MODE_ARFCN)
491 enter_arfcn(key_code);
492 break;
493 case KEY_UP:
494 if (mode == MODE_MAIN)
495 tone_inc_dec(1);
496 else if (mode == MODE_SPECTRUM)
497 inc_dec_spectrum(1);
498 break;
499 case KEY_DOWN:
500 if (mode == MODE_MAIN)
501 tone_inc_dec(0);
502 else if (mode == MODE_SPECTRUM)
503 inc_dec_spectrum(0);
504 break;
505 case KEY_RIGHT:
506 if (mode == MODE_MAIN || mode == MODE_SPECTRUM)
507 inc_dec_arfcn(1);
508 break;
509 case KEY_LEFT:
510 if (mode == MODE_MAIN || mode == MODE_SPECTRUM)
511 inc_dec_arfcn(0);
512 break;
513 case KEY_LEFT_SB:
514 if (mode == MODE_MAIN || mode == MODE_SPECTRUM)
515 toggle_dcs_pcs();
516 else if (mode == MODE_ARFCN)
517 enter_arfcn(key_code);
518 break;
519 case KEY_RIGHT_SB:
520 if (mode == MODE_MAIN || mode == MODE_SPECTRUM)
521 toggle_up_down();
522 else if (mode == MODE_ARFCN)
523 enter_arfcn(key_code);
524 break;
525 case KEY_MENU:
526 hold_max();
527 break;
528 case KEY_POWER:
529 if (mode == MODE_ARFCN)
530 exit_arfcn();
531 else if (mode == MODE_SPECTRUM)
532 toggle_spectrum();
533 break;
534 case KEY_STAR:
535 if (mode == MODE_MAIN || mode == MODE_SPECTRUM)
536 toggle_spectrum();
537 break;
538 default:
539 break;
542 key_code = KEY_INV;
545 static void handle_tone(void)
547 unsigned long elapsed = jiffies - tone_time;
549 if (!tone_on) {
550 if (!tone || mode != MODE_MAIN)
551 return;
552 /* wait depending on power level */
553 if (elapsed < (uint8_t)(63-power))
554 return;
555 buzzer_volume(tone);
556 buzzer_note(NOTE(NOTE_C, OCTAVE_5));
557 tone_time = jiffies;
558 tone_on = 1;
559 return;
562 if (elapsed >= TONE_JIFFIES) {
563 tone_on = 0;
564 tone_time = jiffies;
565 buzzer_volume(0);
569 /* PM handling */
571 static void handle_pm(void)
573 /* start power measurement */
574 if (pm_mode == PM_IDLE && (mode == MODE_MAIN || mode == MODE_SPECTRUM)) {
575 struct msgb *msg = l1ctl_msgb_alloc(L1CTL_PM_REQ);
576 struct l1ctl_pm_req *pm;
577 uint16_t a, e;
579 pm = (struct l1ctl_pm_req *) msgb_put(msg, sizeof(*pm));
580 pm->type = 1;
581 if (mode == MODE_MAIN) {
582 a = arfcn;
583 if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX)
584 a |= ARFCN_PCS;
585 if (uplink)
586 a |= ARFCN_UPLINK;
587 e = a;
588 pm_mode = PM_SENT;
590 if (mode == MODE_SPECTRUM) {
591 if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX) {
592 a = PCS_MIN | ARFCN_PCS;
593 e = PCS_MAX | ARFCN_PCS;
594 } else {
595 a = band->min;
596 e = band->max;
598 pm_mode = PM_RANGE_SENT;
600 if (uplink) {
601 a |= ARFCN_UPLINK;
602 e |= ARFCN_UPLINK;
604 pm->range.band_arfcn_from = htons(a);
605 pm->range.band_arfcn_to = htons(e);
607 l1a_l23_rx(SC_DLCI_L1A_L23, msg);
609 return;
612 if (pm_mode == PM_RESULT) {
613 pm_mode = PM_IDLE;
614 if (pm_count == pm_max) {
615 int i = 0;
616 int sum = 0;
618 if (uplink) {
619 /* find max */
620 for (i = 0; i < pm_count; i++) {
621 if (pm_meas[i] > sum)
622 sum = pm_meas[i];
624 power = sum;
625 } else {
626 for (i = 0; i < pm_count; i++)
627 sum += pm_meas[i];
628 power = sum / pm_count;
630 if (power > max_power)
631 max_power = power;
632 pm_count = 0;
633 pm_max = (uplink) ? NUM_PM_UL : NUM_PM_DL;
634 if (!tone_on)
635 refresh_display();
637 return;
640 if (pm_mode == PM_RANGE_RESULT) {
641 pm_mode = PM_IDLE;
642 refresh_display();
643 buzzer_volume(tone);
644 buzzer_note(NOTE(NOTE_C, OCTAVE_5));
645 tone_time = jiffies;
646 tone_on = 1;
647 return;
651 /* Main Program */
652 const char *hr = "======================================================================\n";
654 /* note: called from IRQ context */
655 static void l1a_l23_tx(struct msgb *msg)
657 struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->l1h;
658 struct l1ctl_pm_conf *pmr;
660 switch (l1h->msg_type) {
661 case L1CTL_PM_CONF:
662 if (pm_mode == PM_SENT) {
663 pmr = (struct l1ctl_pm_conf *) l1h->data;
664 pm_meas[pm_count] = pmr->pm[0];
665 pm_count++;
666 pm_mode = PM_RESULT;
668 if (pm_mode == PM_RANGE_SENT) {
669 for (pmr = (struct l1ctl_pm_conf *) l1h->data;
670 (uint8_t *) pmr < msg->tail; pmr++) {
671 if (!max || pm_spectrum[ntohs(pmr->band_arfcn) & 1023] < pmr->pm[0])
672 pm_spectrum[ntohs(pmr->band_arfcn) & 1023] = pmr->pm[0];
674 if ((l1h->flags & L1CTL_F_DONE))
675 pm_mode = PM_RANGE_RESULT;
677 l1s.tpu_offset_correction += 5000 / NUM_PM_UL;
678 break;
681 msgb_free(msg);
685 static void console_rx_cb(uint8_t dlci, struct msgb *msg)
687 if (dlci != SC_DLCI_CONSOLE) {
688 printf("Message for unknown DLCI %u\n", dlci);
689 return;
692 printf("Message on console DLCI: '%s'\n", msg->data);
693 msgb_free(msg);
696 static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg)
698 int i;
699 printf("l1a_l23_rx_cb (DLCI %d): ", dlci);
700 for (i = 0; i < msg->len; i++)
701 printf("%02x ", msg->data[i]);
702 puts("\n");
705 static void key_handler(enum key_codes code, enum key_states state)
707 if (state != PRESSED) {
708 key_pressed = 0;
709 return;
711 /* key repeat */
712 if (!key_pressed) {
713 key_pressed = 1;
714 key_pressed_when = jiffies;
715 key_pressed_code = code;
716 key_pressed_delay = 60;
719 key_code = code;
722 int main(void)
724 board_init();
726 puts("\n\nOSMOCOM Monitor Tool (revision " GIT_REVISION ")\n");
727 puts(hr);
729 /* Dump device identification */
730 dump_dev_id();
731 puts(hr);
733 /* Dump clock config before PLL set */
734 calypso_clk_dump();
735 puts(hr);
737 keypad_set_handler(&key_handler);
739 /* Dump clock config after PLL set */
740 calypso_clk_dump();
741 puts(hr);
743 sercomm_register_rx_cb(SC_DLCI_CONSOLE, console_rx_cb);
744 sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb);
746 layer1_init();
747 l1a_l23_tx_cb = l1a_l23_tx;
749 // display_unset_attr(DISP_ATTR_INVERT);
751 tpu_frame_irq_en(1, 1);
753 buzzer_mode_pwt(1);
754 buzzer_volume(0);
756 memset(pm_spectrum, 0, sizeof(pm_spectrum));
758 /* inc 0 to 1 and refresh */
759 inc_dec_arfcn(1);
761 while (1) {
762 l1a_compl_execute();
763 osmo_timers_update();
764 handle_key_code();
765 l1a_l23_handler();
766 handle_pm();
767 handle_tone();
770 /* NOT REACHED */
772 twl3025_power_off();