Создание новых видов системных объектов для ввода и вывода файла

Этот пример показывает, как создать и использовать два различных Системных объекта, чтобы упростить потоковую передачу данных в и из MATLAB: dspdemo.TextFileReader и dspdemo.TextFileWriter.

Объекты, обсужденные в этом примере, обращаются ко многим реалистическим вариантам использования, и они могут быть настроены, чтобы достигнуть усовершенствованных и более специализированных задач.

Введение

Этот пример показывает, как создать и использовать новые типы Системных объектов для чтения файла и записи. Внутренне, эти Системные объекты используют стандартные низкоуровневые функции ввода-вывода файлов, доступные в MATLAB (например, fscanf, fprintf, fread, fwrite). Путем абстракции далеко большинства деталей использования тех функций, они стремятся делать задачу из чтения и записи переданных потоком данных более простой и более эффективной.

Этот пример включает использование многих усовершенствованных построений, чтобы создать Системные объекты. Для более основного введения в объекты авторской системы обратитесь к примерам в разделе Define New System Objects документации DSP System Toolbox.

Система интерфейс Object™

Системные объекты являются классами MATLAB, которые выводят от matlab.System. В результате Системные объекты, все наследовали общий открытый интерфейс, который включает следующие стандартные методы:

  • настройка- инициализировать объект, обычно в начале симуляции

  • сброс- очистить внутреннее состояние объекта, возвращая его его состоянию постинициализации по умолчанию

  • шаг- выполнить базовую функциональность объекта, опционально принимая некоторый вход и/или возвращая некоторый выходной параметр

  • релиз- высвобождать любые средства (например, память, оборудование, или специфичный для ОС) используемый внутренне объектом

Когда вы создаете новые виды Системных объектов, вы обеспечиваете определенные реализации для всех предыдущих методов, чтобы определить его поведение.

В этом примере мы обсуждаем внутреннюю структуру и использование следующих двух Системных объектов:

Определение класса dspdemo.TextFileReader

Все Системные объекты в предыдущем списке совместно используют общую структуру. Например, dspdemo.TextFileReader включает следующие разделы

1. Оператор определения класса, который подразумевает этот класс, выведен и от matlab.System и от matlab.system.mixin.FiniteSource.

classdef TextFileReader < matlab.System & matlab.system.mixin.FiniteSource
  • matlab.System требуется и является базовым классом для всех Системных объектов

  • matlab.system.mixin.FiniteSource указывает, что этот класс является источником сигнала с конечным числом выборок данных. Это подразумевает, что в дополнение к обычному интерфейсу Системный объект также представит метод isDone. Когда isDone возвращает true, объект достиг конца доступных данных.

2. Много публичных свойств. В этом случае два являются ненастраиваемыми (они не могут быть изменены после первого вызова шага) и у всех есть значение по умолчанию. Значения по умолчанию присвоены соответствующим свойствам, когда ничто иное не задано пользователем. Публичные свойства могут быть изменены пользователем, чтобы настроить поведение объекта к его конкретному приложению.

   properties (Nontunable)
       Filename   = 'tempfile.txt'
       HeaderLines = 4
   end
   properties
       DataFormat = '%g'
       Delimiter = ','
       SamplesPerFrame = 1024
       PlayCount = 1
   end

3. Много частных свойств. Они не видимы пользователю и могут служить многим целям, включая

  • Держаться за значения вычисляло только иногда (например, во время инициализации, когда setup называется или когда step называется впервые), и затем использованный последующими вызовами step. Это может сохранить перевычисление их во времени выполнения, и следовательно улучшать производительность базовой функциональности

  • Задавать внутреннее состояние объекта. Например, pNumEofReached хранит число раз, что индикатор конца файла был достигнут

   properties(Access = private)
       pFID = -1
       pNumChannels
       pLineFormat
       pNumEofReached = 0
   end

