Разработка sdr-драйвера LabVIEW

Радиотехника, платы, схемы, оборудование, фото- видео- приборы, компьютеры
Аватара пользователя
Mad_Ahab
junior
junior
Сообщения: 66
Зарегистрирован: 12 янв 2015, 16:33
Версия LabVIEW: 2013
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение Mad_Ahab »

В общем, на данный момент я переделал библиотечную С++ функцию чтения данных из прибора: теперь она принимает в качестве параметра фиксированный приемный буфер, а также указатель на переменную, в которой будет храниться количество принятых из прибора байт. Теперь, после вызова функции чтения, будет записано что-то в приемный буфер, и известно в явном виде какого размера это что-то в байтах.
Теперь нужно сделать быстрое создание массива в :labview: по заданному размеру с заданным содержанием, потом этот получившийся массив, который является сообщением, предстоит поместить в очередь.
Курю маны, дым уже глаза ест...
Изображение
Аватара пользователя
Vitekkz88

Activity Silver Автор
expert
expert
Сообщения: 1100
Зарегистрирован: 21 янв 2014, 15:45
Награды: 3
Версия LabVIEW: 12,13,14
Откуда: Томск
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение Vitekkz88 »

То есть, я хотел бы в очередь помещать не голый поток 8-разрядных слов, а именно сообщения, чтобы потом их парсить. Как можно сделать это в :labview: ?
На ум приходит только функция Concatenate Strings, выполненная очень быстро при получении данных для формирования сообщения перед размещением его в очереди - только это функция для какого-то фиксированного числа строк, поданных на вход, чего обеспечить я не смогу.Теперь нужно сделать быстрое создание массива в :labview: по заданному размеру с заданным содержанием, потом этот получившийся массив, который является сообщением, предстоит поместить в очередь.
Вы что передаёте? Текст что ли? Или у вас смесь из данных и текста? Коль функция чтения возвращает массив данных uint8 - то и на строне LabVIEW принимайте в uint8. Если Вы по TCP/IP принимать данные будете и всё-таки планируете со строками работать - то ведите счетчик принятых байт. Я не знаю, какая у Вас структура пакета и протокол передачи. Если в заголовке первым словом идет величина пакета - то заводите условие на приём данных:
если размер принятого сообщения менее значения первого слова, то читаем дальше. Считанные ранее байты объединяем.
Если данные отправляются в uint8, то и конвертируйте их сразу в uint8 и принимайте сразу весь массив(блоком).
Если одного блока мало для обработки и отображения - копите таких блоков N штук хоть в регистре, хоть в очереди.
Пример накопления данных в регистре приложил. Для очередей делается по аналогии, только контроль ведется по кол-ву элементов в очереди.
И еще, не мешайте всё в один канал. Гораздо удобней(на мой взгляд) использовать два канала: канал на передачу данных и канал на передачу текстовых сообщений или служебной информации.
Вложения
Data.png
Untitled 1.vi
(13.28 КБ) 180 скачиваний
Инженер - это открыто светящийся интеллект, свободный и не обидный юмор, это легкость и широта мысли...Это воспитанность, тонкость вкусов, хорошая речь, плавно согласованная и без сорных словечек...
-А. И. Солженицын
Аватара пользователя
Mad_Ahab
junior
junior
Сообщения: 66
Зарегистрирован: 12 янв 2015, 16:33
Версия LabVIEW: 2013
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение Mad_Ahab »

Большое спасибо за ответ, офигенно :super: , сразу много начал понимать.
Vitekkz88 писал(а): Вы что передаёте? Текст что ли? Или у вас смесь из данных и текста?

- Да, я принимаю смесь данных и текста.
И еще, не мешайте всё в один канал. Гораздо удобней(на мой взгляд) использовать два канала: канал на передачу данных и канал на передачу текстовых сообщений или служебной информации.
- Пожалуйста, расскажите чуть подробнее. Как я их разделю? Все равно данные идут сплошным потоком. Мне придется при парсинге делать данные - какие-то на консоль/экран, какие-то - дальше на обработку, Вы имели в виду это?

