Как читать последовательный код (manchester/hamming/reed-solomon/uart и т п)? процедурно

Voodoo Doll
Voodoo Doll аватар
Offline
Зарегистрирован: 18.09.2016

заглушка here (да детка, меня не устраивает функционал форума, WYSIWYG тупа ОТКЛЮЧЕН, ватафак блеять? вот поэтому и код втыкают неправильно)

Voodoo Doll
Voodoo Doll аватар
Offline
Зарегистрирован: 18.09.2016

Делаю USCI (командный интерфейс) для ПК, короче нечто что будет передавать короткие команды, типа как кнопки пульта. Величины полезной нагрузки в 2-3 байта достаточно. Транспортный уровень OSI - ультразвук.

Объясняю как это работает:

1. Берём спектровизуализатор с матрицей 8х32 на БПФ, делаем. Выбрасываем из проекта матрицу.

2. Он 64-образцовый (это ширина окна БПФ если кто непонел) 32-полосный, и авторы заявляют что видит вплоть до 18.6 кГц.

3. Берём последнюю полосу, математику над остальными максимально выкидываем чтобы делать оцифровку быстро насколько возможно.

4. Берём редактор, жмём там Generate-Tone, пишем частоту которую не слышно на слух, которая легко пролезет через аудиокарты любых компов от 1998 г. в., которая надёжно определяется этим анализатором. 18500 Гц подошло. 19000 тоже нормально но наверное такие частоты уже будет тяжко звуковухам говнянного качества.

5. Уря, у нас есть приблуда на одной микросхеме, программа с математикой FFT занимает один таймер и 5 килобайт, дополнительных деталей в смесителе - стабилитрон, 6 резисторов и 2 конденсатора. Никаких тебе дефицитных компараторов, труднорассчитываемых фильтров и прочего аналогового сношательства мозга и необходимости в убер гига измерительных приборах, просто смеситель, как любит Гайвер - навесным монтажом или на макетной плате, плюс БПФ. И если позвучать в винампе каким-то ультразвуком, оно выдаёт на выходе чёткую единицу.

Теперь я копипащу другую библиотеку, для софтварного декода манчестера, и сталкиваюсь с неприятной преградой, что эта срань не пашет, а хорошо бы эту проблемку как-нибудь решить. Что мы имеем?

- "однопроводную" линию связи, то есть

- отсутствие синхронизации

- допустим хифилитики слушают только через бескислородную медь, но у меня допустим на диске качественные файлы (flac/w64) лежат нечасто (жалко места), в основном же это записи на уровне чуть лучше китайских аудиокассет, упакованные lame/mp3/frauhofer iis/m4a кодеками, то есть кодеками которые верхние частоты безбожно жмут (или вообще внагляк выкидывают polyphase lowpass фильтром). То есть сигналы на ультразвуковых частотах с аудиокарт ПуКа крайне редки, то есть - почти полное отсутствие помех.

С кодом авторства ув. victornpb мне удалось добиться распознавания преамбулы и правильного режима работы алгоритма, но его по-прежнему колбасит и полностью распознанную без ошибок посылку я так и не получил. Кроме того, цифры на индикаторе режима (preamble/synced и т д) стали правильными только на пачках 150 мс за полбита, то есть скорость связи - 3.333 bps (три с третью бита в секунду Карл). Должен быть способ проще и эффективнее.

Я думаю, что можно по этому принципу использовать начало преамбулы как захват несущей (как в RFId метках), а потом начать запись в массив, используя для дискретизации период, который был в преамбуле. Если синхронизация собъётся - похер, всё равно длина посылки небольшая, плюс посылки записаны заранее и там то уж все тайминги соблюдены идеально. Надо только подумоть, как бы мне родить алгоритм который сможет выдать в массив нужный бит руководствуясь правилом "большая часть конкретно вот этого кадра дискретизации - конкретно вот такой уровень на линии". Накидайте идей в общем. И мне непонятно, почему так рвёт крышу вышеупомянотому парсеру. Он полностью абстрактный и работает на конечных автоматах, там тупо нечему наткнуться в bottleneck по таймерам/прерываниям. Или ищщщщщщщщо снизить скорость?

