Кольцевой буфер в Qt С++

Posted on 2017-02-02 00:32:00

Кольцевой буфер - буфер фиксированного размера, часто применяемый в шаблоне "Поставщик - Потребитель" (Producer-Сonsumer), когда, например, нужно организовать взаимодействие двух асинхронных друг к другу процесса - запись (Producer) и чтение (Сonsumer). Принцип работы кольцевого буфера - когда буфер заполнен, новые данные пишутся в начало. Отсюда и название. В общем, типичное решение в потоковой обработке данных, с которой я столкнулся при  проектировании архитектуры SDR приложения UnoSDR. Вместо кольцевого буфера можно применять очередь (FIFO) QQueue - но при этом уменьшается производительность из - за не фиксированного размера буфера. Если потребитель и поставщик находятся в разных потоках, совместный доступ к буферу нужно защитить с помощью мьютексов, которые не обьязательно должны быть в самом классе. 

Ниже представлен класс кольцевого буфера, реализованный на С++ с применением фреймворка Qt.

class RingBuffer : public QObject
{
    Q_OBJECT

public:
    explicit RingBuffer(quint32 _size, QObject *parent = 0);
    ~RingBuffer();

    void clean();
    bool read(float *data, quint32 len);
    bool write(float *data, quint32 len);
    float getSample();
    void addSample(float sample);
    quint32 availableRead(); // available size read data
    quint32 availableWrite(); // available size write data

private:
    quint32 size;
    quint32 mask;
    quint32 idxRead;
    quint32 idxWrite;
    quint32 dataCount; // available read data size
    float *buf = NULL;
};

 

RingBuffer::RingBuffer(int type, quint32 _size, QObject *parent)
    : QObject(parent),
      size(_size)
{
    mask = size - 1;
    fbuf = (float*) malloc(sizeof(float) * size);
    clean();
}

RingBuffer::~RingBuffer()
{
    free(fbuf);
}

void RingBuffer::clean()
{
    idxRead = 0;
    idxWrite = 0;
    dataCount = 0;
}

bool RingBuffer::write(float *data, quint32 len)
{
    if(availableWrite() < len)
        return false;

    dataCount += len;

    for(int i = 0; i < len; i++)
    {
        fbuf[idxWrite++] = data[i];
        idxWrite &= mask;
    }

    return true;
}

bool RingBuffer::read(float *data, quint32 len)
{
    if(availableRead() < len)
        return false;

    dataCount -= len;

    for(int i = 0; i < len; i++)
    {
        data[i] = fbuf[idxRead++];
        idxRead &= mask;
    }

    return true;
}

float RingBuffer::getSample()
{
    dataCount --;
    float sample = fbuf[idxRead++];
    idxRead &= mask;
    return sample;
}

void RingBuffer::addSample(float sample)
{
    dataCount ++;
    fbuf[idxWrite++] = sample;
    idxWrite &= mask;
}

quint32 RingBuffer::availableRead()
{
    return dataCount;
}

quint32 RingBuffer::availableWrite()
{
    return size - dataCount;
}

Размер буфера должен быть равен степени двойки. Для оптимизации быстродействия, в методах read и write в цикле for можно убрать наложение маски mask, и присваивать новые значения указателем на позицию чтения и записи другим путем.

Нормальным функционированием буфера считается, когда указатель на запись не догоняет указатель на чтение, т.е. потребитель успевает прочитать все данные, сгенерированные поставщиком, а поставщику есть куда писать данные. Другими словами скорость чтения должна быть равна скорости записи информации. И конечно же, нужно отлавливать и обрабатывать ситуации, когда данный принцип нарушается, что бы программа вела себя предсказуемо. Для этого в классе есть два метода - availableRead и availableWrite, которые возвращают количество доступных данных для чтения и размер области буфера, доступный для записи. Эти методы нужно вызывать всякий раз, когда вы пользуетесь другими двумя методами - getSample и addSample. Для оптимизации быстродействия, проверки были выкинуты из классов, так как они могут применятся в цикле. Если нужна запись или чтение блока данных, то можно использовать методы write и read, где уже встроена проверка.

Релиз фреймворка Qt 5.8

Posted on 2017-01-24 00:07:03

Так случилось, что я пристально следил за датой релиза Qt 5.8, которую несколько раз переносили. И вот, наконец, сегодня можно скачать Qt 5.8