Я разбираю Ваш прибор, пытаюсь понять как он работает и вижу следующее:
создаются очереди, одна для данных, другая - для буферизации. Также задается массив, пусто, он передается отдельно в цикл накопления данных, и в цикл обработки (буферизации) и в цикл визуализации. Меня интересует большой цикл буферизации: Вы передаете туда массив, а также данные, полученные в параллельном независимом цикле в виде очереди, затем пополняете массив данными из очереди данных, проверяете размер получившегося массива, если он оказывается больше предельного заданного размера буфера, то Вы отрезаете от массива часть буферного размера и помещаете снова в очередь, а остаток - куда Вы его отправляете? Обратно в массив? А как он обновляется?

Я спрашиваю, потому что у меня возникла проблема с чтением данных Error 1097 в библиотечной функции read, эта функция пытается записать данные по недоступному для нее адресу - это я выяснил указанным Вами (замечательным!) способом отладки. Очевидно, я неправильно передаю массив в эту функцию, потому что она вылетает только при повторном и далее вызовах... То ли я удаляю указатель на массив где-то в :labview:.
Поэтому я и спрашиваю - каким образом после такого цикла можно корректно обновить массив чтобы снова его туда же в цикл передать? Сдвиговые регистры, если протянуть массив через весь цикл тоже дадут такую ошибку (в точку 1 - первый регистр, в точку 3 - второй, а в точку 2 - ответвление, проведенное до точки 3)
Безымянный1.jpg
И еще тут встал вопрос: как взаимодействуют :labview: и поток, запущенный DLL? То есть, например, если у меня DLL запускает прием и складирование данных в очереди, сама их там где-то формирует и прочее, и делает это - в отдельном потоке (адресное пространство DLL), то как потом можно из :labview: получить доступ к этому автономному, отдельному потоку DLL, который где-то-то там крутится?
Курю маны, дым уже глаза ест...
Изображение
Аватара пользователя
Mad_Ahab
junior
junior
Сообщения: 66
Зарегистрирован: 12 янв 2015, 16:33
Версия LabVIEW: 2013
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение Mad_Ahab »

В общем, я справился с функцией чтения данных, она отрабатывает корректно. Теперь вопрос: как можно заставить ее работать параллельно с выполнением программы в :labview: ?
Я могу просто на диаграмме расположить цикл в самом начале, точнее, в первом, отдельном кадре открытой последовательности в виде которой у меня сейчас существует программа, который будет крутиться и вычитывать данные из очереди (вопрос с очередью все еще открыт) до тех пор, пока работает программа? Если да, то как? Или есть более простой путь, например, сделать управлением событиями?

Вообще, в идеале управление должно выглядеть следующим образом: на панели управления есть тумблер, по щелчку которого начинают отображаться данные - те, что текстовые - в дисплей, иные - на диаграмме, или просто выгружаться в файл, к примеру. Потом перебрасываем тумблер или отжимаем кнопку, и данные перестают отображаться. При этом - и до отображения, и после окончания, они продолжают поступать. Интерфейс просто иногда, по желанию пользователя, "подсоединяется" к потоку данных и их отображает.
Я в принципе представляю как это сделать - я могу сделать callback из DLL: библиотека, при вызове, запустит функцию чтения, создаст для нее поток, и передаст наверх - в :labview: , ссылку на этот поток, в нем будет крутиться цикл, который в одну и ту же область памяти будет перезаписывать вновь поступающие данные, и программа в :labview: будет по своему усмотрению (требованию пользователя) читать и отображать данные из этой области памяти.
Но я не хочу это делать в DLL, я хочу запустить отдельный поток в :labview: для чтения данных, который будет вне зависимости от событий программы все время крутиться, перезаписывая область памяти, как я уже описывал раньше, и прекращая это делать только с прекращением сеанса работы с прибором. Вот как это реализовать?
Курю маны, дым уже глаза ест...
Изображение
Аватара пользователя
dadreamer

