Проект многоканального индикатора звука
- Войдите на сайт для отправки комментариев
Ср, 15/05/2013 - 15:57
Предистория. Протолкнули в нашу контору умелые люди многоканальный индикатор звука. По описанию это один из atmel'овских камней с 8-ти (или 10-ти) битным АЦП. 8 своих мультиплексированных каналов расширенны внешним мультиплексом до 32-х. Далее контроллер гонит данные в com1 в hex на комп в логарифмированной форме (т.е. приведённых к дБ). Формат строки [начало строки chr(130)] и [64 байта] с интервалом 50мкс. Аппаратная часть по многим причинам недоступна. Комповская оболочка отображающая инфу также нас сильно не устраивает. Ну и решил я посмотреть что такое процессинг на примере написания альтернативной оболочки.
получилась такая штука, которая щас на тестировании.
// ИЗУС версия 1.2 // с ком порта приходят данные в формате начало строки char(13) + 64 байта // 64 байта - hex значения для 32х звуковых каналов, приведённых к уровню в дБ import processing.serial.*; import pitaru.sonia_v2_9.*; Serial myPort; String bufer=""; //буфер чтения int[] kanal = new int[33]; //поканальные уровни в dB int scaleX, scaleY; //экранный коэффициент PrintWriter log_output; //ведение лога работы int log_length=10000; //длина лога String comport; //ком порт int[] kanal_out = new int[33]; //номер индикатора вывода канала String[] kanal_name = new String[33]; //имена каналов String[] kanal_sr = new String[33]; //строки накопления значений каналов int[] kanal_stat = new int[33]; //таймер замирания канала int[] kanal_prev = new int[33]; //предыдущее значение int[] kanal_max = new int[33]; //среднее значения по каналам int[] kanal_min = new int[33]; //таймер занижения канала boolean[] kanal_play = new boolean[33]; //кнопки boolean[] kanal_static = new boolean[33]; //ключ замирания канала boolean[] kanal_minimum = new boolean[33]; //ключ занижения канала boolean[] kanal_maximum = new boolean[33]; //ключ завышения канала boolean[] signal_key = new boolean[33]; //ключ сигнала, реакция на звышение или на занижение int zamiranie=300; //порог замирания int pauza=10; //время допустимой паузы int korrdb=-3; //коррекция показаний int maxdb=0; //порог завышения int kanal_signal=-45; //порог срабатывания сигнала по снижению уровня Sample alarm_mini; // звук. Sample alarm_maxi; // mySample.repeat(); - начать воспроизведение // mySample.stop(1); - остановить воспроизведение // mySample.setVolume(vol); - громкость vol от 0 до 1 // mySample.setRate(rate); - скорость воспроизведения rate от 0 до 88200 // mySample.setPan(pan); - баланс (только для моно записей) pan от -1 до 1 // mySample.getCurrentFrame() - текущий воспроизводимый фрейм // mySample.getNumFrames() - всего фреймов void setup() { //OptionsDefault(); logStart(); //ведение лога работы options(); //чтение установок com_start(); Sonia.start(this); // запуск библиотеки Sonia alarm_mini = new Sample("alarmmini.wav"); // создание новых семплов alarm_maxi = new Sample("alarmmaxi.wav"); //scaleX=int(displayWidth/100); //определение параметров экрана //scaleY=int(displayHeight/100); scaleX=int(500/100); scaleY=int(500/100); fill(100, 100, 100); //size(displayWidth, displayHeight); //установка размеров окна size(scaleX*100, scaleY*100); for (int i = 0; i < 32; i = i+1) { //присвение стартовых значений kanal_sr[i]="0000000000"; kanal[kanal_out[i]]=0; kanal_prev[i]=0; kanal_static[i]=false; kanal_min[i]=0; kanal_minimum[i]=false; signal_key[i]=true; } signal_start(); alarm_mini.play(); } //***********************************setup void draw() { //***********************основной цикл while (myPort.available() > 0) { char inByte = myPort.readChar(); // прочитать символ с порта if (inByte==char(13)) { // после прочтения символа начала строки: char(13) ConvrtHexDec (); // отправить буфер на преобразование в dec с разделением на каналы // группа событий после получения данных kanal_control_f(); // строка накопления indicator(); signal(); bufer=""; } // сбросить буфер else {bufer=bufer+inByte;} } } //***********************************draw void ConvrtHexDec (){ //***************преобразование прочитанных у.е. в dB if (bufer.length()==64) { // если буфер не соответствует нужной длине, пропустить обработку for (int i = 0; i < 32; i = i+1) { kanal[kanal_out[i]]=int((unhex(bufer.substring(i*2,i*2+2))-214)*0.234)+korrdb; //выделить их строки HEX, преобразовать в DEC, и конвертировать в dB округлив до целых 0-255уе / (-50)-(+10)dB - } //присвоение с учётом перенаправления вывода, КОРРЕКЦИЯ -3дБ из-за ошибок АЦП для живого сигнала(выненено в файл настроек) }//буфер } //***********************************ConvrtHexDec void indicator () { //*****************обеспечение индикации fill(100, 100, 100); // серое поле rect(0, 0, scaleX*100, scaleY*100); for (int i = 0; i < 16; i = i+1) { fill(255, 255, 255); rect(scaleX, scaleY+scaleY*i*6, scaleX*45, scaleY*5); // белое поле rect(scaleX*50, scaleY+scaleY*i*6, scaleX*45, scaleY*5); if (kanal_max[i]>maxdb) {fill(255, 0, 0);} else if (kanal_max[i]<-10) {fill(0, 0, 255);} else {fill(255, 255, 0);} // псевдопиковый индикатор rect(int(scaleX*45*(kanal_max[i]+50-korrdb)/59), scaleY+scaleY*i*6, scaleX, scaleY*5); if (kanal_max[i+16]>maxdb) {fill(255, 0, 0);} else if (kanal_max[i+16]<-10) {fill(0, 0, 255);} else {fill(255, 255, 0);} rect(int(scaleX*45*(kanal_max[i+16]+50-korrdb)/59)+scaleX*49, scaleY+scaleY*i*6, scaleX, scaleY*5); if (kanal[i]>maxdb) {fill(255, 0, 0);} else if (kanal_static[i]==true) {fill(0, 0, 255);} else {fill(0, 255, 0);} // пиковый индикатор красный если больше 0дБ, синий при замирании канала, зелёный если норма rect(scaleX, scaleY+scaleY*i*6, int(scaleX*45*(kanal[i]+50-korrdb)/59), scaleY*5); if (kanal[i+16]>maxdb) {fill(255, 0, 0);} else if (kanal_static[i+16]==true) {fill(0, 0, 255);}else {fill(0, 255, 0);} rect(scaleX*50, scaleY+scaleY*i*6, int(scaleX*45*(kanal[i+16]+50-korrdb)/59), scaleY*5); textSize(scaleY*4); // печать имён каналов и измеренных значений if (kanal_minimum[i]==true) {fill(255, 0, 0);} else {fill(0, 0, 0);} //если среднее пиковое падает ниже порогового и сигнализация включена text(kanal_name[i], scaleX*2, scaleY*5+scaleY*i*6); if (kanal_minimum[i+16]==true) {fill(255, 0, 0);} else {fill(0, 0, 0);} text(kanal_name[i+16], scaleX*52, scaleY*5+scaleY*i*6); fill(0, 0, 0); textSize(scaleY*2); text(kanal[i], scaleX*36, scaleY*5+scaleY*i*6); text(kanal[i+16], scaleX*85, scaleY*5+scaleY*i*6); textSize(scaleY); if ((kanal_play[i]==true) && static_kanal(kanal_stat[i], millis())>0) { text(static_kanal(kanal_stat[i], millis()), scaleX*42, scaleY*5+scaleY*i*6);} // замирания if ((kanal_play[i+16]==true) && static_kanal(kanal_stat[i+16], millis())>0) {text(static_kanal(kanal_stat[i+16], millis()), scaleX*91, scaleY*5+scaleY*i*6);} if (kanal_play[i]==false) {fill(200, 200, 200);} else {fill(0, 200, 200);} // кнопочки rect(scaleX*47, scaleY+scaleY*i*6, scaleX, scaleY*5, 7); if (kanal_play[i+16]==false) {fill(200, 200, 200);} else {fill(0, 200, 200);} rect(scaleX*96, scaleY+scaleY*i*6, scaleX, scaleY*5, 9); } fill(255, 255, 255); rect(0, scaleY*96, scaleX*100, scaleY*4); fill(0, 0, 0); textSize(scaleY*2); text(static_kanal(0,millis()), scaleX, scaleY*99); } //***********************************indicator void options() { //********************чтение установочных параметров из файла String lines[] = loadStrings("options.ini"); if (lines.length<81) {log_output.println("восстановление файла options"); log_output.flush(); OptionsDefault(); exit(); } // восстановление файла options и закрытие программы print("длина файла оптионс");println(lines.length); for (int i = 0 ; i < lines.length; i++) { if (lines[i].equals("номер_порта:")==true) {comport=lines[i+1];} //при нахождении заголовка значения, соответствующим переменным присвоить значения if (lines[i].equals("расположение_каналов:")==true) {for (int z = 0 ; z < 32; z++) {kanal_out[z]=int(lines[i+1+z].substring(3,5));}} if (lines[i].equals("имена_каналов:")==true) {for (int z = 0 ; z < 32; z++) {kanal_name[z]=lines[i+1+z].substring(3,lines[i+1+z].length());} control_kanal_out();} if (lines[i].equals("сигнализация:")==true) {kanal_signal=int(lines[i+1]);} if (lines[i].equals("замирание:")==true) {zamiranie=int(lines[i+1]); } if (lines[i].equals("коррекция(дБ):")==true) {korrdb=int(lines[i+1]); } if (lines[i].equals("пауза:")==true) {pauza=int(lines[i+1]); } } }// ***********************************options void OptionsDefault() { //*************установка опций по умолчанию String words = "файл_настроек номер_порта: COM1"; words += " расположение_каналов: 00-00 01-01 02-02 03-03 04-04 05-05 06-06 07-07 08-08 09-09 10-10 11-11 12-12 13-13 14-14 15-15 16-16 17-17 18-18 19-19 20-20 21-21 22-22 23-23 24-24 25-25 26-26 26-27 28-28 29-29 30-30 31-31"; words += " имена_каналов: 00-канал00 01-канал01 02-канал02 03-канал03 04-канал04 05-канал05 06-канал06 07-канал07 08-канал08 09-канал09 10-канал10 11-канал11 12-канал12 13-канал13 14-канал14 15-канал15 16-канал16 17-канал17 18-канал18 19-канал19"; words += " 20-канал20 21-канал21 22-канал22 23-канал23 24-канал24 25-канал25 26-канал26 26-канал27 28-канал28 29-канал29 30-канал30 31-канал31"; words += " сигнализация: -45 замирание: 300 пауза: 10 коррекция(дБ): -3"; String[] list = split(words, ' '); saveStrings("options.ini", list); } //***********************************OptionsDefault void logStart() { // ******************старт ведения лога String lines[] = loadStrings("log.txt"); //чтение предыдущих значений log_output = createWriter("log.txt"); //создание файла лога if (lines.length<log_length) { //перезапись предыдущих значений for (int i = 0 ; i < lines.length; i++) { //если строк меньше log_output.println(lines[i]); }} else { for (int i = lines.length-log_length; i < lines.length; i++) { //если строк больше log_output.println(lines[i]); }} log_output.print(time_now()); log_output.println("старт приложения"); log_output.flush(); } //************************************logStart void com_start () { // *****************стартовые настройки компорта String port[] = Serial.list(); //создание списка портов for (int i = 0 ; i < port.length; i++) { if (port[i].equals(comport)==true) {myPort = new Serial(this, Serial.list()[i], 9600); }} //проверка наличие порка указанного в настройках, при наличии - открыть порт if (myPort==null) {log_output.print("указанный com порт не обнаружен, обнаружены порты: "); //при отсутствии - записать сообщение в лог и закрыть приложение for (int i = 0 ; i < port.length; i++) {log_output.print(port[i]);log_output.print(" ");} log_output.println(" требуется изменить настройки"); log_output.flush(); exit();} } //************************************com_start void signal_start () {//****************чтение последних состояний кнопок String lines[] = loadStrings("signal.ini"); //чтение предыдущих значений if (lines.length==33) {for (int i = 0 ; i < 31; i++) {kanal_play[i]=boolean(lines[i]);}} else {for (int i = 0 ; i < 31; i++) {kanal_play[i]=false;}} } //************************************signal_start void control_kanal_out() { // **********контроль правильности перенаправления каналов for (int x = 0 ; x < 31; x++) { for (int y = x+1 ; y < 32; y++) { if (kanal_out[x]==kanal_out[y]) { log_output.println(" ошибка перенаправления каналов "); log_output.print("канал №"); log_output.print(x); log_output.print(" перенаправлен на индикатор #"); log_output.print(kanal_out[x]); log_output.print(" также как и канал №"); log_output.print(y); OptionsDefault(); log_output.println(" файл options восстановлен по умолчанию"); log_output.flush(); exit(); } }} } //************************************control_kanal_out() void kanal_control_f() {//*********************контроль каналов на завышение, занижение, замирание уровня и появление сигнала int zn=0; for (int i = 0; i < 32; i = i+1) { //----- kanal_sr[i]=kanal_sr[i].substring(1)+char(kanal[i]+100); // псевдопик kanal_max[i]=-51; for (int z = 0; z < kanal_sr[i].length()-1; z = z+1) { // поиск максимального значения-------------------------------переделать на плавный спад без строки накопления zn=int(kanal_sr[i].charAt(z)-100); if (kanal_max[i]<zn) {kanal_max[i]=zn;} } if (signal_key[i]==true) { // сигнализация при наличии сигнала //----- if (kanal_play[i]==true) { //занижения уровня if (kanal_max[i]>=kanal_signal) {kanal_min[i]=millis(); kanal_minimum[i]=false;} else if (static_kanal(kanal_min[i], millis())>pauza) {kanal_minimum[i]=true;} //----- //замирание if (kanal_prev[i]!=kanal[i]) {kanal_prev[i]=kanal[i]; kanal_stat[i]=millis(); kanal_static[i]=false;} //значения изменились - начать отсчёт времени от сих и и сравнивать будущие значения с текущим else if (static_kanal(kanal_stat[i], millis())>zamiranie) {kanal_static[i]=true;} //замирание более порогового //----- if (kanal[i]>maxdb) {kanal_maximum[i]=true;} else {kanal_maximum[i]=false;} //завышение уровня (мгновенное) } else {kanal_minimum[i]=false; kanal_static[i]=false;} //----- } else {if (kanal[i]>=kanal_signal) {kanal_minimum[i]=true;} else {kanal_minimum[i]=false;}} //появление ожидаемого сигнала выше порогового //----- }// цикл i } //************************************kanal_control_f int static_kanal(int time1, int time2) { //*********получение значений замирания канала в секундах [время начала] [время окончания(текущее)] в миллисекундах time1=int((time2-time1)/1000); return time1; } //************************************static_kanal void mousePressed() { // ***************клик мышью int i_local=int(mouseY/scaleY/6); //координаты клика по высоте преобразовать в номер канала if ((mouseButton==LEFT) && (mouseX/scaleX < 50 )) { //левая часть экрана kanal_play[i_local]= !kanal_play[i_local]; //смена состояния кнопок if ((kanal[i_local]<=kanal_signal) && (kanal_play[i_local]==true)) {signal_key[i_local]=false;} else {signal_key[i_local]=true;} //ключь на реакцию по пропаданию сигнала } else if ((mouseButton==LEFT) && (mouseX/scaleX > 50 )) { //правая часть экрана (+16) kanal_play[i_local+16]= !kanal_play[i_local+16]; if ((kanal[i_local+16]<=kanal_signal) && (kanal_play[i_local+16]==true)) {signal_key[i_local+16]=false;} else {signal_key[i_local+16]=true;} //ключь на реакцию по появлению сигнала } String words2=""; for (int i = 0; i < 32; i = i+1) { words2 += str(kanal_play[i])+" ";} String[] list2 = split(words2, ' '); saveStrings("signal.ini", list2); } //************************************mousePressed() void signal() { //**********************события по выходу уровня сигнала за рамки допустимого for (int i = 0; i < 32; i = i+1) { if ((kanal_play[i]==true) && (kanal_maximum[i]==true)) { //превышения уровня - пик log_output.print(time_now()); log_output.print(kanal_name[i]); log_output.print(" = +"); log_output.print(kanal[i]); log_output.println("dB"); log_output.flush(); alarm_mini.play(); } if ((kanal_play[i]==true) && (kanal_minimum[i]==true) && (signal_key[i]==true)) { //занижение уровня - псевдопик log_output.print(time_now()); log_output.print(kanal_name[i]); log_output.print(" < "); log_output.print(str(kanal_signal)); log_output.print("dB "); log_output.print(static_kanal(kanal_min[i], millis())); log_output.print(" сек."); log_output.println(""); log_output.flush(); alarm_maxi.play(); } if ((kanal_minimum[i]==true) && (signal_key[i]==false)) { //появление ожидаемого сигнала log_output.print(time_now()); log_output.print(kanal_name[i]); log_output.print(" появился с уровнем "); log_output.print(str(kanal[i])); log_output.println(""); log_output.flush(); alarm_maxi.play(); } if ((kanal_play[i]==true) && (kanal_static[i]==true) && (kanal_minimum[i]==false)) { log_output.print(time_now()); log_output.print(kanal_name[i]); log_output.print(" - замирание канала на уровне "); log_output.print(str(kanal[i])); log_output.print("dB "); log_output.print(static_kanal(kanal_stat[i], millis())); log_output.print(" сек."); log_output.println(""); log_output.flush(); alarm_maxi.play(); } } // конец цикла i } //************************************signal() String time_now() { //******************строка текущего времени String text=str(year())+"-"+str(month())+"-"+str(day())+" "+str(hour())+":"+str(minute())+":"+str(second())+" "; return text; } //************************************time_now() public void stop(){ // ****************безопасная остановка Sonia.stop(); myPort.stop(); super.stop(); }если кому то не лень, поглядите, может есть принципиальные ошибки.
работает так:
1. зацеплены две библиотеки, ком и звук
2. задать базовые установки, прочитать изменяемые установки из внешнего файла (при отдельных ошибках в файле сбросить его по умолчанию).
3. в основном цикле чтаем данных из кома, при сборе строки данных отправляем их на преобразование(порезать по два байта и записать в массив в очевидном десятичном виде), далее полученные данные отправляются на индикцию, сигнализацию (уровень ниже указанного, выше 0дБ, замирание сигнала на одном уровне дольше заданного)
все остальные функции дополнительные, так например все события пишутся в лог-файл длиной не более заданного, при превышении длины старые данные удаляются.
Визуально уровень сигнала отображается в виде прогрессбара, нормальный уровень - зелёный, превышение - красный. Как настоящий пиковый индикатор (только тут он псевдопиковый).
т.к. на живом устройстве отлаживать было неудобно в нану был закинут эмулятог гонящий схожие данные в ком
//имитация ИЗУСа int z; int x; int x1=100; int a1=1; int x2=100; int a2=2; int zn[33]; void setup() { Serial.begin(9600); Serial.println("--start--"); for (z=5; z<29; z++) {zn[z]=random(0,255);} } void loop() { Serial.print(char(13)); if (x1>253) {a1=-1;} if (x1<17) {a1=1;} x1=x1+a1; Serial.print(x1, HEX); if (x2>253) {a2=-2;} if (x2<19) {a2=2;} x2=x2+a2; Serial.print(x2, HEX); Serial.print("5500"); for (z=5; z<29; z++) { if (random(0, 2)==0) {zn[z]=zn[z]+4;} else {zn[z]=zn[z]-4;} if (zn[z]<5) {zn[z]=5;} if (zn[z]>250) {zn[z]=250;} if (zn[z]<16) {Serial.print(0);} Serial.print(zn[z], HEX); } Serial.print("00000000"); delay(20); }полноценные кнопочки-пимпочки мне было рисовать лень, а цеплять внешние библиотеки вроде бы и не обязательно, потому всё оперативное управление приложением сводится к возможности кликнуть по соответствующему индикатору включив или выключив сигнализацию (координаты клика преобразуются в соответствующий номер канала).
Последним что добавил (и чего в оригинальной оболочке не ожидалось) была возможность сигнализации не пропадания сигнала, а его появления (без добавления лишних элементов управления).