![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
TFileStream и array of extended [XP]
SergejM © (2009-09-28 17:31:00)Здравствуйте.
Возникает проблема при записи в поток и последующем чтении массива extended. Пишутся и читаются не те данные, которые ожидаются.
Вот примерный код (все, не относящееся непосредственно к записи или чтению, выкинуто):
type
TArrayOfExtended = array of Extended;
...
procedure First;
var
Data:TArrayOfExtended;
Stream:TFileStream;
DataSize: Integer;
...
begin
...
SetLength(Data, DataSize);
...
Stream.Write(Data[Low(Data)], Length(Data)*SizeOf(Extended));
...
end;
procedure Second;
var
Data:TArrayOfExtended;
Stream:TFileStream;
DataSize: Integer;
...
begin
...
SetLength(Data, DataSize);
Stream.Read(Data[Low(Data)], DataSize*SizeOf(Extended));
...
end;
В процедуре First в массиве данных находились дробные числа в диапазоне от -100 до 100. После загрузки в процедуре Second массив содержит в себе дробные числа в диапазоне от -1e-4000 до 1e4000. В качестве дополнения при просмотре сохраненного файла видно, что в него попадают куски из совершенно другой области - фрагменты журнала работы программы. Первое такое попадание случается спустя 8192 байта от начала и дальше спустя каждые 10240 байт. Значение переменной DataSize кратно 4096 байт.
Вопрос - что в приведенном коде может заставлять поток сохранения брать данные из другой области памяти?..
Leonid Troyanovsky (2009-09-29 19:55:00) [1]
> Sapersky (29.09.09 19:20) [24]
Ээ..х, хотелось еще что-то пожелать автору.
Но, так и не сумел сформулировать внятно :)
Поэтому отступаю, отступаю.
--
Regards, LVT.
Sapersky (2009-09-29 19:20:00) [2]На будущее - можно вместо SizeOf(тип) писать SizeOf(DataChannel1[0]).
Ну и разбивать неохватных размеров функции на несколько мелких, в которых подобные глупые ошибки видны с первого-второго взгляда.
SergejM (2009-09-29 17:49:00) [3]Real, одномерный массив.
Осознал свою неправоту...
Спасибо.
Sapersky (2009-09-29 17:35:00) [4]Какого типа элементы TReal1DArray?
SergejM (2009-09-29 13:26:00) [5]> Sapersky
Range Check включен. А закомментировать там и нечего больше - если убрать функцию канального фильтра и быстрое преобразование Фурье, задача сведется к тому, что написано в самом первом сообщении. Что интересно, мусор в виде фрагментов журнала событий программы все равно попадает в результирующий файл.
> qwer_qwer
Кусок - сложно... Попробую выдрать пару первых блоков.
> Сергей М.
Что FileStream ничего не портит, я знаю. Я впервые столкнулся именно с такой ситуацией. А тестовые функции, естественно, что записали, то и считывают, я проверял.
Сергей М. (2009-09-29 13:20:00) [6]Не знаю.
Разбираться в этом барахле нет ни малейшего желания.
Вот демо-пример, приближенный к боевым условиям.
Пробуй и убедись сам, что FileStream ничего не портит:
procedure TForm1.Button6Click(Sender: TObject);
var
src_arr, dst_arr: array of Extended;
fs: TFileStream;
i: Integer;
begin
SetLength(src_arr, 65536);
SetLength(dst_arr, 65536);
for i := Low(src_arr) to High(src_arr) do
src_arr[i] := Random;
fs := TFileStream.Create('c:\stream_test.dat', fmCreate);
try
fs.WriteBuffer(src_arr[Low(src_arr)], Length(src_arr) * SizeOf(Extended));
finally
fs.Free;
end;
fs := TFileStream.Create('c:\stream_test.dat', fmOpenRead);
try
fs.ReadBuffer(dst_arr[Low(dst_arr)], Length(dst_arr) * SizeOf(Extended));
finally
fs.Free;
end;
if CompareMem(@src_arr[Low(src_arr)], @dst_arr[Low(dst_arr)], Length(src_arr) * SizeOf(Extended)) then
ShowMessage('Содержимое оригинального и восстановленного массивов идентично')
else
ShowMessage('Содержимое оригинального и восстановленного массивов отличается');
end;
qwer_qwer (2009-09-29 13:16:00) [7]
> В принципе, там особо секретного ничего нету, но отправить
> могу только вечером. Правда, исходные данные, считывающиеся
> в tmpChannel1 и 2, точно не дам - это файл в полтора Гб:
> ) Отсчеты функции, для тестирования использовался синус.
> Хотя в других местах никаких обращений к этим данным нет,
> все только здесь. И в основном потоке отношение к данным
> имеет только вызов потока.
Если есть возможность кусок данных кинуть, было бы нормально, потому что всё-таки тестировать надо на конкретных входных данных, потому что на другой информации можно и не получить того результата.
Выслать можно на palmih@gmail.com
SergejM (2009-09-29 13:13:00) [8]В принципе, там особо секретного ничего нету, но отправить могу только вечером. Правда, исходные данные, считывающиеся в tmpChannel1 и 2, точно не дам - это файл в полтора Гб:) Отсчеты функции, для тестирования использовался синус.
Хотя в других местах никаких обращений к этим данным нет, все только здесь. И в основном потоке отношение к данным имеет только вызов потока.
Sapersky (2009-09-29 13:12:00) [9]Но я не уверен, что вызов функции быстрого преобразования Фурье, оставшийся в выкинутом коде, может привести к таким фатальным результатам...
Наверняка что-то между вводом-выводом и приводит. Включи Range Check, попробуй закомментировать всё кроме операций с файлами.
qwer_qwer (2009-09-29 13:04:00) [10]
> SergejM © (29.09.09 13:01) [15]
По коду пока вроде бы ничего не заметно.
У тебя проект с данными засекречен или можешь на тестирование дать?
SergejM (2009-09-29 13:01:00) [11]Это Boolean.
qwer_qwer (2009-09-29 12:49:00) [12]
> MainForm.flgStatusCorrelation
Это переменная типа Boolean или компонент?
SergejM (2009-09-29 11:50:00) [13]В основном потоке код работает точно также, без малейших изменений.
Вот первые пять элементов записываемого массива:
-0,0230583879
-94,343305337
-1424,1663944
-7650,9707988
-22555,084089
А вот перыве пять элементов считанного массива:
-2,3438792137e+4353
3,6754775808e+2720
-1,3877469433e+2695
-3,6754775808e+65
7,8405128178e+3745
Причем значение 3,6754775808 повторяется во многих элементах массива, но с разным знаком и разной экспонентой. А при просмотре считанного массива в отладке поэлементно эти элементы показываются пустыми.
Сергей М. (2009-09-29 10:54:00) [14]А в основномпотоке этот код работает правильно ?
Anatoly Podgoretsky (2009-09-29 10:50:00) [15]> Сергей М. (29.09.2009 08:43:08) [8]
А я вообще то не столько о динамических массивах, сколько о привычке не использовать литералы, для указания границ. Не составит труда сделать это привычкой, а не думать прокатит или нет, а что будет завтра, а что будет если я изменю тип и так далее. Это приведет к меньшему количеству ошибок. Использование литералов наживается хесткое кодирование (Hard Coded) и является вредной, плохой привычкой.
SergejM (2009-09-29 10:41:00) [16]
//Усредняем
for j := 0 to Length(DataChannel1) - 1 do
begin
SumChannel1[j] :=
SumChannel1[j] + Abs(DataChannel1[j]) / NSum;
SumChannel2[j] :=
SumChannel2[j] + Abs(DataChannel2[j]) / NSum;
end;//for i := 0 to Length(DataChannel1) do
end;//for j := 1 to Nsum do
//Выводим данные на график
Synchronize(UpdateChart);
end;//while (DataFile.Position <= DataFile.Size) and MainForm.flgStatusCorrelation do
finally//ChFilter2 := TChannelFilter.Create(Fs,FreqRes,Fc2,M,R,N,MainForm.fldFileNameCoeffs);
ChannelFilter2.Free;
end;//ChFilter2 := TChannelFilter.Create(Fs,FreqRes,Fc2,M,R,N,MainForm.fldFileNameCoeffs);
finally//ChFilter1 := TChannelFilter.Create(Fs,FreqMain,Fc2,M,R,N,MainForm.fldFileNameCoeffs);
ChannelFilter1.Free;
end;//ChFilter1 := TChannelFilter.Create(Fs,FreqMain,Fc2,M,R,N,MainForm.fldFileNameCoeffs);
finally//WriteFile2 := TFileStream.Create(FileChannel2Name, fmCreate);
WriteFile2.Free;
end; //WriteFile2 := TFileStream.Create(FileChannel2Name, fmCreate);
finally//WriteFile1 := TFileStream.Create(FileChannel1Name, fmCreate);
WriteFile1.Free;
end; //WriteFile1 := TFileStream.Create(FileChannel1Name, fmCreate);
finally//DataFile := TFileStream.Create(MainForm.fldFileNameCorrelation);
DataFile.Free;
end;//DataFile := TFileStream.Create(MainForm.fldFileNameCorrelation);
end;
Все целиком не влезло.
SergejM (2009-09-29 10:40:00) [17]Здравствуйте. Не смог вчера ответить.
Вот полный код функции, осуществляющей запись, без сокращений - скопировал все целиком из проекта.
Исходные данные, получаемые ранее и используемые в расчетах:
Nfft = 1024
NSum = 10
FDiscreet = 80 * 1e6
FreqMain = 70 * 1e6
FreqRes = 70 * 1e6
Функция ChannelFilter1.ChannelFilter на вход получает массив размерностью R и на выход передает одно значение. Для простоты можно считать, что выходное значение - первый элемент массива.
procedure TCorrelationThread.ModeChannelFilter;
/// Процедура, реализующая режим работы "Фильтрация и выгрузка в файл"
var
DataFile: TFileStream;
WriteFile1: TFileStream;
WriteFile2: TFileStream;
M: Integer;
R: Integer;
N: Integer;
Fs: Extended;
Fc2: Extended;
FileChannel1Name: String;
FileChannel2Name: String;
DataArray: array of Byte;
DataSize: Integer;
cntRead: Integer;
cntWrite: Integer;
cntIndex: Integer;
DataChannel1: TReal1DArray;
DataChannel2: TReal1DArray;
ChannelFilter1: TChannelFilter;
ChannelFilter2: TChannelFilter;
tmpChannel1: ArrayOfExtended;
tmpChannel2: ArrayOfExtended;
i: Integer;
j: Integer;
k: Integer;
begin
DataFile := TFileStream.Create(MainForm.fldFileNameCorrelation,
fmOpenRead or fmShareDenyWrite);
try//DataFile := TFileStream.Create(MainForm.fldFileNameCorrelation);
FileChannel1Name := ChangeFileExt(MainForm.fldFileNameCorrelation, '.ch1');
FileChannel2Name := ChangeFileExt(MainForm.fldFileNameCorrelation, '.ch2');
WriteFile1 := TFileStream.Create(FileChannel1Name, fmCreate);
try//WriteFile1 := TFileStream.Create(FileChannel1Name, fmCreate);
WriteFile1.Position := 0;
WriteFile2 := TFileStream.Create(FileChannel2Name, fmCreate);
try//WriteFile2 := TFileStream.Create(FileChannel2Name, fmCreate);
WriteFile2.Position := 0;
//Настраиваем массивы конечных данных
SetLength(DataChannel1, Nfft);
SetLength(DataChannel2, Nfft);
SetLength(SumChannel1, Nfft);
SetLength(SumChannel2, Nfft);
//Задаем параметры для будущих канальных фильтров
Fs := 200 * 1e6;//Тактовая частота первого DDS
//Тактовая частота второго DDS в R раз меньше
Fc2 := 500 * 1e3;//Несущая частота второго DDS
M := 3;//Кол-во каскадов
R := Trunc(200 * 1e6 / fDiscreet);//Коэффициент децимации
N := 2;//Задержка гребенчатой секции
//Настраиваем массив чтения из файла
//Длина одной порции кратна кол-ву каналов - одному или двум
DataSize := 2 * (Nfft * Nsum * R) * cntChannels;
SetLength(DataArray, DataSize);
DataFile.Position := 0;
//Задаем размерность временных массивов
SetLength(tmpChannel1, R);
SetLength(tmpChannel2, R);
//Создаем канальные фильтры для первого и второго каналов
ChannelFilter1 := TChannelFilter.Create(Fs, FreqMain, Fc2,
M, R, N, MainForm.fldFileNameCoeffs);
try//ChFilter1 := TChannelFilter.Create(Fs,FreqMain,Fc2,M,R,N,MainForm.fldFileNameCoeffs);
ChannelFilter2 := TChannelFilter.Create(Fs, FreqRes, Fc2,
M, R, N, MainForm.fldFileNameCoeffs);
try//ChFilter2 := TChannelFilter.Create(Fs,FreqRes,Fc2,M,R,N,MainForm.fldFileNameCoeffs);
while (DataFile.Position <= DataFile.Size) and
MainForm.flgStatusCorrelation do
begin
//Считываем порцию файла. Если файл кончился, выходим
cntRead := DataFile.Read(DataArray[Low(DataArray)], DataSize);
if cntRead < DataSize then
Exit;
//Обнуляем массивы сумм для усредения
for i := Low(SumChannel1) to High(SumChannel1) do
begin
SumChannel1[i] := 0;
SumChannel2[i] := 0;
end;//for i := Low(SumChannel1) to High(SumChannel1) do
//Цикл по усреднениям
for i := 0 to NSum - 1 do
begin
if not MainForm.flgStatusCorrelation then
Break;
//Цикл по одному преобразованию
for j :=
0 to DataSize div (NSum * 2 * cntChannels * R) - 1 do
begin
//Цикл по тактам работы канального фильтра
for k := 0 to R - 1 do
begin
cntIndex :=
i * Nfft * R * 2 * cntChannels + j *
R * 2 * cntChannels + k * 2 * cntChannels;
tmpChannel1[k] :=
DataArray[cntIndex + 0] * 256 +
DataArray[cntIndex + 1];
if tmpChannel1[k] > 32767 then
tmpChannel1[k] := tmpChannel1[k] - 65536;
tmpChannel2[k] :=
DataArray[cntIndex + 2] * 256 +
DataArray[cntIndex + 3];
if tmpChannel2[k] > 32767 then
tmpChannel2[k] := tmpChannel2[k] - 65536;
end;//for k := 0 to R - 1 do
DataChannel1[j] := ChannelFilter1.ChannelFilter(tmpChannel1);
DataChannel2[j] := ChannelFilter2.ChannelFilter(tmpChannel2);
end;//for j := 1 to DataSize div (NSum * 2 * cntChannels) do
//Сохраняем отсчеты временной области в файлы
cntWrite :=
WriteFile1.Write(DataChannel1[Low(DataChannel1)],
SizeOf(Extended) * Length(DataChannel1));
if cntWrite <> SizeOf(Extended) * Length(DataChannel1) then
Exit;
cntWrite :=
WriteFile2.Write(DataChannel2[Low(DataChannel2)],
SizeOf(Extended) * Length(DataChannel2));
if cntWrite <> SizeOf(Extended) * Length(DataChannel2) then
Exit;
//Проводим быстрое преобразование Фурье
RealFastFourierTransform(DataChannel1, Nfft, False);
RealFastFourierTransform(DataChannel2, Nfft, False);
Сергей М. (2009-09-29 08:43:00) [18]
> Anatoly Podgoretsky © (28.09.09 21:10) [7]
> возможно завтра это будет не так
Сомневаюсь)
Впрочем я не настаиваю - можно и повыёживаться, на сабж это никак не влияет)
Anatoly Podgoretsky (2009-09-28 21:10:00) [19]> Сергей М. (28.09.2009 19:51:05) [5]
Тоже мы слышали про Integer и про String.
Делать надо правильно, возможно завтра это будет не так, и код с Low будет продолжать работать.
Кроме это хорошая практика писать правильно.
Сергей М. (2009-09-28 19:54:00) [20]А уж если "вредная привычка" использовать Read и Write чем-то оправдана, то заведи другую "полезную привычку - получать и анализировать результаты выполнения этих функциональных(!!!) методов.