Activity Professionalism Автор
professor
professor
Сообщения: 3926
Зарегистрирован: 17 фев 2013, 16:33
Награды: 4
Версия LabVIEW: 2.5 — 2022
Благодарил (а): 11 раз
Поблагодарили: 127 раз
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение dadreamer »

Mad_Ahab писал(а):Но я не хочу это делать в DLL, я хочу запустить отдельный поток в :labview: для чтения данных, который будет вне зависимости от событий программы все время крутиться, перезаписывая область памяти, как я уже описывал раньше, и прекращая это делать только с прекращением сеанса работы с прибором. Вот как это реализовать?
А нельзя завести отдельную функцию получения буфера с данными (GetBuffer), как это обычно во множестве продуктов сделано? А в этой функции вы делаете обычное копирование из ваших внутренних потоков/буферов в этот буфер:

Код: Выделить всё

memcpy(LV_Buffer, Your_Buffer, Buffer_Length)
В :labview: просто вызываете GetBuffer, передавая туда массив U8 (указатель) и размер (длину) в байтах.
Mad_Ahab писал(а):И еще тут встал вопрос: как взаимодействуют :labview: и поток, запущенный DLL? То есть, например, если у меня DLL запускает прием и складирование данных в очереди, сама их там где-то формирует и прочее, и делает это - в отдельном потоке (адресное пространство DLL), то как потом можно из :labview: получить доступ к этому автономному, отдельному потоку DLL, который где-то-то там крутится?
Мне кажется, управлять этим потоком из :labview: не получится, т.к. он находится в другом контексте, хотя вы можете это проверить :crazy: Запустите поток в DLL, передайте в :labview: хэндл, потом попробуйте завершить его из :labview: через TerminateThread. Проделайте это несколько раз, потом закройте все :vi: . Если :labview: не упадёт и в логе ошибок ничего не отобразится, то можно сказать, что такие манипуляции имеют право на жизнь. Но по идее нужно искать более элегантное решение, а то мало ли, может ваша библиотека завершит поток раньше, чем :labview: ... Посмотрите вот эту тему, может, что-то полезное там найдёте.
Последний раз редактировалось dadreamer 27 фев 2015, 19:15, всего редактировалось 1 раз.
Аватара пользователя
Mad_Ahab
junior
junior
Сообщения: 66
Зарегистрирован: 12 янв 2015, 16:33
Версия LabVIEW: 2013
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение Mad_Ahab »

Я скачал :labview: - драйвер для осциллографов PICO: https://www.picotech.com/downloads (содержится в Development Kit), и, хоть у меня и не было DLL для этих осциллографов, мне удалось посмотреть :vi: для работы с ними. В нем есть функция stream, которая является subVI, состоящим из вызовов функций библиотеки - и среди них есть функция ps2000_run_streaming_ns, которая, по всей видимости (мне так показалось), запускает поток и в нем уже собирает и складирует информацию, в соответствии с переданными в нее параметрами настройки буфера.
Значит, и мне стоит организовать у себя нечто такое же, и я попробую вариант с использованием функции _beginthreadex из process.h для WinAPI. Хотел не использовать нативный код, но, видимо, уже не объехать.
О результатах отчитаюсь.
Курю маны, дым уже глаза ест...
Изображение
Аватара пользователя
Mad_Ahab
junior
junior
Сообщения: 66
Зарегистрирован: 12 янв 2015, 16:33
Версия LabVIEW: 2013
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение Mad_Ahab »

dadreamer писал(а): В :labview: просто вызываете GetBuffer, передавая туда массив U8 (указатель) и размер (длину) в байтах.
С удовольствием бы, вот только буфер постоянно перезаписывается, скорость передачи данных потом будет до 80 Мбайт/с, и данные в буфер DLL сыпятся постоянно, и остается главный вопрос: как мне сделать постоянный просмотр этого буфера, чтобы из него копировать в буфер :labview: данные? Я с великим удовольствием так сделал бы, но проблема все та же: мне нужно не в каком-то месте программы копировать его один раз себе, а постоянно обновлять буфер :labview: данными из буфера DLL. Как это сделать параллельно с выполнением программы в :labview: ?
Курю маны, дым уже глаза ест...
Изображение
Аватара пользователя
dadreamer

