Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2009.11.15;
Скачать: CL | DM;

Вниз

TFileStream и array of extended   Найти похожие ветки 

 
SergejM ©   (2009-09-28 17:31) [0]

Здравствуйте.
Возникает проблема при записи в поток и последующем чтении массива 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-28 18:12) [1]


> SergejM ©   (28.09.09 17:31)  

> DataSize: Integer;

И откуда он берется?

> Вот примерный код (все, не относящееся непосредственно к
> записи или чтению, выкинуто):

Приводить код нужно не минимальный,
а необходимый и достаточный, дабы пожелавшие его
испытать могли сделать это легким движением руки.

И, еще, как там у нас с конфликтами имен?

--
Regards, LVT.


 
SergejM ©   (2009-09-28 18:25) [2]

DataSize вычисляется по исходным параметрам. В данном случае он равен 1024. Это размер порции данных, которые обрабатываются в выкинутых кусках. Но я не уверен, что вызов функции быстрого преобразования Фурье, оставшийся в выкинутом коде, может привести к таким фатальным результатам...
Могу привести и весь код процедуры First целиком, но она длинная и запись (а во второй, соответственно, чтение) - лишь очень малый ее участок. И перед записью, и после я проверяю содержимое массива в отладке - не меняется.
С именами все в порядке, никаких ошибок в том, какой массив куда передавать и из какого модуля вызывать функции тоже нет.


 
Leonid Troyanovsky ©   (2009-09-28 18:46) [3]


> SergejM ©   (28.09.09 18:25) [2]

> он равен 1024. Это размер порции данных, которые обрабатываются

Ну, а где же тот самый необходимый и достаточный код?

У мну, например, есть несколько колов времени для изощрения ума,
но мне лениво осмысливать все "..."  (кроме того, их же _надо_ менять).

Надо начинать с простейших способов ловли: сначала на червя, потом
на блесну, а затем плавно переходим к ..  динамиту и электроудочке [ОНР]

--
Regards, LVT.


 
qwer_qwer   (2009-09-28 19:27) [4]


> SergejM ©   (28.09.09 18:25) [2]


Ну так сделай простой пример со входными данными, в котором у тебя неправильно получается, чтобы посмотреть можно было.


 
Сергей М. ©   (2009-09-28 19:51) [5]


> Low(Data)


С Low можно и не выёживаться - нижняя граница значения индекса у любой размерности дин.массива всегда равна 0.


> Stream.Read


> Stream.Write


Заимей полезную привычку использовать ReadBuffer и WriteBuffer.


 
Сергей М. ©   (2009-09-28 19:54) [6]

А уж если "вредная привычка" использовать Read и Write чем-то оправдана, то заведи другую "полезную привычку - получать и анализировать результаты выполнения этих функциональных (!!!) методов.


 
Anatoly Podgoretsky ©   (2009-09-28 21:10) [7]

> Сергей М.  (28.09.2009 19:51:05)  [5]

Тоже мы слышали про Integer и про String.
Делать надо правильно, возможно завтра это будет не так, и код с Low будет продолжать работать.
Кроме это хорошая практика писать правильно.


 
Сергей М. ©   (2009-09-29 08:43) [8]


> Anatoly Podgoretsky ©   (28.09.09 21:10) [7]


> возможно завтра это будет не так

Сомневаюсь)
Впрочем я не настаиваю - можно и повыёживаться, на сабж это никак не влияет)


 
SergejM ©   (2009-09-29 10:40) [9]

Здравствуйте. Не смог вчера ответить.
Вот полный код функции, осуществляющей запись, без сокращений - скопировал все целиком из проекта.
Исходные данные, получаемые ранее и используемые в расчетах:
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);


 
SergejM ©   (2009-09-29 10:41) [10]

               //Усредняем
               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;


Все целиком не влезло.


 
Anatoly Podgoretsky ©   (2009-09-29 10:50) [11]

> Сергей М.  (29.09.2009 08:43:08)  [8]

А я вообще то не столько о динамических массивах, сколько о привычке не использовать литералы, для указания границ. Не составит труда сделать это привычкой, а не думать прокатит или нет, а что будет завтра, а что будет если я изменю тип и так далее. Это приведет к меньшему количеству ошибок. Использование литералов наживается хесткое кодирование (Hard Coded) и является вредной, плохой привычкой.


 
Сергей М. ©   (2009-09-29 10:54) [12]