Первым делом, я проверил компиляцию моего проекта, который использует OpenGL ES 2.0 - в новой версии Qt значительно переделали рендеринг quick, добавили программный рендеринг, Direct, а так же убрали зависимости OpenGL - это меня очень настрораживало. Но, проект компилируется без ошибок!

Так же, в Qt 5.8 добавили долгожданные quick controls - MenuBar, Menu, SystemTrayIcon, ColorDialog, FontDialog, MessageDialog, FileDialog and FolderDialog и др. Обо всех новых вкусностях вы можете узнать здесь

Цифровые фазовращатели в SDR технике

Posted on 2017-01-18 22:35:25

В SDR технике часто применяется квадратурное представление сигналов. Ниже представлены варианты цифровых фазовращателей, для формирования квадратурного сигнала гетеродина приемника или передатчика.

Самая распостраненная схема в SDR технике - счетчик Джонсона. Вход - LOin, на выходах CLK0 и CLK1 сдвинутые на 90 град друг относительно друга меандры. При этом на входе скважность сигнала может быть любая.

Johnson Counter SDR

Из недостатков можно отнести то, что выходная частота в четыре раза меньше входной. Из - за этого максимальная выходная частота такого формирователя равна примерно ~ 40 .. 55 МГц, если применять 74LVC74.

Вторая схема делит входную частоту на 2, но имеет еще более худшую точность, чем первая схема. Логические элементы "Исключающее ИЛИ" формируют прямой и инверсный сигналы, которые подаются на два независимых триггера, которые и делят частоту на два (180 град / 2 = 90 град). Недостаток схемы - неопределенность I и Q выхода при подаче питания. Теоретически можно устранить, заменив элементы "исключающее или" на один инвертор (не проверял, поэтому не могу утверждать), но тогда ухудшится точность получения фазового сдвига, которая к тому же теперь будет зависеть от входной частоты.

Цифровой фазовращатель

Ну и последний вариант на RC - цепочке. Входная частота равна выходной, из - за этого ограничен диапазон частот работы фазовращателя.

RC-фазовращатель для SDR приемника

На одной RC цепочке невозможно получить фазовый сдвиг в 90 град., потому что в таком случае выходное напряжение фазовращателя будет равно нулю. Нужно минимум две RC - цепочки, каждая из которых дает вклад в 45 град, а между ними стоит усилитель, который нормирует сигнал к логическому уровню. В качестве усилителя применен инвертор. Такая схема оптимальна для простых SDR приемников, если в качестве гетеродина применен LC - генератор. Неточность фазового сдвига компенсируется алгоритмами автоматической компенсации дисбаланса при цифровой обработке сигнала. Сдвиг в 90 град не является обьязательным условием, он может быть и 110 или 20 град ... Так что по большому счету, неточность фазового сдвига в таком случае ни на что не влияет. Это даже можно использовать как полезное свойство! Зависимость фазового сдвига от частоты линейна, а значит по измеренной фазовой ошибке можно ориентироваочно узнать, на какой частоте работает приемник, без использования частотомера! Я подумывал реализовать такую функциональность в UnoSDR, но пока отложил в ящик.

Параметры RC цепочки расчитываются так, что бы в верхней рабочей частоте фазовый сдвиг был не более 45 град, так как с повышением частоты падает уровень выходного сигнала, т.е. активное сопротивление должно быть равно реактивному - 1/(6.28*C*F).

 

Простой SDR приемник для звуковой карты

Posted on 2016-12-25 23:11:25

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

Простой SDR приемник для звуковой карты

ОУ включен для однополярного питания. Смещение подается на неинвертирующий вход. Блягодаря этому усиление ОУ можно выставлять достаточно большим. Цепочка C2 C3 R3 R4 создает ВЧ фильтр, с помощью которого уменшается усиление на НЧ, на частотах, близких к нулю.  Сигнал гетеродина подается на вход LOin.

Но.. и эта схема имеет недостаток, или особенность применения. Она расчитана на питание от батарейки, или от импульсного блока питания с малыми пульсациями сетевой частоты. Если вы планируете применять линейный блок питания, вам нужно позаботиться о хорошем фильтре, или применять двухполярное питание.

Так же, если вы наблюдаете помехи от компьютера, рекомендую прочесть эту статью

На последок, фото моей конструкции на макетной плате:

simple sdr receiver for soundcard

И обратная сторона:

простой sdr приемник для звуковой карты

SDR приемник на звуковой карте поддерживает программа UnoSDR и QuestaSDR а так же android приложение в виде клиент - серверной архитектуры.

First 1 2 3 4 5 6 7 8 9 10 11 12 13 Last