Activity Professionalism Автор
professor
professor
Сообщения: 3926
Зарегистрирован: 17 фев 2013, 16:33
Награды: 4
Версия LabVIEW: 2.5 — 2022
Благодарил (а): 11 раз
Поблагодарили: 127 раз
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение dadreamer »

Mad_Ahab писал(а):Как это сделать параллельно с выполнением программы в :labview: ?
Я не хотел бы опять привязываться к "extcode.h", но там есть такая полезная функция, как PostLVUserEvent. Если в :labview: зарегистрировать стандартными :vi: пользовательский эвент, передать его хэндл в DLL, и оттуда вызвать PostLVUserEvent, то сработает Event-структура в :labview: (её нужно будет разместить предварительно на диаграмме). Таким образом можно ловить данные из различных callback функций, например из libvlc библиотеки и т.п. Пример: http://labviewportal.org/viewtopic.php?p=48149#p48149
Аватара пользователя
Mad_Ahab
junior
junior
Сообщения: 66
Зарегистрирован: 12 янв 2015, 16:33
Версия LabVIEW: 2013
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение Mad_Ahab »

Вы стреляете только крупным калибром, и как всегда, попадаете) Спасибо за отличный пример :super:
dadreamer писал(а):... то сработает Event-структура в :labview: (её нужно будет разместить предварительно на диаграмме).
То есть, я в event-структуру упаковываю всю свою программу, выходит? Чтобы осуществить изначальную подписку на событие появления данных в буфере, т.к. они начнут возникать там еще при настройке прибора, я правильно Вас понял?
И мне тоже придется подключать и использовать kernel32.dll для GetProcAddress?
Курю маны, дым уже глаза ест...
Изображение
Аватара пользователя
dadreamer

Activity Professionalism Автор
professor
professor
Сообщения: 3926
Зарегистрирован: 17 фев 2013, 16:33
Награды: 4
Версия LabVIEW: 2.5 — 2022
Благодарил (а): 11 раз
Поблагодарили: 127 раз
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение dadreamer »

Mad_Ahab, можно оставить инициализацию и конфигурирование за бортом эвента. Ну и, соответственно, закрытие устройства и прочую очистку. У вас же новые данные начнут поступать после аналога функции Start/Play/...? Или же сразу после инициализации? Vitekkz88 предложил хорошую организацию программы с помощью очередей. Я бы разместил эвент в отдельном потоке :labview: , а всю остальную обработку с конечным буфером в :labview: вынес бы в другой поток (чтобы не тормозить получение данных, т.к. эвент будет выполняться с большой частотой и там не должно быть ничего лишнего: ни задержек, ни работы с массивами - только копирование по указателю в массив :labview: и опционально помещение в очередь). Похожим образом я работал с видеокамерами, пользовательский эвент всегда выполнялся параллельно с остальным кодом. Остановка программы реализована через нотифаер, т.к. это удобно при работе с множеством потоков LabVIEW.
И мне тоже придется подключать и использовать kernel32.dll для GetProcAddress?
Это было сделано для установки Callback'а для API видеокамеры. Если у вас нет подобных функций, то можно это и не делать.

Попробуйте для начала создать простенькую DLL, генерирующую случайный массив, и передать этот массив через PostLVUserEvent, а в :labview: получить его. Поэкспериментируйте, может быть, вам этот способ вовсе не подойдет.
Аватара пользователя
Mad_Ahab
junior
junior
Сообщения: 66
Зарегистрирован: 12 янв 2015, 16:33
Версия LabVIEW: 2013
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение Mad_Ahab »