// Forked from these repos:
// shajeebtm/Arduino-audio-spectrum-visualizer-analyzer
// victornpb/manch_decode

#include <TM1637.h>
#include <arduinoFFT.h>
#define SAMPLES          (64)       // Must be a power of 2
#define HFVOL_THRESHOLD  (5)

double vReal[SAMPLES];
double vImag[SAMPLES];
char data_avgs; // data_avgs[32];
char incoming;
byte display_shadow[4]={1,2,3,4};

bool b_mark;
arduinoFFT FFT = arduinoFFT();
TM1637 tm1637(7,6);

namespace Buffer{
    const int ringsize = 8; // buffer length
    uint8_t head = 0;
    uint8_t tail = 0;
    byte ring[ringsize];
    int enqueue (byte val) {
        int newtail = (tail + 1) % ringsize;
        if(newtail == head){
            return 0;         // buffer full
        }else{
            ring[tail] = val;
            tail = newtail;
            return 1;         // success
        }
    }
    byte dequeue(){
        if(head == tail) return 0;
         else{
            byte val = ring[head];
            head  = (head + 1) % ringsize;
            return val;
        }
    }
    int queuelevel(){ return tail - head + (head > tail ? ringsize : 0); }     // number of elements
}
namespace Manch{
// You need to send a 11110100 preamble before your data.
// If your data does not pass CRC, you can set Manch.status = RESYNC to force a resync. 
// more comments https://github.com/victornpb/manch_decode
    char status;
    enum {
        PREAMBLE, //waiting for checksum
        SYNC,     //synced signal
        CHECK,    //passed checksum
        RESYNC    //lost sync
    };
    // Called every falling or rising edge
    // s - current logic state
    // t - time in ms since last transition
    void decode(bool s, uint16_t t){
        static uint16_t T; // timing of a half bit
        static uint8_t i;  // count edges on the preamble stage
        static bool w;     // keep track of the 2T timing
        static struct{     // used to store bits until a full byte is received
            byte buffer;
            uint8_t pos;
        } bits;
        ++i;
        if(status==RESYNC){ // reset everything and wait for a preamble and resync
            i=0;
            T=0;
            w=0;
            bits.buffer=0x0; //clear bits buffer
            bits.pos=0;
            status=PREAMBLE; 
        }
        if(status==PREAMBLE){
            if(i>3){
                if(!T){ T=t; }                   // default if T is undefined
                else if(t<T*0.75){ T=t;  }       // found smaller, use that instead
                else if(t>T*0.75 && t<=T*1.5){ } // within 1.0*T, do nothing
                else if(t>T*1.5 && t<T*2.5 ){    // first 2T is a sync signal
                    status = SYNC;
                    i=0;
                    bits.pos=4; //checksum start at the 4th bit
                    bits.buffer = bits.buffer | s<<7-bits.pos++;
                }
                else status = RESYNC;            // found much longer T
            }
        }else{                                    // SYNCED, start decoding
            if(t>T*2.5 || t<T*0.75){ status=RESYNC; }    // huge timing error
            if(t>T*1.5){ bits.buffer = bits.buffer | s<<7-bits.pos++; }   // 2T, decode
            else{                 //T
                if(w<1){ w=1; }   // first t
                else{             //second t
                    bits.buffer = bits.buffer | s<<7-bits.pos++;
                    w=0;
                }        
            }
            if(status==SYNC){                      // do a CHECKSUM after sync signal
                if(bits.pos>7){// wait for a complete byte  
                    if(bits.buffer==0b0100){ // ok
                        status = CHECK;
                        bits.buffer=0x0;     // clear
                        bits.pos=0;
                    }
                    else status=RESYNC;      // invalid
                } 
            }
            else if(status==CHECK){                        //start pushing bytes to out buffer
                if(bits.pos>7){                          // received a full byte
                    Buffer::enqueue(bits.buffer);
                    bits.buffer = 0x0; //clear
                    bits.pos = 0;
                }
            }
        }                                   // decoding on SYNCED state ends
    }
    uint8_t available(){ return Buffer::queuelevel(); }
    byte read(){ return Buffer::dequeue(); }
}

