Преобразование CSV-файла Tektronix в звуковой формат Печать
Добавил(а) microsin   

Осциллограф Tektronix MSO2014 может сохранить сигнал на флешку в файл формата CSV. Это стандартный, широко используемый текстовый формат для хранения таблиц [1], его можно загрузить в Microsoft Excel или просмотреть в текстовом редакторе. Но к сожалению, для звукового анализа такой формат подходит не очень хорошо. Лучше всего использовать файл в WAV-формате [2], тогда можно его прослушать, открыть в звуковом редакторе, просмотреть форму сигнала и оценить спектр. Для этой цели я написал утилиту конвертера, которая из CSV-файла Tektronix MSO2014 генерирует стандартный WAV-файл.

Вот так выглядит начало CSV-файла осциллографа Tektronix MSO2014:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
Model,MSO2014
Firmware Version,1.25
 
Point Format,Y,
Horizontal Units,S,
Horizontal Scale,0.2,
Sample Interval,1.6e-05,
Filter Frequency,1e+08,
Record Length,125000,
Gating,0.0% to 100.0%,
Probe Attenuation,1,
Vertical Units,V,
Vertical Offset,0,
Vertical Scale,2,
Label,,
TIME,REF1,REF1 Peak Detect
-1.0000e+00,-0.28,-0.0400002
-9.9998e-01,-0.36,-0.44
-9.9997e-01,-0.2,-0.0400002
-9.9995e-01,-0.2,-0.36
-9.9994e-01,-0.36,-0.0400002
-9.9992e-01,-0.12,-0.36
-9.9990e-01,-0.2,-0.0400002
-9.9989e-01,-0.28,-0.44
-9.9987e-01,-0.2,-0.0400002
...

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

1, 2. Это общая информация об осциллографе (модель, прошивка).
4. Назначение этой строки я не понял, но для преобразования данных в WAV-файл она не понадобится.
5. Информационная строка, которая говорит, что по оси X отображается шкала времени (секунды). Именно так и есть для всех обычных осциллограмм.
6. Длительность по горизонтали в секундах на одну клетку. В данном примере 0.2 секунды.
7. Важный параметр - интервал времени между выборками сигнала в секундах. Приведен в "научном" экспоненциальном формате. В данном примере это число 1.6e-05 с = 1.6 * 10-5 с = 0.000016 с = 16 мкс. Это соответствует частоте дискретизации 1/0.000016 Гц = 62500 Гц. Этот параметр будет использован при формировании заголовка WAV-файла.
8. Частота настройки фильтра, Гц. Это ничего не значащий для нас параметр, в данном примере он просто обозначает полосу пропускания вертикального канала осциллографа (100 МГц).
9. Количество выборок сигнала, которые записаны в этом файле (в этом примере 125 тысяч выборок). Это тоже важный параметр, который будет использоваться в формировании WAV-файла.
10. Непонятно, для чего это нужно.
11. Установленный коэффициент деления на щупе осциллографа.
12. Единицы отображаемого сигнала по шкале Y - вольты.
13. Смещение сигнала по Y в вольтах (0V).
14. Выбранная шкала усиления по Y в вольтах (2V в клетке).
15. Непонятно, для чего это.
16. Информационная строка, обозначающая назначение столбцов таблицы сигнала. Судя по всему, первый столбец TIME обозначает абсолютное время. Второй столбец REF1 это наверняка значение амплитуды выборки оцифрованного сигнала. Судя по названию третьего столбца REF1 Peak Detect это скорее всего какие-то пиковые значения сигнала.
17, 18, ..., 125016 Начиная со строки 17 до конца файла идет таблица оцифрованного сигнала. В данном примере таблица имеет 125000 строк (от 17 до 125016 включительно), обозначающих данные выборок.

По второму и третьему столбцу табличных значений можно довольно просто синтезировать 32-битный, двухканальный WAV-файл, где каждое значение хранится в формате 32-битного числа с плавающей запятой одинарной точности (IEEE 754 Single-precision floating-point format [3]).

[Заголовок WAV-файла]

Ниже приведен код функции, который формирует в памяти (буфер в MemoryStream) данные заголовка.