dadreamer, да у меня после автодетекта появляются первые данные в буфере, затем при установке конфигурации прибора в буфере появляются отклики от DSP, которые уже имеют текстовый вид, появляются там они вместе с потоком всяких статусных данных от ПЛИС и DSP. И вот потом, собственно, по усмотрению пользователя "Start/Play" так сказать. И тоже пойдут данные, после указания параметров приема.
dadreamer писал(а):Я бы разместил эвент в отдельном потоке :labview: , а всю остальную обработку с конечным буфером в :labview: вынес бы в другой поток (чтобы не тормозить получение данных, т.к. эвент будет выполняться с большой частотой и там не должно быть ничего лишнего: ни задержек, ни работы с массивами - только копирование по указателю в массив :labview: и опционально помещение в очередь).
Вопрос дня: а как мне организовать эту самую многопоточность? Это просто циклы? Я с радостью применю, только никак понять не могу эту "многопоточность" - мне нужен не Data Flow, а именно поток как единица планирования ОС. Извините, я просто не знаю как это выполняется в :labview: , и гуглю видимо неправильно.
dadreamer писал(а):dadreamer, Это было сделано для установки Callback'а для API видеокамеры. Если у вас нет подобных функций, то можно это и не делать.
Если у меня нет каких именно функций?

Большое спасибо.
Курю маны, дым уже глаза ест...
Изображение
Аватара пользователя
dadreamer

Activity Professionalism Автор
professor
professor
Сообщения: 3926
Зарегистрирован: 17 фев 2013, 16:33
Награды: 4
Версия LabVIEW: 2.5 — 2022
Благодарил (а): 11 раз
Поблагодарили: 127 раз
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение dadreamer »

Mad_Ahab, тогда вам придётся зарегистрировать эвент и отослать в DLL его хэндл до автодетекта. Чтобы :labview: был готов к приёму данных и ничего не потерялось.
Вопрос дня: а как мне организовать эту самую многопоточность? Это просто циклы?
Два не связанных проводами цикла While - это два потока :labview: . Поток данных в :labview: идёт по проводам, это базовый принцип data flow. Если у вас в программе имеется две или более веток проводов - то столько же и будет потоков при запуске программы. Если взять опять же диаграмму Vitekkz88 с очередями, то там образуется три потока: генератор данных, обработчик и приёмник (или два приёмника, без разницы). На самом деле такое представление грубовато, но для начала сойдёт. Подробнее можете почитать здесь.
Если у меня нет каких именно функций?
Функций для установки пользовательского callback'а (SetCallback и т.п.). API видеокамеры не знает, какую функцию ему вызывать при поступлении новых данных. Вот я ему и указываю на неё с помощью получения её адреса в памяти и передачи этого адреса в API через SetCallback. Дальше уже API будет обращаться каждый раз к моей функции. Но вы можете пропустить этот этап и просто постить новые события при поступлении очередных данных внутри вашей библиотеки.

В качестве примера сделал вот такую библиотеку:

Код: Выделить всё

#include "stdafx.h"
#include "extcode.h"

LVUserEventRef *LVEvent;
uInt8 Array[100]; //тестовый массив
uInt8 *pArray = Array; //указатель на массив

extern "C"{
__declspec(dllexport) void SendEvent(LVUserEventRef *EventRef);
__declspec(dllexport) void GenerateArray();
}

//запоминаем ссылку на event
__declspec(dllexport) void SendEvent(LVUserEventRef *EventRef)
{
	LVEvent = EventRef;
}

__declspec(dllexport) void GenerateArray()
{
float64 xp = 0;
int32 i = 0;
//заполняем массив случаными числами
for (i=0; i<100; i++)
{
	RandomGen(&xp);
	Array[i] = (uInt8)(xp * 255);
}
//передаём указатель на него в LabVIEW
//(LabVIEW автоматически разыменовывает указатель (void *),
//так что берём не сам указатель на массив, а его адрес)
PostLVUserEvent(*LVEvent, (void *)&pArray);
}
Диаграмма в LV:
2015-02-28_1-15-52.jpg
Для того, чтобы разобраться с PostLVUserEvent, этого хватит в самый раз. Заодно и продемонстрирована работа нотифаера.
Вложения

[Расширение dll было запрещено, вложение больше недоступно.]

PostArray.vi
lv2011
(19.51 КБ) 164 скачивания
Аватара пользователя
Vitekkz88