void setup(){
    ADCSRA = 0b11100101;            // set ADC to free running mode and set pre-scalar to 32 (0xe5)
    ADMUX = 0b00000000;             // use pin A0 and external voltage reference
    delay(50);                      // wait to get reference voltage stabilized
//    Serial.begin(9600);
    pinMode(13, OUTPUT);
    digitalWrite(13,0);
    tm1637.init();
    tm1637.set(2);//0-7
    tm1637.display(display_shadow);
}
 
void loop(){
   byte i,j,k;
   for(i=0; i<SAMPLES; i++){    // sampling
      while(!(ADCSRA & 0x10));      // wait for ADC to complete current conversion ie ADIF bit set
      ADCSRA = 0b11110101 ;         // clear ADIF bit so that ADC can do next operation (0xf5)
      int value = ADC - 512 ;       // subtract DC offset
      vReal[i] = value/8;           // copy to bins after compressing
      vImag[i] = 0;                         
    }
    FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
    FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
    FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
    data_avgs = vReal[31];
    data_avgs = constrain(data_avgs, 0, 80);              // rescale
    data_avgs = map(data_avgs, 0, 80, 0, 32);
    b_mark = (data_avgs>HFVOL_THRESHOLD) ? 1 : 0;         // compare

//    Serial.println(b_mark);                               // plot related, ???? mbshit

    edgeDetect(); //runs on loop

    digitalWrite(13,b_mark);
    display_shadow[0]=Manch::status;
    
    while (Manch::available() > 0) {
      incoming=Manch::read();
      i=(byte)incoming;
      j=i;
      j>>=8; // hi
      j<<=8;
      k=i-j; // lo
      j>>=8;
//      display_shadow[0]=display_shadow[2];
//      display_shadow[1]=display_shadow[3];
      display_shadow[2]=j;
      display_shadow[3]=k;
    }
      tm1637.display(display_shadow);
} 

void edgeDetect(){
    bool pinState = 0;                 // current state
    static bool lastPinState = 0;      // previous state
    pinState = b_mark;
    if (pinState!=lastPinState) pinChanged(pinState);
    lastPinState = pinState;
}
void pinChanged(bool state){
    static long previousMillis = 0; 
    unsigned long currentMillis = millis();
    int duration = currentMillis - previousMillis;
    previousMillis = currentMillis;
    Manch::decode(state, duration);
}

На глаз по светодиоду, уверенно распознаются пачки вплоть до 30 мс/полубит, то есть я могу повысить скорость в 5 раз. Ну норм, меня устроит. Но щас-то оно даже на 3 bps не работает? А потом мб в обход loop() и реще получится.

Voodoo Doll
Voodoo Doll аватар
Offline
Зарегистрирован: 18.09.2016

В строках 002 и 003 яшнадеюсь ясно что чтобы получить адрес репы надо дописать ввв.гитхуб.ком

алсо там и схемы по ссылкам. как у меня подключен экран написано в 018 (clk=7,dio=6)

Q: эмм, на██я, э, с какой целью зачем оно надо?

A: USB HID наоборот. Бездрайверный вывод. Тупа челик воткнул в HDA/AC97 разъём (десятипиновый такой, на маме) и у него всё работает. И не надо ставить ch341 говна и прочий ftdi.

Q: мамкин изобретатель лисапеда, возьми eth, не?