4. Конструктор. Это называется, когда новый экземпляр dspdemo.TextDataReader создается пользователем. При вызове метода setProperties в конструкторе позволяет пользователям устанавливать свойства объекта путем обеспечения пар "имя-значение" при конструкции.

   methods
       function obj = TextFileReader(varargin)
           setProperties(obj, nargin, varargin{:});
       end
   end

5. Много переопределенных методов от базового класса matlab.System. Открытые методы, характерные для всех Системных объектов, у каждого есть соответствующие защищенные методы, которые они вызывают внутренне. Имена этих защищенных методов все включают постфикс Impl. Они могут быть реализованы при определении класса, чтобы программировать определенное поведение конкретного Системного объекта.

Для получения дополнительной информации о соответствии между стандартными открытыми методами и их внутренними реализациями, обратитесь к Синхронизации Методов.

Например, конкретные методы внедрения, которые заменены для dspdemo.TextFileReader,

  • setupImpl

  • resetImpl

  • stepImpl

  • releaseImpl

  • isDoneImpl

  • processTunedPropertiesImpl

  • loadObjectImpl

  • saveObjectImpl

6. Много закрытых методов. Эти методы только доступны из других методов того же класса. Они могут использоваться, чтобы сделать остальную часть кода более читаемой. Они могут также улучшить возможность многократного использования кода путем группировки в соответствии с отдельным кодом стандартных программ, который используется многократно в различных частях класса.

Запись и чтение данных - введение в пример

Код, который следует, дает простую демонстрацию того, как эти новые объекты могли использоваться. Следующие задачи показывают

  • Создайте текстовый файл, содержащий выборки двух различных синусоидальных сигналов с помощью dspdemo.TextFileWriter

  • Читайте из текстового файла с помощью dspdemo.TextFileReader и запишите во второй файл в двоичной форме, на этот раз с помощью dsp.BinaryFileWriter

  • Считайте выборки сигнала циклически из нового двоичного файла с помощью dsp.BinaryFileReader и анализируйте результаты графически.

Создайте простой текстовый файл, содержащий желаемые данные

Чтобы запуститься, новый текстовый файл создается, чтобы сохранить два синусоидальных сигнала частотами 50 Гц и 60 Гц, соответственно. Для каждого сигнала хранимые данные будут состоять из 800 выборок на уровне выборки 8 кГц.

Следующее готовит данные

% Create data samples
fs = 8000;
tmax = 0.1;
t = (0:1/fs:tmax-1/fs)';
N = length(t);
f = [50,60];
data = sin(2*pi*t*f);

% Optionally, form a header string to describe the data in a readable way
% for future use
fileheader = sprintf(['The following contains %d samples of two ',...
    'sinusoids,\nwith frequencies %d Hz and %d Hz and a sample rate of',...
    ' %d kHz\n\n'], N, f(1),f(2),fs/1000);

Чтобы сохранить сигнал к текстовому файлу, создайте экземпляр средства записи текстового файла. Конструктору dspdemo.TextFileWriter нужно имя конечного файла и некоторых дополнительных параметров, которые могут быть переданы в как пары "имя-значение".

TxtWriter = dspdemo.TextFileWriter('Filename','sinewaves.txt',...
    'Header',fileheader) %#ok<NOPTS>
TxtWriter = 

  dspdemo.TextFileWriter with properties:

      Filename: 'sinewaves.txt'
        Header: 'The following contains 800 samples of two sinusoids,...'
    DataFormat: '%.18g'
     Delimiter: ','