static void WriteWavHeader(ref MemoryStream stream, bool isFloatingPoint,
                           ushort channelCount, ushort bitDepth,
                           int sampleRate, int totalSampleCount)
{
   stream.Position = 0;
   // RIFF header, идентификатор секции
   stream.Write(Encoding.ASCII.GetBytes("RIFF"), 0, 4);
   // Размер секции = размер файла - 8.
   stream.Write(BitConverter.GetBytes(((bitDepth / 8) * totalSampleCount) + 36), 0, 4);
   // Идентификатор секции формата
   stream.Write(Encoding.ASCII.GetBytes("WAVE"), 0, 4);
   // Подсекция 1, формат
   stream.Write(Encoding.ASCII.GetBytes("fmt "), 0, 4);
   // Размер подсекции формата 16.
   stream.Write(BitConverter.GetBytes(16), 0, 4);
   // Формат выборок звука (floating point (3) или PCM (1)). Любые другие значения 
   // обозначают форматы со сжатием.
   stream.Write(BitConverter.GetBytes((ushort)(isFloatingPoint ? 3 : 1)), 0, 2);
   // Количество каналов.
   stream.Write(BitConverter.GetBytes(channelCount), 0, 2);
   // Частота выборок (Гц).
   stream.Write(BitConverter.GetBytes(sampleRate), 0, 4);
   // Частота следования байт.
   stream.Write(BitConverter.GetBytes(sampleRate * channelCount * (bitDepth / 8)), 0, 4);
   // Выравнивание блока.
   stream.Write(BitConverter.GetBytes((ushort)channelCount * (bitDepth / 8)), 0, 2);
   // Количество бит на одну выборку.
   stream.Write(BitConverter.GetBytes(bitDepth), 0, 2);
   // Подсекция 2, данные.
   stream.Write(Encoding.ASCII.GetBytes("data"), 0, 4);
   // Размер данных.
   stream.Write(BitConverter.GetBytes((bitDepth / 8) * totalSampleCount), 0, 4);
   //Дальше в файле должны идти данные оцифровки.
}

Для нашего примера эта функция должна быть вызвана со следующими параметрами:

MemoryStream header = new MemoryStream();
double sampleInterval = 0.000016;
int samples = 125000;
WriteWavHeader(ref header,                // поток для буфера данных заголовка
               true,                      // используется формат с плавающей запятой
               2,                         // 2 канала
               32,                        // 32 бита на каждую выборку
               (int)(1/sampleInterval),   // частота дискретизации, Гц
               samples);                  // количество 2-канальных выборок

Теперь в цикле нужно сформировать данные для WAV-файла. Выборки данных должны идти одна за другой, в таком порядке (будем использовать только столбцы REF1 и REF1 Peak Detect): выборка REF1 первой строки, потом выборка REF1 Peak Detect первой строки, потом выборка REF1 второй строки, потом выборка REF1 Peak Detect второй строки, и так далее, до последней 125000 строки. Полный код и скомпилированную программу можно скачать по ссылке [4].

[Использование csv2wav.exe]

Пользоваться утилитой довольно просто. Без ключей она выводит подсказку по командной строке. Для преобразования нужно запустить csv2wav.exe с указанием имени преобразуемого файла. Имя выходного файла сгенерируется автоматически из имени входного, просто будет изменено расширение файла на *.wav. Если добавить опцию -i, то дополнительно будет выведена информация из заголовка CSV-файла Tektronix.

C:\TEMP>csv2wav.exe
CSV to WAV converter (using with TEKTRONIX MSO2014 oscilloscope).
Published by microsin.net 180405.
Usage: csv2wav [-i] [csvfile] [wavfile]
 
C:\TEMP>csv2wav.exe -i t0000rf1.csv
Oscilloscope model MSO2014
Firmware           1.25
X units:           S
X scale:           0.2s
Sample interlal:   0.000016s
Filter Frequency:  1e+08
Samples:           125000
Gating:            0.0% to 100.0%
Probe Attenuation: 1
Y units:           V
Y offset:          0
Y scale:           2V
 
C:\TEMP>

[Ссылки]

1. Comma-separated values site:wikipedia.org.
2. Wave File Format - формат звукового файла WAV.
3. Single-precision floating-point format site:wikipedia.org.
4. 180405csv2wav.zip - проект Visual Studio C# и готовая скомпилированная программа, документация.