A: не ну так то даааа. А так то неееет...

Upd: мне не впадлу взять вторую атмегу и кинуть ей на Rx провод с D13 первой. Но хотелось бы на одной, тем более места во флеше ещё куча.

Voodoo Doll
Voodoo Doll аватар
Offline
Зарегистрирован: 18.09.2016

Зафрапсил небольшое хоумвидео чтоб доктору было понятно, на что жалуемся: https://youtu.be/O1LuhpLxdSw

Voodoo Doll
Voodoo Doll аватар
Offline
Зарегистрирован: 18.09.2016

Короче выбросил буфер, просто в очереди опрашиваю, работает. Докучи можно контрольных сумм повычислять но оно и так недурно. Но скорость хромает, и это не ардуина не успевает, это переходные процессы в звуковой. Так что реще 3 bps не получится. Максимум вдвое повысить, и то с ухищрениями. Тему думаю логично в проекты переместить. Оформлю в кучу нормально и выложу.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Voodoo Doll пишет:

Но скорость хромает, и это не ардуина не успевает, это переходные процессы в звуковой. Так что реще 3 bps не получится.

А ничего, что аналоговые модемы по 56к/с передавали? При полосе пропускания телефонной линии 3.1 кГц.

FoxJone
Offline
Зарегистрирован: 19.04.2019

Боже, что она несет? А можно просто ТЗ в смысле что вы хотите от него ожидать?

Плюс хоумвидео совершенно не то....

Voodoo Doll
Voodoo Doll аватар
Offline
Зарегистрирован: 18.09.2016

andriano пишет:
аналоговые модемы по 56к/с передавали? При полосе пропускания телефонной линии 3.1 кГц.

- Если ты знаешь что-то такое чего не знаю я, иди сюда и сядь на моё место.

(из диалога Фрай с Оуэнсом, Pitch black, реж. David Twohy, 2004 г.)

FoxJone боже тут ни при чём.

Logik
Offline
Зарегистрирован: 05.08.2014

Ну если коротко - все плохо. Пищим - не пищим заменить на двухтоновое. Манчестер требователен к стабильности фазы, джитер его погубил. Заменяй на протокол с разной длительностью 0 и 1. Не, ну понизить скорость - как бы тоже выход ;) БПФ, штука хорошая, но не надо ее совать туда, куда не лезет. Просто БПФ знают некоторые, избранные, чем сильно гордятся и дальше не учатся. Цифровой фильтр КИХ или ДПФ в данном случае намного лучше. Почитай про алгоритм Герцеля, на ДТМФ он делает обычный БПФ (Герцель - он тоже фурье, но не на равномерных частотных интервалах) как бык овцу. Я пробовал был из интереса сравнить. 

А чем те радиоканал на 1527 не канал? Нафига тот УЗ?

Voodoo Doll
Voodoo Doll аватар
Offline
Зарегистрирован: 18.09.2016

Logik, мне нужно чтобы оно работало на ПК, без драйверов. Например в виде хтмл страницы с вшитыми quoted-printable mp3 файлами. Ультразвук - очевидный канал, не светодиодом же на CD-ROM мигать?

FFT юзаю т. к. не могу найти/не могу заставить работать схемы эквалайзеров/фильтров и не располагаю приборами (у меня даже частотомера нет, лол) - это очевидный способ как вытащить нужные частоты из звука с минимумом деталей (всё равно ардуине эта задача по зубам, плюс сверхточность не нужна).

Logik
Offline
Зарегистрирован: 05.08.2014

Так повесь Нану на юсб, и юзай из броузера https://developer.mozilla.org/en-US/docs/Web/API/SerialPort  Я еще не пробовал, правда, но скоро попробую. А к нане уже - все что хош.

Приборы и схемы тебе не нужны. Тебе нужна хорошая математика. Вон те пример КИХ. http://digitrode.ru/computing-devices/mcu_cpu/988-detektor-urovnya-zvuka-na-arduino-svoimi-rukami.html Если б автор еще от плавающей запятой ушел - я бы сказал шо так и надо делать.

 