Activity Silver Автор
expert
expert
Сообщения: 1100
Зарегистрирован: 21 янв 2014, 15:45
Награды: 3
Версия LabVIEW: 12,13,14
Откуда: Томск
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение Vitekkz88 »

Mad_Ahab, я не понимаю Вашу озабоченность на счет приёма данных. В примере, который выше приводил, добавляйте отдельный цикл с вложенной event-cтруктурой. В event-cтруктуре обрабатывайте события с GUI.
Сделайте отдельную функцию чтения данных с прибора и вызывайте эту .dll-функцию в потоке захвата данных в LabVIEW.
У Вас должна получится следующая последовательность в приложении: Открытие сессии работы с прибором, пуск, захват данных,завершение работы. Я Вам картинку приложил, взята с http://www.ni.com/tutorial/3382/en/ . Только там единичный захват данных, пример для осциллографа. А у Вас непрерывный захват, значит нужно в цикл взять функцию захвата(чтения) данных. Всё остальное - это предварительные настройки. Обработку ошибок пока оставим на потом.
Мне не ясно, удалось ли Вам выполнить чтение данных?
Если да, то в цикле вызывайте эту функцию и проверяйте ( в примере в цикле "Поток захвата данных" вместо цикла for вставляйте функцию чтения данных).
Если у Вас сложность с парсингом, то опишите структуру пакета и мы разберемся как реализовать парсинг. Или у Вас пакеты имеют разную структуру?
Повторюсь, я бы не стал смешивать всё в один поток: и данные и текстовые сообщения. Это маразм какой-то...извините за грубость.
Передавайте данные функцией "Чтение данных" и текстовые сообщения функцией "Чтение сообщений". Данные могут поливать непрерывным потоком.А выполнять чтение текстовых сообщений можете хоть каждую секунду в отдельном потоке.
Вложения
Untitled_1d.png
Untitled 1.vi
(17.83 КБ) 167 скачиваний
Пример
Пример
Инженер - это открыто светящийся интеллект, свободный и не обидный юмор, это легкость и широта мысли...Это воспитанность, тонкость вкусов, хорошая речь, плавно согласованная и без сорных словечек...
-А. И. Солженицын
Аватара пользователя
Mad_Ahab
junior
junior
Сообщения: 66
Зарегистрирован: 12 янв 2015, 16:33
Версия LabVIEW: 2013
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение Mad_Ahab »

Огроменное спасибо, Vitekkz88 и dadreamer.

Все резко стало понятнее. Да, мне удалось прочесть данные из устройства, и теперь я медленно но верно реализую чтение данных в отдельных потоках. Пришлось ради этого сделать несколько правок в коде DLL, а также добавить в нее несколько функций.
В процессе добавления я использовал фрагмент кода, предоставленного dadreamer:

Код: Выделить всё

PostLVUserEvent(*LVEvent, (void *)&pArray);
При попытке компиляции выскакивает ошибка линковщика №2019: driver_tcp_labview.obj : error LNK2019: unresolved external symbol _PostLVUserEvent . То есть, линковщику нужна DLL, в которой реализована функция PostLVUserEvent.
Где взять эту DLL?
Курю маны, дым уже глаза ест...
Изображение
Аватара пользователя
Mad_Ahab
junior
junior
Сообщения: 66
Зарегистрирован: 12 янв 2015, 16:33
Версия LabVIEW: 2013
Контактная информация:

Re: Разработка sdr-драйвера LabVIEW

Сообщение Mad_Ahab »

Отвечу сам себе, а также для тех, кто пойдет за мной:
библиотека labviewv.lib лежит в папке C:\Program Files (x86)\National Instruments\LabVIEW [ваша версия LV]\cintools, подключить ее в MS VC++ можно кликнув правой клавишей на проекте в Solution Explorer -> Properties и прописав в Additional Dependencies после точки с запятой в конце указанный путь до статической библиотеки.
Это неправильный способ, но приемлемый, вообще рекомендуется все это собирать с помощью CMake, и там указывать соответствующие цели для линковки.
Курю маны, дым уже глаза ест...
Изображение
Ответить
  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Железо»