А в основном потоке этот код работает правильно ?


 
SergejM ©   (2009-09-29 11:50) [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 повторяется во многих элементах массива, но с разным знаком и разной экспонентой. А при просмотре считанного массива в отладке поэлементно эти элементы показываются пустыми.


 
qwer_qwer   (2009-09-29 12:49) [14]


> MainForm.flgStatusCorrelation


Это переменная типа Boolean или компонент?


 
SergejM ©   (2009-09-29 13:01) [15]

Это Boolean.


 
qwer_qwer   (2009-09-29 13:04) [16]


> SergejM ©   (29.09.09 13:01) [15]


По коду пока вроде бы ничего не заметно.
У тебя проект с данными засекречен или можешь на тестирование дать?


 
Sapersky   (2009-09-29 13:12) [17]

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

Наверняка что-то между вводом-выводом и приводит. Включи Range Check, попробуй закомментировать всё кроме операций с файлами.


 
SergejM ©   (2009-09-29 13:13) [18]

В принципе, там особо секретного ничего нету, но отправить могу только вечером. Правда, исходные данные, считывающиеся в tmpChannel1 и 2, точно не дам - это файл в полтора Гб:) Отсчеты функции, для тестирования использовался синус.
Хотя в других местах никаких обращений к этим данным нет, все только здесь. И в основном потоке отношение к данным имеет только вызов потока.


 
qwer_qwer   (2009-09-29 13:16) [19]


> В принципе, там особо секретного ничего нету, но отправить
> могу только вечером. Правда, исходные данные, считывающиеся
> в tmpChannel1 и 2, точно не дам - это файл в полтора Гб:
> ) Отсчеты функции, для тестирования использовался синус.
> Хотя в других местах никаких обращений к этим данным нет,
>  все только здесь. И в основном потоке отношение к данным
> имеет только вызов потока.


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

Выслать можно на palmih@gmail.com


 
Сергей М. ©   (2009-09-29 13:20) [20]

Не знаю.
Разбираться в этом барахле нет ни малейшего желания.
Вот демо-пример, приближенный к боевым условиям.
Пробуй и убедись сам, что 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;


 
SergejM ©   (2009-09-29 13:26) [21]

> Sapersky

Range Check включен. А закомментировать там и нечего больше - если убрать функцию канального фильтра и быстрое преобразование Фурье, задача сведется к тому, что написано в самом первом сообщении. Что интересно, мусор в виде фрагментов журнала событий программы все равно попадает в результирующий файл.

> qwer_qwer

Кусок - сложно... Попробую выдрать пару первых блоков.

> Сергей М.

Что FileStream ничего не портит, я знаю. Я впервые столкнулся именно с такой ситуацией. А тестовые функции, естественно, что записали, то и считывают, я проверял.


 
Sapersky   (2009-09-29 17:35) [22]

Какого типа элементы TReal1DArray?


 
SergejM ©   (2009-09-29 17:49) [23]

Real, одномерный массив.
Осознал свою неправоту...
Спасибо.


 
Sapersky   (2009-09-29 19:20) [24]

На будущее - можно вместо SizeOf(тип) писать SizeOf(DataChannel1[0]).
Ну и разбивать неохватных размеров функции на несколько мелких, в которых подобные глупые ошибки видны с первого-второго взгляда.


 
Leonid Troyanovsky ©   (2009-09-29 19:55) [25]


> Sapersky   (29.09.09 19:20) [24]

Ээ..х, хотелось еще что-то пожелать автору.
Но, так и не сумел сформулировать внятно :)

Поэтому отступаю, отступаю.

--
Regards, LVT.



Страницы: 1 вся ветка

Текущий архив: 2009.11.15;
Скачать: CL | DM;

Наверх




Память: 0.56 MB
Время: 0.014 c
15-1253513903
_@!!ex
2009-09-21 10:18
2009.11.15
Помогите подключить винчестер


15-1253392203
Юрий
2009-09-20 00:30
2009.11.15
С днем рождения ! 20 сентября 2009 воскресенье


2-1254386902
NGPOL
2009-10-01 12:48
2009.11.15
DCOM-сервер и "протокол не поддерживается"


1-1224572864
Gurd
2008-10-21 11:07
2009.11.15
TXMLDocument


15-1248261640
@!!ex
2009-07-22 15:20
2009.11.15
Сколько вариантов чисел?