Logik
Offline
Зарегистрирован: 05.08.2014

Voodoo Doll пишет:

 вытащить нужные частоты из звука с минимумом деталей (всё равно ардуине эта задача по зубам, плюс сверхточность не нужна).

Эта задача как правило не по зубам авторам кода. А проц может очень многое. Я когда игрался с тем Герцелем видео снимал https://www.youtube.com/watch?v=i6ZzAEG1vaE там и БПФ на всю память, и генерация ДТМФ и уже не помню шо там еще было...

Morroc
Offline
Зарегистрирован: 24.10.2016

Какой ширины канал то нужен ? БПФ зачем если нужна одна частота ? Во времена 8ми битных МК такое детектили чуть ли не XORом, насколько я помню. В те же времена считывание/запись на магнитофон делались довольно простыми алгоритмами до скоростей ~4800, правда там контроль был вида считалось/не считалось вся запись целиком (такое сам делал), но имея такой рабочий код прикрутить туда избыточность и контроль/исправление ошибок несложно (хотя вот такое сам не делал).

Проц действительно многое может даже в плане звука, вон радиостанцию целую упихали однополосную с DSP обработкой - https://www.youtube.com/watch?v=9DjDdcnP0GE, но там в коде такое, что ну его нафиг :)

Voodoo Doll пишет:
Например в виде хтмл страницы с вшитыми quoted-printable mp3 файлами.

Не уверен, что кодер mp3 оставит в покое УЗ, он же выкидывает из сигнала все "лишнее" неслышимое по его мнению. Задача не особо понятна, imho 50/50, что в целом она решается более традиционными способами. Без дров же ничего не работает - если есть дрова для звуковухи почему нет дров под ардуину (можно взять не на китайском usb чипе) ? нипанятна...

sadman41
Offline
Зарегистрирован: 19.10.2016

Все просто. Вуду распродаст миллиарды девайсов, которые будут ждать свистка. И в час Хэ со странички в даркнете браузер выдаст ультразвуком кодовое слово "Дортмунд"...

Voodoo Doll
Voodoo Doll аватар
Offline
Зарегистрирован: 18.09.2016

sadman41, совершенно верно :)

Morroc если остались примеры (про xor и т п) то с удовольствием почитаю, может научусь чему-нибудь.

Logik
Offline
Зарегистрирован: 05.08.2014

Morroc пишет:

но там в коде такое, что ну его нафиг :)

Когда паталогическая тупость становится нормой, тогда код спеца становится "ну его нафиг".  Такова рожа современного ИТ. А по существу работы кода замечания есть? Отступы кривые? Переменные не так названы? Или любому дибилу не понятно, значить код сложен в поддержке дибилами? Или проц выбран неверно, надо ж мощней было...

Morroc
Offline
Зарегистрирован: 24.10.2016

По существу работы, конечно, нет, чувствуется, что выжато все что можно, к сожалению местами этого недостаточно, но многие довольны. Такое ощущение, что автору по приколу было не проект сделать, а именно упихать его в мегу, он же недавний совсем, выбор платформ уже был неплохой.

Voodoo Doll пишет:
если остались примеры

Не, оно все на магнитофонных кассетах еще было, выкинуто давно, когда носители посовременней пошли той темой не интересовался уже. Вроде там ничего хитрого, будет пару часов свободных - попробую сочинить что нибудь под дуину.

Voodoo Doll
Voodoo Doll аватар
Offline
Зарегистрирован: 18.09.2016
GarryC
Offline
Зарегистрирован: 08.08.2016

Посмотрите исходники АОНов, там решалась задача селекции 6 заданных частот при помощи 1 битного (после компаратора) АЦП.
И вполне себе удачно решалась на длительных интервалах.