dspdemo.TextFileWriter записывает данные к разделенным от разделителя ASCII-файлам. Его публичные свойства включают следующее

  • FileName : имя файла, который будет записан. Если файл с этим именем уже существует, это перезаписывается. Когда операции запускаются, объект начинает запись к файлу сразу после заголовка - это затем добавляет новые данные в каждом последующем вызове шага, пока это не выпущено. Вызов сброса продолжает писать с начала файла.

  • Header: символьная строка, часто состоящая из нескольких строк и отключенная символом новой строки (\n). Это задано пользователем и может быть изменено, чтобы встроить человекочитаемую информацию, которая описывает фактические данные.

  • DataFormat: формат раньше хранил каждую выборку данных. Это может принять любое значение, присваиваемое как Спецификатор Преобразования в строке formatSpec, используемой встроенной функцией MATLAB fprintf. DataFormat применяется ко всем каналам, записанным в файл. Значением по умолчанию для этого свойства является '%.18g', который позволяет сохранять данные с плавающей запятой двойной точности в полной точности.

  • Delimiter: символ раньше разделял выборки от различных каналов одновременно момент. Каждая строка записанных карт файлов к моменту времени, и это включает столько же выборок сколько количество каналов, обеспеченных, как введено (т.е. количество столбцов в матричном входе передало step).

Чтобы записать все доступные данные в файл, один вызов step может использоваться можно следующим образом

% Write to file with data as input
% Note: |TxtWriter(data)| is equivalent to |step(TxtWriter, data)|
TxtWriter(data)

% Release control of file
release(TxtWriter)

Данные теперь хранимы в новом файле. Чтобы осмотреть файл визуально вводят edit('sinewaves.txt'). Из-за заголовка обратите внимание, что данные запускаются на строке 4, после 3 строк заголовка.

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

Существуют случаи, когда этот подход не возможен или практичен. Например, данные могут быть слишком большими, чтобы поместиться в одну переменную MATLAB (т.е. слишком большой, чтобы соответствовать на системной памяти), или это может быть создано циклически в цикле или передано потоком в MATLAB из внешнего источника. Во всех этих случаях может быть удобно передать данные потоком в файл при помощи подхода, подобного следующему

% Use a streamed sine wave generator to create a frame of data per step
frameLength = 32;
SineWave = dsp.SineWave('Frequency',[50,60], 'SampleRate', fs, ...
    'SamplesPerFrame', frameLength);

% Run the desired number of iterations to create the data and store it into
% the file
tmax = 10; % Write more data in this scenario
t = (0:1/fs:tmax-1/fs)';
N = length(t);
data = sin(2*pi*t*f);
numCycles = N/frameLength;
for k = 1:numCycles
    dataFrame = SineWave();
    TxtWriter(dataFrame)
end

% Release control of file and sine wave generator
release(TxtWriter)
release(SineWave)

Читайте из файла существующего текста и записи к новому двоичному файлу

Следующий шаг состоит в чтении данных из недавно созданного файла и записи его в новый двоичный файл.

Чтобы читать из текстового файла, создайте экземпляр dspdemo.TextFileReader.

% Create a text file reader
TxtReader = dspdemo.TextFileReader('Filename','sinewaves.txt',...
    'HeaderLines',3,'SamplesPerFrame',frameLength) %#ok<NOPTS>
TxtReader = 

  dspdemo.TextFileReader with properties:

           Filename: 'sinewaves.txt'
        HeaderLines: 3
         DataFormat: '%g'
          Delimiter: ','
    SamplesPerFrame: 32
          PlayCount: 1

