Меню

+7 (495) 785-95-25
sale@lcard.ru
sale@lcard.ru
|
||||
|
Проблемы с записью данных платы L791Здравствуйте.
void *DeviceAPI::detectThreadFunc(void *arg)
}
Как только данные обнаружены на каком-либо канале, запускается поток чтения. Код ниже:
И все вроде работает, но, данные пишутся с двух каналов одновременно. Даже если подключен один микрофон, пишутся одинаковые данные с двух каналов! Например если к 0 каналу подключен микрофон и на него начинают поступать данные, то запись стартует с 0 и 8 каналов... Данные одинаковые! По всей видимости из-за этого запись данных идет с прерываниями и частоту записи приходится дополнительно делить на 2(запись идет с частотой 256 КГц соответственно делим на 16, получаем 16КГц, приходится делить еще на 2 , т.е. 8КГц). Подскажите, пожалуйста, в чем может быть дело... Может я как-то не правильно рассчитал сдвиг указателя кольцевого буфера?
|
|||
|
||||
|
Re: Проблемы с записью данных платы L791Если опрашиваете не подключенный вход, то его нужно заземлить. |
|||
|
||||
|
Re: Проблемы с записью данных платы L791<"Если опрашиваете не подключенный вход, то его нужно заземлить.">
|
|||
|
||||
|
Re: Проблемы с записью данных платы L791Кажется, разобрался сам... Дело было в сдвиге указателя на кольцевой буфер. Поскольку указатель типа unsigned short, он сдвигался не на (kadr + chan * 4), а на (kadr + chan * 4)*2. Соответственно, после исправления (поделил на 2 величину сдвига (kadr + chan * 4)/2)запись пошла корректно с одного канала!
|
|||
|
||||
|
Re: Проблемы с записью данных платы L791Уточню... Пишу код для 64 битной операционной системы Linux. Может дело в этом:
Насколько корректно это будет работать для 64-х битной системы?? |
|||
|
||||
|
Re: Проблемы с записью данных платы L791Чтобы не биться с битностью, я бы посоветовал #include <stdint.h> или <cstdint> и использовать специально предназначенные для данных фиксированного размера типы uint32_t, int16_t и т.д. (у cstdint они в namespace std::).
Кроме того, я бы заменил все цифры, обозначающие размеры (4, 16, 64), на sizeof(). Если данные в массиве 32-битные, то лучше, чтобы и указатель на них (tmpbegptr) был uint32_t*, тогда и хитрить при вычислении индекса не надо (+1 = один отсчет). Что касается пропусков, надо проверять, как сделан ввод-вывод и успевает ли программа обработать данные. Например, что делает метод DeviceAPI::writeData()? Не понимаю, что там с чтением - ждете 10 кадров, из них берется только один отсчет? И еще - я не знаю, критично ли это на современных головокружительно быстрых машинах, но код вообще-то выглядит изрядно неэффективным. Зачем-то 4 байта перегоняются через memcpy, потом на каждый отсчет вызывается метод объекта... плюс еще параллельно прямо из платы читают данные с разными смещениями от кадра несколько потоков.
Я бы написал вообще все не так. Один поток чтения из устройства разбирает кадры на каналы и заполняет буферы (если надо - двойные), по мере накопления данных куда-то их отдает порциями... |
|||
|
||||
|
Re: Проблемы с записью данных платы L791Александр Е! Спасибо за ответ. Код действительно не оптимизирован. К сожалению у меня не дошли до этого руки, ибо очень спешу (сроки поджимают). Код изначально мной писался в 32 битной системе и про особенности 64 бит я как-то подзабыл... Пришлось делать костыли. А буфер я разбираю таким образом потому, что мне необходимо начинать запись с канала, как только на него поступили данные и только эти самые данные, а затем сохранять их в wav формате. Можно было бы стартовать сразу n потоков чтения из кольцевого буфера и уже в каждом потоке анализировать, пришли ли данные или нет... Не знаю насколько это было бы оптимальнее, ведь у меня в холостом режиме работает только один поток...
это просто такая активная пауза, которая не позволяет мне читать из циклического буфера быстрее, чем он наполняется... По крайней мере такая задумка... obj->writeData(buf2byte, 2, chan) делает fwrite(buf2byte, 2, 1, fileDesc); В общих чертах, только через метод ofstream...
|
|||
|
||||
|
Re: Проблемы с записью данных платы L791На 64-битном линуксе, если я не ошибаюсь, на самом деле отличается только размер long. Ну и размер указателей, конечно. >Можно было бы стартовать сразу n потоков чтения из кольцевого буфера и уже в каждом потоке анализировать, пришли ли данные или нет Почему не делать это в одном потоке? Ведь поток данных все равно один, они не приходят асинхронно с разных устройств. Демультиплексор каналов, разобрав кадры, может и амплитуду детектировать, и буферы заполнять.
> obj->writeData(buf2byte, 2, chan) делает fwrite(buf2byte, 2, 1, fileDesc); В файл по два байтика на отсчет с частотой дискретизации, умноженной на число каналов?! Хм.
Константин, я не могу сказать, в том ли дело или где-то зарыта какая-то ошибка. Но я не исключаю, что просто не успевает записать на диск и происходит переполнение буфера.
И еще я не понимаю, как Вы работаете со счетчиками ADC_PCI_Count. Плата в режиме bus master? |
|||
|
||||
|
Re: Проблемы с записью данных платы L791Кстати, я не очень понимаю, зачем выделять динамически malloc(4) и malloc(2) (а чем не устроила просто локальная переменная?), но уж
|
|||
|
||||
|
Re: Проблемы с записью данных платы L791Хм... Вообще идея насчет 1 потока достаточно интересна! Действительно, можно реализовать опрос каналов и запись данных в одном единственном потоке. У меня изначально представление об архитектуре будущей программы сложилось сразу как-то так "Диспетчер Файлов <- Потоки чтения для 16 каналов <- Детектор сигналов". Не знаю, от куда это взялось в моем мозгу... Я видел так: "постоянно работает детектор сигнала, сканируя все каналы, как только пришли данные, сразу происходит запуск отдельного потока чтения, в котором осуществляется основная работа. Пока он работает, на канале стоит статус busy".
Насчет записи, насколько я знаю iostream буферизирует данные сам, а потом только пишет. Поэтому я с буферизацией и не заморачивался, если быть точнее, то запись осуществляется как-то так :
Насчет переполнения буфера... Не успевает iostream? Тогда при чем здесь ADC_Ovf_Event? Буфер переполняется где? В потоке iostream? Не совсем понял, о каком буфере идет речь, уточните, пожалуйста... Насчет счетчиков... Вы имеете в виду I_ADC_PCI_COUNT_L791? Да, плата в режиме bus master. Счетчик корректно отрабатывает, как мне кажется...
|
|||
|
||||
|
Re: Проблемы с записью данных платы L791> А частота у меня 256/16 = 16 КГц. Думаете это много для 4-х ядерного процессора с частотой 2.7 ГГц?
>Не совсем понял, о каком буфере идет речь
Насчет PCI_COUNT я не уверен, не занимаюсь L791. Хотя с функцией abs() кажется странным. Если Вы хотите получить расстояние от последней считанной позиции до убежавшего вперед по циклическому буферу счетчика, то скорее не abs(new-old), а
|
|||
|
||||
|
Re: Проблемы с записью данных платы L791<"Кстати, я не очень понимаю, зачем выделять динамически malloc(4) и malloc(2) (а чем не устроила просто локальная переменная?), но уж
Локальные переменные? Т.е.
<"Потому что если new = old - 1, то буфер полон, а abs() при переходе через конец буфера начнет убывать.">
Этим циклом я поддерживаю стабильное отставание позиции считывания от позиции записи на 10 кадров... abs() как раз и нужен для перехода через конец кольцевого буфера. Таким образом я буду ждать, только когда позиция считывания приблизится к позиции записи больше, чем на 10 кадров. Поправьте, если ошибаюсь... А насчет пропусков данных я уже и не знаю на что думать... Проверял позицию чтения и позицию записи, так обгона не происходит. Пауза отрабатывает и при переходе через конец буфера. Сейчас буду смотреть ADC_Ovf_Event и загрузку проца, но мне кажется, что дело в чем-то другом... |
|||
|
||||
|
Re: Проблемы с записью данных платы L791>int buf4byte;
Вообще указатель на данные из платы (tmpbegptr) должен быть не unsigned short* (16-битный), а uint32_t*, потому что это соответствует разрядности данных, которые оттуда читаются.
volatile uint32_t* src_ptr; /* указатель на буфер карты */
И никаких malloc, memcpy, и временная переменная на 4 байта не нужна. Кстати, проверьте, как Вы вычисляете смещение в буфере. Я смотрю, в одном месте kadr + channel * 4, в другом - (kadr + channel) * 4. Как раз во избежание путаницы я и советую вычислять смещения не в байтах, а в "размерах того, на что указывает указатель", что соответствует синтаксису языка C, т.е.
>abs() как раз и нужен для перехода через конец кольцевого буфера. Таким образом я буду ждать, только когда позиция считывания приблизится к позиции записи больше, чем на 10 кадров. Приблизится с какой стороны? Функция abs() просто не является корректным способом вычисления количества данных в кольцевом буфере.
Пример 2. То же на другом месте
Пример 3.
Пример 4. То же в другом месте.
Так понятно? |
|||
|
||||
|
Re: Проблемы с записью данных платы L791А src_index это kadr + channel размером 4 байта?
Насчет abs(). У меня в программе чтение из кольцевого буфера всегда идет быстрее, чем запись в него, поэтому ни 3 ни 4 примеры не могут произойти. Во втором примере у меня в программе все будет нормально, т.к. если расстояние между W и R больше 10 кадров, то я продолжаю чтение из буфера... |
|||
|
||||
|
Re: Проблемы с записью данных платы L791src_index в моем примере - это количество 32-битных слов от начала буфера (потому что индекс массива DWORD/'ов). В каких там единицах у Вас kadr и channel - посчитайте, пожалуйста, сами >всегда идет быстрее, чем запись в него, поэтому ни 3 ни 4 примеры не могут произойти
|
|||
|
||||
|
Re: Проблемы с записью данных платы L791А нет, проявится, конечно, это я уже устал.
|
|||
|
||||
|
Re: Проблемы с записью данных платы L791<"Пример 2 прекрасно будет возникать: буфер почти пуст, но abs() даст большое значение.
Хорошо! Убедили! А если вот так:
Тут size_of_buf это полный размер буфера... |
|||
|
||||
|
Re: Проблемы с записью данных платы L791Блин, у Вас на форуме нельзя исправлять... Там ошибочка: while((abs(W-R)<10)&&(abs(W-R)>(size_of_buf-10)))
Ну вот так по идее... |
|||
|
||||
|
Re: Проблемы с записью данных платы L791Сейчас посмотрел свой предыдущий пост... Писал утром, видимо мозг не включился еще:) Там "или" в условии, вместо "и". А по существу поста, после всех вышеприведенных доработок, пропуски данных остались. Попробую сделать дополнительную буферизацию перед записью в файл... Может поможет. |
|||
|
||||
|
Re: Проблемы с записью данных платы L791Константин, программа Ваша, Вы можете выбрать любую формулу, какая Вас устроит. Мне кажется более естественным использовать корректную формулу, отвечающую на вопрос "сколько ячеек в данный момент занято (свободно) в буфере" и сравнивать ее результат с порогом, что отражает "физику" процесса.
Сознательно неточные вычисления (например, верные только на подмножестве области определения) МОЖЕТ быть смысл использовать - в тех случаях, когда точное вычисление неприемлемо долго или ресурсоемко. Тогда действительно приходится заменять вычисление оценкой, причем в большинстве задач, по-видимому, выбирать формулу так, чтобы ошибка могла быть только в сторону перестраховки. Но в данном случае правильная формула
Ладно, это действительно все несущественно. А почему у Вас пропадают данные, проверьте. Посмотрите процент загрузки CPU программой, проверьте счетчики переполнений буфера платы. Мы это обсуждали выше. |
|||
|
||||
|
Re: Проблемы с записью данных платы L791Сделал буферизацию, и немного оптимизировал программу. Проверил загрузку процессора. Она в холостом режиме 7%, под нагрузкой 12-14%. Приемлемо.
|
|||
|
||||
|
Re: Проблемы с записью данных платы L791>В 64 битной системе операция "& 0xFFFF" будет правильно работать?
|
|||
|
||||
|
Re: Проблемы с записью данных платы L791Ну, я знаю, например, что число 0xffffffff (или -1) для 64 битной системы некорректно.
|
|||
|
||||
|
Re: Проблемы с записью данных платы L791Потому что size_t в них 64-битный.
#include <stdio.h>
OUTPUT:
Причем gcc с большим уровнем warning/'ов говорит
У Вас результат все равно преобразуется в uint16_t (кстати, можно и в int16_t), поэтому разрядность промежуточных вычислений не играет роли. И заодоно в linux x64 int 32-битный, только long и size_t - 64. Ну и указатели, разумеется. |
|||
|
||||
|
Re: Проблемы с записью данных платы L791Здравствуйте. Не хочу создавать отдельную тему... Вопрос к Poul.
|