Меню
+7 (495) 785-95-25
sale@lcard.ru
sale@lcard.ru
Страницы 1
Спасибо. Версию взял, проверил - утечки больше нет.
Добрый день.
При работе через ethernet утекло 40 байт, вот что показал valgrind:
40 bytes in 1 blocks are definitely lost in loss record 21 of 63
в main в main.cpp:26
1: malloc в /mnt/valgrind-3.10.0/coregrind/m_replacemalloc/vg_replace_malloc.c:296
2: osspec_mutex_create в /usr/local/lib/libe502api.so.1.1.12
3: f_iface_open в /usr/local/lib/libe502api.so.1.1.12
4: X502_OpenByDevRecord в /usr/local/lib/libx502api.so.1.1.12
5: E502_OpenByIpAddr в /usr/local/lib/libe502api.so.1.1.12
6: main в <a href="file:///appl/LCardE502ApiMemCheckTest/main.cpp:26" >main.cpp:26</a>
Судя по логу, теряется память в функции открытия через запись об устройстве. Специально для теста (чтобы исключить возможность утечки в нашей части ПО) был сделан чистый проект, в котором мы просто открыли устройство и тут же его закрыли. Ради эксперимента сделали также но с соединением через USB, утечки не было (по крайней мере valgrind ничего подозрительного не заметил). Проект в приложенном архиве.
Конфигурация запуска.
система Linux Debian
Distributor ID: Debian
Description: Debian GNU/Linux 8.10 (jessie)
Release: 8.10
Codename: jessie
Устройство E502, подключено через ethernet (в локальную сеть, ip выделен автоматически сеткой).
Если нужна схема подключения - скажите, сделаю.
Чтобы не тратить время на написание типовой програмы, проект в приложении (проект под Qt, но можно легко переделать и под голый C++). Чтобы сменить ip, в проекте нужно строку QString ipaddr("192.168.20.68"); заменить на строку с нужным ip.
Проект: http://rgho.st/6YmW5pFbw
PS. Возможно, я не очень внимательно прочитал руководство программиста, перед тем, как написать сюда - проверил, вроде как специальной процедуры закрытия соединения при работе через ethernet не предусмотрено, если не прав - прошу поправить.
Спасибо. Вроде всё прояснилось. По поводу делителя частоты пример с часами оказался очень удачным. Частота зависит от того, какую единицу времени мы выберем, мы можем выбрать какую-нибудь странную единицу (например иррациональное число секунд), в итоге численное значение частоты не будет иметь нетривиальных множителей, хотя внешне физика процессов не изменится.
Как я понял (после вашего объяснения), делитель частоты это любое целое число, если опорная частота равна f, то с помощью делителей мы можем получить любую частоту из ряда f, f/2, f/3, f/4, f/5, ..... (физически это скорее всего происходит просто как взятие каждого k-го отсчёта). То, что частота равна 2000000 Гц никак не влияет на возможности деления, а диапазон делителей от 1 до 1024*1024 это просто некоторое техническое ограничение (диапазон мог бы оказаться равен от 1 до 1024*1024 + 1).
Немного подведу итог по вопросам (для себя, и на случай, если кто ещё позже будет читать эту тему). Если в чём ошибаюсь - прошу поправить.
МГц это 10^6 герц и никак иначе. По степеням двойки метрические приставки допустимы только для единиц измерения информации (объёма памяти в компьютере) - для байт и для бит (но! по степеням двойки это неофициальная система, в документации и в официальных документах килобайт это всё же 1000=10^3 байт ! по крайней мере в РФ). Делитель это ни в коем случае не "множитель" в математическом смысле, это просто любое натуральное число от 1 до максимального ограничения (теоретически до бесконечности).
По поводу получения делителя частоты дискретников и получения шага передачи, пока их отсутствие для нас не критично, если что - напишу повторно, либо самостоятельно добавим их в api (оно по-моему открыто и доступно в репозитории, для теста можно будет немного подправить).
Насчёт шага тоже понял, USB может делить на блоки из своих соображений и поэтому точное знание шага нам ничего не даст.
То, что после вычитывания данных в памяти на плате остаются данные мешает тем, что в итоге нельзя считать время завершения команды Recv временем рождения последнего отсчёта в блоке, придётся править на число оставшихся в памяти отсчётов. Ну и вообще довольно странно получается - у нас почти стабильно остаётся 8192 отсчёта, "почти" выражается в том, что 1 из 10 считывания их там остаётся 0, остальные разы всегда одно и то же количество (8192). Будем экспериментировать дальше, пока проблем это не вызвало, просто показалось странным, вот мы и стали пытаться что-то менять.
Добрый день.
1. Функции действительно есть только для установки значений. Получения действительно не сделаны, т.к. Вы знаете, какое значение установили. В принципе при необходимости можно добавить и Get методы. По поводу пересчета из частоты в делитель, то можно использовать X502_CalcDinFreq(), подав на вход опорную частоту и частоту цифровых линий и получив на выходе делитель.
А частота (которая реально будет выставлена) будет равна отношению опорной частоты к значению делителя, верно?
2. А что Вы хотите добиться установкой явно шага прерывания? по какому интерфейсу работаете?
Работаем через USB. Мы обратили внимание на этот параметр, потому что у нас происходит странная вещь - когда мы запрашиваем много данных, скажем, 10^6 отсчётов, и ставим большой таймаут (подбираем так, чтобы функция Recv ожидала, но не весь таймаут), то после получения данных функция GetRecvReadyCount сообщала, что в буфере остались непрочитанные данные (и так было всегда, нуль в этой ситуации получить не удалось). Стабильный нуль удалось получить только когда мы сначала запрашивали, сколько в буфере есть данных, и потом ровно это количество и считывали. Но и в этой ситуации периодически в плате оставались данные (но в этой ситуации это может быть обусловлено задержками при передаче данных по USB). У нас возникло предположение, что устройство может отдавать данные лишь блоками, кратными величине шага, то есть если, например, шаг равен 100, а мы попросим 150 отсчётов, то он отдаст нам 150 отсчётов, но лишь когда в буфере будет 200 = 2*100 отсчётов (так как по достижении 150 прерывание не будет возбуждено). Поэтому мы пытались выставлять шаг, учитывая запрашиваемый объём данных (пытались делители считать) - получилось пока плохо. Возможно шаг не выставился - не понятно, как проверить, выставился ли он (функция отработала без ошибок, но вдруг она как и функция установки частоты чуть этот шаг изменила).
3. 2 МГц это 2000000 Гц. https://ru.wikipedia.org/wiki/%D0%93%D0 … %B8%D1%8F). В документации это может быть явно не указано, т.к. это обозначение стандартное и по нему как раз разночтений нет (это стандартные приставки СИ).
Байты с их производными по сути являются единственным исключением, где исторически используются двоичные приставки (Кило = 2^10 = 1024 вместо 1000). Хотя с байтами это как раз не так однозначно и в разных областях по разному и по некоторыми документами привычное использование считается некорректным.
Понял, значит 2 миллиона отсчётов в секунду. Попробую разобраться с делителями, может остальное про частоты станет ясно.
Вы ставите под сомнение, что технически любую опорную частоту можно физически поделить на любое целое число с помощью обычной цифровой (счётной) схемы деления частоты (c заданным любым целым коэффициентом деления)? - Если да, то это - тяжелейшее заблуждение.
Значит я заблуждаюсь... Я думал, что делить можно лишь нацело, мне казалось, что деление будет происходить взятием, скажем, каждого второго отсчёта, и в итоге частота поделится на два... Попробую найти статьи про эти частоты, если есть какие-то предпочтительные источники - напишите, пожалуйста.
PS. До встречи с устройством E502 я ни разу не сталкивался с подобными цифровыми устройствами, поэтому эти термины для меня совершенно новые, и я могу допускать очень глупые ошибки и заблуждения. Я даже не знаю, как именно работает счётная схема деления частоты, лишь предполагаю, что там взводится какой-то счётчик, с помощью которого берётся каждый k-й отсчёт, и в итоге происходит деление на k.
X502_DIN_FREQ_DIV_MAX = 1024*1024, страница 46 описания API для E502
По третьему вопросу: спор тут неуместен, см.
http://www.fundmetrology.ru/depository/01_npa/po879.pdfНаименование и обозначение единицы количества информации "байт" (1 байт = 8 бит) применяются с двоичными приставками "Кило", "Мега", "Гига", которые соответствуют множителям "2^10", "2^20" и "2^30" (1 Кбайт = 1024 байт, 1 Мбайт = 1024 Кбайт, 1 Гбайт = 1024 Мбайт). Данные приставки пишутся с большой буквы. Допускается применение международного обозначения единицы информации с приставками "K" "M" "G", рекомендованного Международным стандартом Международной электротехнической комиссии МЭК 60027-2 (KB, MB, GB, Kbyte, Mbyte, Gbyte)
В тоже время, там же см. ДЕСЯТИЧНЫЕ МНОЖИТЕЛИ, ПРИСТАВКИ И ОБОЗНАЧЕНИЯ ПРИСТАВОК ДЛЯ ОБРАЗОВАНИЯ КРАТНЫХ И ДОЛЬНЫХ ЕДИНИЦ ВЕЛИЧИН:
10^6 - мега - M(международное) - М(русское)В нашем случае частота - это не количество информации (в байтах-битах не измеряется).
Правильно ли я понял, что в таком случае делитель частоты должен быть делителем числа 2*10^6 ? То есть произведением двоек и пятёрок в количестве не более чем их будет в двух миллионах? Тогда непонятно, как можно установить делитель частоты 1024*1024, это число не является делителем 2*10^6, а в документации оно указано как максимально возможное значение делителя частоты.
Добрый день.
Для делителя частоты сбора данных с аналоговых каналов есть функция установки и получения, для дискретных есть только установка делителя. Есть ли способ получить делитель для дискретных каналов? Я понимаю, что можно получить его косвенно, получив частоту опроса дискретных каналов и поделив на неё опорную частоту, но это может оказаться неточно (так как частота опроса может быть нецелой). Вообще странно, что для аналоговых есть получение делителя, а для дискретных нету (или я плохо искал? всякое бывает)
Второй вопрос - можно ли как-то получить текущее значение шага для прерываний? Мы пытаемся установить свой шаг, и не уверены на 100%, что он установился ровно такой, который мы просили.
И третье, возможно я прозевал это в документации, опорная частота 2МГц это 2000000 раз в секунду, или 2*1024*1024 раз в секунду? (я всегда считал, что второе, но один из наших программистов со мной не согласен, поэтому решил уточнить).
Добрый день.
Сегодня взял данные, снятые устройством E502 с пьезодатчика (датчик установлен в реальном устройстве). В попытке извлечь информацию о величине давления, приложенного к датчику, попробовал применить преобразование Фурье. Обнаружилась интересная закономерность.
Если погасить низкие частоты (которые соответствуют всплеску напряжения - полезному сигналу с датчика), то спектр в целом равномерный, кроме частот примерно между 0.6 и 0.8, в этом диапазоне есть всплеск амплитуд. Правильно ли я понимаю, что это всплеск от внутренних колебаний устройства? Или же это полезный всплеск, несущий какую-то информацию, которую мы до сих пор не видели?
Если этот всплеск выделить и преобразовать обратно в вольты (обратным преобразованием Фурье), то получится сигнал, не превышающий 0.01 Вольт.
Кстати, только что подумал - это ведь может быть и шум с пьезодатчика... Лучше всего это проверить, сняв данные с невозбуждённого датчика (это смогу сделать позже)...
Нам нужно точное время снятие отсчёта, по тикам прибора мы бы смогли это время определить (по крайней мере думали, что смогли бы). Тики ПК, к которому подключено устройство, мы получить можем, но они нам малополезны.
Спасибо.
Проблемы могут возникнуть, если команды на АЦП проходят с задержкой, в итоге мы получим сдвиг по времени относительно реальных тиков. Но я понимаю, что запрос тиков также может пройти с задержкой, и будет то же самое.
Будем думать, как работать в этой ситуации. Есть подозрение, что время слегка "уплывает" (рассчитанное время), при том, что это не приводит к переполнению буффера. Что странно, потому что если мы вычитываем данные хоть немного медленнее, чем они считываются, мы бы переполнились.
Экспериментировать пока не можем, так как нам положили сервер, на котором установлено устройство. Позже, если проблемы останутся, напишу ещё раз.
Добрый вечер.
Устройство E502. Есть ли возможность получения тиков устройства? Нам это нужно, чтобы иметь возможность рассчитать время приёма каждого отсчёта.
Было бы идеально, если к каждому отсчёту прилагалось бы время снятия данных (в тиках). Но подойдёт также функция, которая сказала бы текущее значение тиков.
За 5 минут работы ни одного разрыва не было. Данные принимаются корректно (факт получения и их равенство реальному сигналу, измеренному вольтметром). Думаю, ошибка исправлена. Спасибо за помощь.
Вопрос с символическими ссылками закрыл (просто сам их пересоздал). Тестирую изменения.
Сейчас проверяю исправление, уже возникла проблема - символические ссылки libe502api.so и аналогичные указывают на старую версию библиотеки. Либо make install их не обновил, либо в сборке они неверно устанавливаются.
Открылись новые обстоятельства.
Ошибка возникает лишь в случае, если проект собран с флагом -pg, исправленный проект : http://rgho.st/8z7GSRdGN , собрать необходимо в debug-режиме, после этого запускать (можно как под отладчиком, так и без, ошибка воспроизводится).
Если убрать флаг -pg, ошибок не будет (по крайней мере я не дождался). Чтобы убрать этот флаг, нужно в pro-файле убрать две строки, которые его подключают (для компилятора и для линковщика, там их сразу видно).
Проект по ссылке это ранее выложенный проект (то есть чистый, без посторонних действий), но собранный с флагом -pg. Судя по всему, ошибка как-то связана именно с ним.
Опять накосячил со ссылками, вот верная ссылка на последнюю версию проекта : http://rgho.st/8CmqXpyzj .
Общий вопрос - от чего вообще может произойти ошибка -9 ? (ошибка чтения данных синхронного ввода)
Причину -140 нашёл - неиницилизированная переменная (n_channels). Добавил n_channels - channels.size();, теперь -140 нету.
Исправленный проект: http://rgho.st/6YlJjlFH9 .
Запустил приложение, пока разрыва связи нету. Значит проблема всё-таки в нашей архитектуре (скорее всего).
Итог на текущий момент - в чистом приложении ошибки не возникает. Буду пробовать получить ошибку в многопоточном приложении, если удастся - напишу снова (с приложением проекта). Тему можно пока закрыть (или оставить, на случай если удастся это ошибку повторить в чистом многопоточном приложении, но могу и другую тему создать).
Спасибо.
PS. Ранее не ответил на вопрос про ОС, ОС linux debian .
Исправленная ссылка на архив проекта: http://rgho.st/7rr8kchGP (ссылка в предыдущем сообщении потёрлась, что-то не то нажал на файлообменнике).
Сделал отдельный проект, в котором в цикле выполняется чтение с модуля. Ссылка на архив: http://rgho.st/7cXhFvy5L (проект под Qt4). Но пока есть проблемка - стабильно идёт -140 ошибка (неверный номер канала в буффере), это скорее всего какой-то явный ляп в коде у меня (наверняка memset сделал неправильно), сейчас разбираюсь, думаю с ней справлюсь (код взят ровно тот, что я выкладывал сообщением выше).
Штатный консольный пример это какой? Я кажется несколько запутался в материалах, и проглядел его. Код lqmeasstudio пытался разобрать, но он довольно большой, поэтому его пока не осилил.
usleep(1000000) в принципе не нужен, но если его убрать, то мы задёргаем модуль, так как цикл будет лететь с дикой скоростью. Однако скорее всего беды не будет - задержку сделает модуль, так как он физически не сможет выдавать столько отсчётов в секунду, и задержку сделает X502_Recv, в итоге мы будет также получать 2 миллиона отсчётов в секунду. Так что эта команда тут скорее как стиль программирования, чтобы отдельные процессы не сожрали все ресурсы.
Оставил только чтение, убрал вывод прочитанных данных и сохранение данных. Ошибка всё равно возникла.
Чтение происходит в отдельном потоке в бесконечном цикле вида:
lcardE502 dev("serial_number");
for(;;)
{
dev.getBlock();
}
Код, в котором производится выделение буфферов, установка их размеров и прочие вспомогательные действия:
#include <QVector>
#include "e502api.h"
#include "l502api.h"
class lcardE502
{
private:
t_x502_hnd hnd ;
bool hnd_opened;
int n_channels;
QVector<int> channels ;
const char* serial_;
public:
uint32_t* buffer ;
uint32_t buffer_size ;
double* adc_data ;
uint32_t adc_data_size;
static const uint32_t adc_data_size_basic = 2097152; // 2*1024*1024
uint32_t* din_data ;
uint32_t din_data_size;
static const uint32_t din_data_size_basic = 2097152; // 2*1024*1024
void make_channels_array();
int getBlock ( );
int openDevice ( );
int closeDevice( );
public:
explicit lcardE502(const char* serial);
virtual ~lcardE502();
};
lcardE502::lcardE502(const char* serial) : serial_(serial)
{
// Готовим описатель модуля
hnd_opened = false;
hnd = X502_Create();
// Выделяем память под буффера
buffer_size = 2097152;
buffer = new uint32_t[buffer_size];
adc_data_size = adc_data_size_basic;
adc_data = new double[adc_data_size];
din_data_size = din_data_size_basic;
din_data = new uint32_t[din_data_size];
// Сгенерируем массив используемых каналов
make_channels_array();
}
lcardE502::~lcardE502()
{
closeDevice();
X502_Free(hnd);
delete[] buffer;
delete[] adc_data;
delete[] din_data;
}
int lcardE502::openDevice()
{
// Открыть устройство
int err_code = E502_OpenUsb(hnd, serial_);
if (err_code == X502_ERR_OK)
{
// Сконфигурировать устройство
int conf_error = X502_SetLChannelCount(hnd, n_channels);
if (conf_error != X502_ERR_OK)
{
qDebug() << "error X502_SetLChannelCount : " << X502_GetErrorString(conf_error);
}
for (int i = 0; i < n_channels; ++i)
{
// Все каналы работают только в режиме с общей землёй
conf_error = X502_SetLChannel(hnd, i, channels[i], X502_LCH_MODE_COMM, X502_ADC_RANGE_10, 0);
if (conf_error != X502_ERR_OK)
{
qDebug() << "error X502_SetLChannel : " << X502_GetErrorString(conf_error);
break;
}
}
double freq = 2000000.;
conf_error = X502_SetAdcFreq(hnd, &freq, 0);
if (conf_error != X502_ERR_OK)
{
qDebug() << "error X502_SetAdcFreq : " << X502_GetErrorString(conf_error);
}
conf_error = X502_Configure(hnd, 0);
if (conf_error != X502_ERR_OK)
{
qDebug() << "error X502_Configure : " << X502_GetErrorString(conf_error);
}
hnd_opened = true;
qDebug() << "error X502_StreamsEnable : " << X502_GetErrorString(X502_StreamsEnable(hnd, X502_STREAM_ADC));
qDebug() << "error X502_StreamsStart : " << X502_GetErrorString(X502_StreamsStart(hnd));
}
else
{
qDebug() << "Opening error : " << err_code << " " << X502_GetErrorString(err_code);
}
return err_code;
}
int lcardE502::closeDevice()
{
int err_code = X502_Close(hnd);
if (err_code == X502_ERR_OK)
{
hnd_opened = false;
}
else
{
qDebug() << "Closing error : " << err_code << " " << X502_GetErrorString(err_code);
}
return err_code;
}
void lcardE502::make_channels_array()
{
channels.append(0);
channels.append(1);
}
int lcardE502::getBlock()
{
// Если не открыто соединение - открыть его
if (!hnd_opened)
{
openDevice();
usleep(1000000);
}
uint32_t next_channel = ~uint32_t(0);
int32_t err0 = X502_GetNextExpectedLchNum(hnd, &next_channel);
if (err0 != X502_ERR_OK)
{
qDebug() << X502_GetErrorString(err0);
return -1;
}
memset(buffer, 0, buffer_size*sizeof(buffer[0]));
int32_t err = X502_Recv(hnd, buffer, buffer_size, 1000);
if (err >= 0)
{
adc_data_size = adc_data_size_basic;
din_data_size = din_data_size_basic;
int32_t err2 = X502_ProcessData(hnd, buffer, err, X502_PROC_FLAGS_VOLT, adc_data, &adc_data_size, din_data, &din_data_size);
if (err2 != X502_ERR_OK)
{
qDebug() << "error X502_ProcessData : " << err2 << " " << X502_GetErrorString(err2);
return -1;
}
}
else
{
static int counter = 0;
++counter;
qDebug() << "error X502_Recv : " << err << " " << X502_GetErrorString(err) << counter;
closeDevice();
hnd_opened = false;
return -1;
}
return 0;
}
Здравствуйте.
При испытании под lqmeasstudio связь устойчива (при том же подключении, тех же настройках и физических условиях)?
Да, lqmeasstudio ошибок не выдаёт, данные в ней верные, за месяц активной работы только 1 раз в lqm была ошибка чтения, думаю это была случайность (может что-то с питанием).
Возникла проблема с получением данных с устройства E502.
Схема очень простая, на 1 канал подключён источник постоянного тока, подаётся напряжение от 0 до 8 вольт (можно менять), второй канал заземлён, остальные не используются (могу выложить чертёж схемы). Оба канала (1 и 2) запущены в режиме с общей землёй, запущен синхронный ввод, частота 2000000 (=2x10^6, 2 МГц).
Чтение происходит в бесконечном цикле, после каждого чтения выводим первый и последний отсчёт. Данные выдаются правильные, при изменении напряжения реакция мгновенная. Но есть одна проблема - периодически теряется связь. Функция X502_Recv возвращает ошибку -9, после чего дальнейшие попытки чтения оказываются бесполезными (постоянно летит ошибка -9), помогает только закрыть соединение и открыть его по новой. Тогда опять некоторое время будет успешное чтение, но потом ошибка появляется снова. В среднем время до потери связи составляет 20 секунд (даже 15).
Как бороться с этой проблемой?
Ниже код, так как скорее всего его всё равно нужно показать (может там явный мой косяк). Для укорочения кода убраны проверки кодов ошибок, а также описания временных массивов-буферов, также убраны описания классов, отвечающих за хранение данных. Если нужно ещё что-то - выложу.
Код чтения данных:
uint32_t next_channel = ~uint32_t(0);
int32_t err0 = X502_GetNextExpectedLchNum(hnd, &next_channel);
memset(buffer, 0, buffer_size*sizeof(buffer[0]));
int32_t err = X502_Recv(hnd, buffer, buffer_size, 1000);
if (err >= 0)
{
adc_data_size = adc_data_size_basic;
din_data_size = din_data_size_basic;
int32_t err2 = X502_ProcessData(hnd, buffer, buffer_size, X502_PROC_FLAGS_VOLT, adc_data, &adc_data_size, din_data, &din_data_size);
}
else
{
static int counter = 0;
++counter;
qDebug() << "error X502_Recv : " << err << " " << X502_GetErrorString(err) << counter;
return -1;
}
Код установления соединения:
int err_code = E502_OpenUsb(hnd, serial_);
if (err_code == X502_ERR_OK)
{
// Сконфигурировать устройство
int conf_error;
conf_error = X502_SetLChannelCount(hnd, n_channels);
for (int i = 0; i < n_channels; ++i)
{
conf_error = X502_SetLChannel(hnd, i, channels[i], X502_LCH_MODE_COMM, X502_ADC_RANGE_10, 0);
}
double freq = 2000000.;
conf_error = X502_SetAdcFreq(hnd, &freq, 0);
conf_error = X502_Configure(hnd, 0);
}
Страницы 1
Адрес: 117105, Москва, Варшавское шоссе, д. 5, корп. 4, стр. 2
Многоканальный телефон:
+7 (495) 785-95-25
Отдел продаж: sale@lcard.ru
Техническая поддержка: support@lcard.ru
Время работы: с 9-00 до 19-00 мск