dspdemo.TextFileReader считывает числовые данные из разделенных от разделителя ASCII-файлов. Его свойства подобны тем из dspdemo.TextFileWriter. Некоторые различия следуют

  • HeaderLines получает количество строк, используемых заголовком в файле, заданном в Filename. Первый вызов step начинает читать из номера строки HeaderLines+1. Последующие вызовы step продолжают читать из строки сразу после ранее прочитанной строки. Вызов reset продолжит читать из строки HeaderLines+1.

  • Delimiter является снова символом, используемым, чтобы разделить выборки от различных каналов одновременно момент. В этом случае это также используется, чтобы определить количество каналов данных, сохраненных в файле: когда step будет сначала назван, числа объектов количество символов Delimiter в строке HeaderLines+1, скажите numDel; это затем принимает в течение каждого раза момент, это должно считать числовые значения numChan = numDel+1 с форматом DataFormat. Матрица, возвращенная шагом, имеет размер SamplesPerFrame x numChan.

  • SamplesPerFrame является количеством строк, прочитанных каждым вызовом step, т.е. количеством строк матрицы, возвращенной, как выведено. Когда последние доступные строки данных достигнуты, это может произойти, что они - меньше, чем необходимый SamplesPerFrame. В этом случае доступные данные дополнены нулями, чтобы получить матрицу размера SamplesPerFrame x numChan. Если все данные считаны, шаг просто возвращает zeros(SamplesPerFrame,numChan) до reset, или release называется.

  • PlayCount является числом раз, данные в файле считаны циклически. Если объект достигает конца файла, и файл еще не был считан неоднократно равный PlayCount, читая резюме с начала данных (т.е. строка HeaderLines+1). Если последние строки файла не обеспечивают достаточно выборок, чтобы сформировать полную выходную матрицу размера SamplesPerFrame x numChan, то кадр завершается с помощью исходных данных. Если файл является чтением времена PlayCount, выходная матрица, возвращенная шагом, заполнена нулями, и все вызовы isDone возвращают true, если не сброшено, или релиз называется. Чтобы циклично выполниться через доступные данные неопределенно, PlayCount может быть установлен в Inf.

Чтобы записать в новый двоичный файл, создайте экземпляр dsp.BinaryFileWriter

BinWriter = dsp.BinaryFileWriter('Filename','sinewaves.bin',...
    'HeaderStructure',struct('Info',fileheader));

Данные, переданные как вход dsp.BinaryFileWriter, хранятся с помощью его собственного типа данных. Например, вход с плавающей точкой с двойной точностью будет использовать 8 байтов за выборку. Выборки данных хранятся в порядке времени, и чередованы несколько каналов. Как следствие входные матрицы, переданные step, добавлены к файлу с помощью главного строкой подхода (т.е. первая строка хранится сначала, слева направо, затем вторая строка, и так далее).

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

% Write binary data using single-precision floating point
% Preallocate a data frame with |frameLength| rows and 2 columns
dataFrame = zeros(frameLength,2,'single');

% Read from the text file and write to the binary file, whilst data is
% present in the source text file. Notice how the method |isDone| is used
% to control the execution of the while loop
while(~isDone(TxtReader))
    dataFrame(:) = TxtReader();
    BinWriter(dataFrame)
end

% Release control of both files
release(TxtReader)
release(BinWriter)

Считайте содержимое двоичного файла циклически

sinewaves.bin содержит целое число периодов для обеих синусоид, т.е. 500 периодов на уровне 50 Гц и 600 на уровне 60 Гц. Такие сигналы могут читаться циклически и использоваться, чтобы сгенерировать синусоиды произвольной длины. В последней части этой демонстрации dsp.BinaryFileReader используется, чтобы сделать точно это. Когда данные считаны, эти две синусоиды визуализируются во временном интервале, и их продукт анализируется в частотном диапазоне.

frameLength = 1024;

% Create an instance of a binary file reader
BinReader = dsp.BinaryFileReader('Filename','sinewaves.bin',...
    'HeaderStructure',struct('Info',zeros(1,numel(fileheader),'uint8')),...
    'NumChannels',2,...
    'DataType','single',...
    'SamplesPerFrame',frameLength) %#ok<NOPTS>
header = BinReader.readHeader.Info;
% Display the header read from the file:
char(header)
BinReader = 

  dsp.BinaryFileReader with properties:

           Filename: 'sinewaves.bin'
    HeaderStructure: [1x1 struct]
    SamplesPerFrame: 1024
        NumChannels: 2
           DataType: 'single'
      IsDataComplex: false


ans =

    'The following contains 800 samples of two sinusoids,
     with frequencies 50 Hz and 60 Hz and a sample rate of 8 kHz
     
     '

Интерфейс dsp.BinaryFileReader в основном очевиден. Следующее стоит заметить

  • HeaderStructure задает ожидаемый заголовок в начале файла.

  • NumChannels задает, сколько чередованных выборок ожидается в течение каждого раза момент. Это также определяет количество столбцов выходной матрицы, возвращенной объектом. Когда выполняется, объект возвращает матрицу размера SamplesPerFrame x NumChannels, сохраненный в файле главным строкой способом.

Следующее создает компоненты, которые помогают с визуальным анализом переданных потоком сигналов, и во время и в частотном диапазоне

Для визуализации временного интервала создается экземпляр dsp.TimeScope. Это используется, чтобы построить все кадры данных для обеих синусоид, когда они читаются из файла.

TimeScope = dsp.TimeScope('SampleRate',fs,'TimeSpan',frameLength/fs,...
    'ShowGrid',true,'YLimits',[-1 1],'TimeSpanOverrunAction','Scroll');

Для визуализации частотного диапазона создается экземпляр dsp.SpectrumAnalyzer. Это используется, чтобы анализировать спектр продукта между этими двумя синусоидами, ожидая два тональных компонента в (60-50) Гц и (60+50) Гц, соответственно.

Поскольку отношение между частотами в сигнале и частоте дискретизации является очень низким, сигналы сначала подкошены фактором 16 с экземпляром dsp.SampleRateConverter. Это понижает частоту дискретизации до 500 Гц и делает частотные составляющие из целевого более легко идентифицируемого использования сигнала стандартного спектрального анализа.

RateConverter = dsp.SampleRateConverter('InputSampleRate', 8000, ...
    'OutputSampleRate', 500, 'Bandwidth', 100);

% Note how a single spectral snapshot with the following settings
% requires 16384 input samples, far more than the 800 actually stored in
% the file
SpectAnalyzer = dsp.SpectrumAnalyzer(...
    'FrequencyResolutionMethod', 'WindowLength', 'WindowLength', 1024,...
    'FFTLengthSource', 'Property', 'FFTLength', 2048, 'SampleRate',500,...
    'PlotAsTwoSidedSpectrum', false, 'SpectralAverages', 16);

Следующий цикл с условием продолжения считывает данные из файла и визуализирует сигналы, когда они читаются. Это запускает в течение 10 минут ценность времени симуляции, независимо от фактического количества выборок в файле. Графики в двух переданной потоком визуализации совпадают с ожидаемым поведением.

simtime = 0;
% Run the loop until the simulation time is less than 10*60 seconds
while(simtime < 600)
    % Rewind file if there are no more new samples
    if isDone(BinReader)
        reset(BinReader)
    end
    % Read from binary file, 1024 samples per frame
    dataFrame = BinReader();
    % Visualize a single frame of both channels in the time domain
    TimeScope(dataFrame)
    % Decimate the two channels down to a new sample rate of 500 Hz,
    % resulting in a new frame length of 64 samples
    dataDecimated = RateConverter(dataFrame);
    % Analyze the product of the two sine waves in the frequency domain, by
    % accumulating multiple data frames internally and updating the
    % visualization when ready
    SpectAnalyzer(prod(dataDecimated,2))

    % Update value of simulation time elapsed
    simtime = simtime + frameLength/fs;
end

% Release control of files and scopes
release(BinReader)
release(RateConverter)
release(TimeScope)
release(SpectAnalyzer)

Сводные данные

Этот пример проиллюстрировал, как создать и использовать Системные объекты, чтобы читать из и записать в файлы числовых данных. Все используемые объекты (т.е. dspdemo.TextFileReader, dspdemo.TextFileWriter, dsp.BinaryFileReader и dsp.BinaryFileWriter) могут быть отредактированы, чтобы выполнить файл специального назначения читающие и пишущие операции.

Для получения дополнительной информации об объектах авторской системы для пользовательских алгоритмов см. Создание системных объектов.