-
Notifications
You must be signed in to change notification settings - Fork 101
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This allows importing of analog/math/digital waveforms from V2/V4 Siglent BIN waveform formats. Siglent scopes that export V2 BIN format: - SDS2000X Plus with FW 1.2.6 to 1.3.9RX - SDS5000X with FW 0.8.6 to 0.9.6 - SDS6000 with FW older than 1.4.1.0 Siglent scopes that export V4 BIN format: - SDS2000X Plus with FW newer than 1.4.0 - SDS5000X with FW newer than 0.9.6 - SDS6000 with FW newer than 1.4.1.0 - SDS2000X HD V2 and V4 spec details can be found on page 29 and 37 of E02A document, respectively. E02A: https://web.archive.org/web/20230730072643/https://www.siglenteu.com/wp-content/uploads/2021/08/How-to-Extract-Data-from-the-Binary-File.pdf
- Loading branch information
Showing
5 changed files
with
390 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,284 @@ | ||
/*********************************************************************************************************************** | ||
* * | ||
* libscopeprotocols * | ||
* * | ||
* Copyright (c) 2012-2023 Andrew D. Zonenberg and contributors * | ||
* All rights reserved. * | ||
* * | ||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * | ||
* following conditions are met: * | ||
* * | ||
* * Redistributions of source code must retain the above copyright notice, this list of conditions, and the * | ||
* following disclaimer. * | ||
* * | ||
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the * | ||
* following disclaimer in the documentation and/or other materials provided with the distribution. * | ||
* * | ||
* * Neither the name of the author nor the names of any contributors may be used to endorse or promote products * | ||
* derived from this software without specific prior written permission. * | ||
* * | ||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * | ||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * | ||
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * | ||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * | ||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * | ||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * | ||
* POSSIBILITY OF SUCH DAMAGE. * | ||
* * | ||
***********************************************************************************************************************/ | ||
|
||
#include "../scopehal/scopehal.h" | ||
#include "SiglentBINImportFilter.h" | ||
|
||
using namespace std; | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
// Construction / destruction | ||
|
||
SiglentBINImportFilter::SiglentBINImportFilter(const string& color) | ||
: ImportFilter(color) | ||
{ | ||
m_fpname = "Siglent (V2/V4) BIN File"; | ||
m_parameters[m_fpname] = FilterParameter(FilterParameter::TYPE_FILENAME, Unit(Unit::UNIT_COUNTS)); | ||
m_parameters[m_fpname].m_fileFilterMask = "*.bin"; | ||
m_parameters[m_fpname].m_fileFilterName = "V2/V4 Siglent binary waveform files (*.bin)"; | ||
m_parameters[m_fpname].signal_changed().connect(sigc::mem_fun(*this, &SiglentBINImportFilter::OnFileNameChanged)); | ||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
// Accessors | ||
|
||
string SiglentBINImportFilter::GetProtocolName() | ||
{ | ||
return "Siglent (V2/V4) BIN Import"; | ||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
// Actual decoder logic | ||
|
||
void SiglentBINImportFilter::OnFileNameChanged() | ||
{ | ||
//Wipe anything we may have had in the past | ||
ClearStreams(); | ||
|
||
auto fname = m_parameters[m_fpname].ToString(); | ||
if(fname.empty()) | ||
return; | ||
|
||
//Set waveform timestamp to file timestamp | ||
time_t timestamp = 0; | ||
int64_t fs = 0; | ||
GetTimestampOfFile(fname, timestamp, fs); | ||
|
||
string f = ReadFile(fname); | ||
uint32_t fpos = 0; | ||
|
||
FileHeader fh; | ||
f.copy((char*)&fh, sizeof(FileHeader), fpos); | ||
fpos += sizeof(FileHeader); | ||
|
||
switch(fh.version) | ||
{ | ||
case 2: | ||
break; | ||
case 4: | ||
fpos += 4; | ||
break; | ||
default: | ||
LogError("Unsupported version (%d) in file header\n", fh.version); | ||
return; | ||
} | ||
|
||
LogDebug("Version: %d\n", fh.version); | ||
|
||
//Parse waveform header | ||
WaveHeader wh; | ||
f.copy((char*)&wh, sizeof(WaveHeader), fpos); | ||
fpos += sizeof(WaveHeader); | ||
|
||
for(int i = 0; i < 4; i++) | ||
{ | ||
LogDebug("ch%d_en: %d\n", i+1, wh.ch_en[i]); | ||
LogDebug("ch%d_v_gain: %f\n", i+1, wh.ch_v_gain[i].value); | ||
LogDebug("ch%d_v_offset: %f\n", i+1, wh.ch_v_offset[i].value); | ||
LogDebug("ch%d_probe: %f\n", i+1, wh.ch_probe[i]); | ||
LogDebug("ch%d_codes_per_div: %d\n", i+1, wh.ch_codes_per_div[i]); | ||
} | ||
|
||
LogDebug("digital_en: %d\n", wh.digital_en); | ||
for(int i = 0; i < 16; i++) | ||
{ | ||
LogDebug("d%d_ch_en: %d\n", i, wh.d_ch_en[i]); | ||
} | ||
|
||
LogDebug("time_div: %f\n", wh.time_div); | ||
LogDebug("time_delay: %f\n", wh.time_delay); | ||
LogDebug("wave_length: %d\n", wh.wave_length); | ||
LogDebug("s_rate: %f\n", wh.s_rate); | ||
LogDebug("d_wave_length: %d\n", wh.d_wave_length); | ||
LogDebug("d_s_rate: %f\n", wh.d_s_rate); | ||
|
||
LogDebug("data_width: %d\n", wh.data_width); | ||
LogDebug("byte_order: %d\n", wh.byte_order); | ||
LogDebug("num_hori_div: %d\n", wh.num_hori_div); | ||
|
||
for(int i = 0; i < 4; i++) | ||
{ | ||
LogDebug("math%d_en: %d\n", i+1, wh.math_en[i]); | ||
LogDebug("math%d_v_gain: %f\n", i+1, wh.math_v_gain[i].value); | ||
LogDebug("math%d_v_offset: %f\n", i+1, wh.math_v_offset[i].value); | ||
LogDebug("math%d_wave_length: %d\n", i+1, wh.math_wave_length[i]); | ||
LogDebug("math%d_s_interval: %f\n", i+1, wh.math_s_interval[i]); | ||
} | ||
LogDebug("math_codes_per_div: %d\n", wh.math_codes_per_div); | ||
|
||
switch(fh.version) | ||
{ | ||
case 2: | ||
fpos = 0x800; | ||
break; | ||
case 4: | ||
fpos = 0x1000; | ||
break; | ||
default: | ||
LogError("Unsupported version (%d) in file header\n", fh.version); | ||
return; | ||
} | ||
|
||
//Process analog data | ||
uint32_t data_width = wh.data_width + 1; // number of bytes | ||
int32_t center_code = (1 << (8*data_width - 1)) - 1; | ||
|
||
uint32_t wave_idx = 0; | ||
for(int i = 0; i < 4; i++) | ||
{ | ||
if(wh.ch_en[i] == 1) | ||
{ | ||
string name = string("C") + to_string(i+1); | ||
AddStream(Unit(Unit::UNIT_VOLTS), name, Stream::STREAM_TYPE_ANALOG); | ||
auto wfm = new UniformAnalogWaveform; | ||
wfm->m_timescale = FS_PER_SECOND / wh.s_rate; | ||
wfm->m_startTimestamp = timestamp * FS_PER_SECOND; | ||
wfm->m_startFemtoseconds = fs; | ||
wfm->m_triggerPhase = 0; | ||
wfm->PrepareForCpuAccess(); | ||
SetData(wfm, m_streams.size() - 1); | ||
|
||
LogDebug("Waveform[%d]: %s\n", wave_idx, name.c_str()); | ||
double v_gain = wh.ch_v_gain[i].value * wh.ch_probe[i] / wh.ch_codes_per_div[i]; | ||
LogDebug("\tv_gain: %f\n", v_gain); | ||
LogDebug("\tcenter: %d\n", center_code); | ||
|
||
if(data_width == 2) | ||
{ | ||
for(size_t j = 0; j < wh.wave_length; j++) | ||
{ | ||
uint16_t* sample = (uint16_t*)(f.c_str() + fpos); | ||
float value = (((int32_t)*sample - center_code)) * v_gain - wh.ch_v_offset[i].value; | ||
wfm->m_samples.push_back(value); | ||
fpos += 2; | ||
} | ||
} | ||
else | ||
{ | ||
for(size_t j = 0; j < wh.wave_length; j++) | ||
{ | ||
uint8_t* sample = (uint8_t*)(f.c_str() + fpos); | ||
float value = ((int32_t)*sample - center_code) * v_gain - wh.ch_v_offset[i].value; | ||
wfm->m_samples.push_back(value); | ||
fpos += 1; | ||
} | ||
} | ||
|
||
wfm->MarkModifiedFromCpu(); | ||
AutoscaleVertical(wave_idx); | ||
wave_idx += 1; | ||
} | ||
} | ||
|
||
//Process math data | ||
for(int i = 0; i < 4; i++) | ||
{ | ||
if(wh.math_en[i] == 1) | ||
{ | ||
string name = string("Math") + to_string(i+1); | ||
AddStream(Unit(Unit::UNIT_VOLTS), name, Stream::STREAM_TYPE_ANALOG); | ||
auto wfm = new UniformAnalogWaveform; | ||
wfm->m_timescale = wh.math_s_interval[i] * FS_PER_SECOND; | ||
wfm->m_startTimestamp = timestamp * FS_PER_SECOND; | ||
wfm->m_startFemtoseconds = fs; | ||
wfm->m_triggerPhase = 0; | ||
wfm->PrepareForCpuAccess(); | ||
SetData(wfm, m_streams.size() - 1); | ||
|
||
LogDebug("Waveform[%d]: %s\n", wave_idx, name.c_str()); | ||
double v_gain = wh.math_v_gain[i].value / wh.math_codes_per_div; | ||
LogDebug("\tv_gain: %f\n", v_gain); | ||
LogDebug("\tcenter: %d\n", center_code); | ||
|
||
if(data_width == 2) | ||
{ | ||
for(size_t j = 0; j < wh.wave_length; j++) | ||
{ | ||
uint16_t* sample = (uint16_t*)(f.c_str() + fpos); | ||
float value = (((int32_t)*sample - center_code)) * v_gain - wh.math_v_offset[i].value; | ||
wfm->m_samples.push_back(value); | ||
fpos += 2; | ||
} | ||
} | ||
else | ||
{ | ||
for(size_t j = 0; j < wh.wave_length; j++) | ||
{ | ||
uint8_t* sample = (uint8_t*)(f.c_str() + fpos); | ||
float value = ((int32_t)*sample - center_code) * v_gain - wh.math_v_offset[i].value; | ||
wfm->m_samples.push_back(value); | ||
fpos += 1; | ||
} | ||
} | ||
|
||
wfm->MarkModifiedFromCpu(); | ||
AutoscaleVertical(wave_idx); | ||
wave_idx += 1; | ||
} | ||
} | ||
|
||
//Process digital data | ||
if(wh.digital_en) | ||
{ | ||
for(int i = 0; i < 16; i++) | ||
{ | ||
if(wh.d_ch_en[i] == 1) | ||
{ | ||
string name = string("D") + to_string(i); | ||
AddStream(Unit(Unit::UNIT_VOLTS), name, Stream::STREAM_TYPE_DIGITAL); | ||
auto wfm = new UniformDigitalWaveform; | ||
wfm->m_timescale = FS_PER_SECOND / wh.d_s_rate; | ||
wfm->m_startTimestamp = timestamp * FS_PER_SECOND; | ||
wfm->m_startFemtoseconds = fs; | ||
wfm->m_triggerPhase = 0; | ||
wfm->PrepareForCpuAccess(); | ||
SetData(wfm, m_streams.size() - 1); | ||
|
||
LogDebug("Waveform[%d]: %s\n", wave_idx, name.c_str()); | ||
for(size_t j = 0; j < (wh.d_wave_length / 8); j++) | ||
{ | ||
uint8_t samples = *(uint8_t*)(f.c_str() + fpos); | ||
for(int k = 0; k < 8; k++) | ||
{ | ||
bool value = samples & 0x1; | ||
samples >>= 1; | ||
wfm->m_samples.push_back(value); | ||
} | ||
fpos += 1; | ||
} | ||
|
||
wfm->MarkModifiedFromCpu(); | ||
AutoscaleVertical(wave_idx); | ||
wave_idx += 1; | ||
} | ||
} | ||
} | ||
|
||
m_outputsChangedSignal.emit(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/*********************************************************************************************************************** | ||
* * | ||
* libscopeprotocols * | ||
* * | ||
* Copyright (c) 2012-2023 Andrew D. Zonenberg and contributors * | ||
* All rights reserved. * | ||
* * | ||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * | ||
* following conditions are met: * | ||
* * | ||
* * Redistributions of source code must retain the above copyright notice, this list of conditions, and the * | ||
* following disclaimer. * | ||
* * | ||
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the * | ||
* following disclaimer in the documentation and/or other materials provided with the distribution. * | ||
* * | ||
* * Neither the name of the author nor the names of any contributors may be used to endorse or promote products * | ||
* derived from this software without specific prior written permission. * | ||
* * | ||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * | ||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * | ||
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * | ||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * | ||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * | ||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * | ||
* POSSIBILITY OF SUCH DAMAGE. * | ||
* * | ||
***********************************************************************************************************************/ | ||
|
||
/** | ||
@file | ||
@author Andrew D. Zonenberg | ||
@brief Declaration of SiglentBINImportFilter | ||
*/ | ||
#ifndef SiglentBINImportFilter_h | ||
#define SiglentBINImportFilter_h | ||
|
||
class SiglentBINImportFilter : public ImportFilter | ||
{ | ||
public: | ||
SiglentBINImportFilter(const std::string& color); | ||
|
||
static std::string GetProtocolName(); | ||
|
||
PROTOCOL_DECODER_INITPROC(SiglentBINImportFilter) | ||
|
||
//Siglent binary capture structs | ||
#pragma pack(push, 1) | ||
struct FileHeader | ||
{ | ||
uint32_t version; //File format version | ||
}; | ||
|
||
// V2/V4 wave header | ||
struct WaveHeader | ||
{ | ||
int32_t ch_en[4]; //C1-C4 channel enable | ||
struct { //C1-C4 vertical gain | ||
double value; | ||
char reserved[32]; | ||
} ch_v_gain[4]; | ||
struct { //C1-C4 vertical offset | ||
double value; | ||
char reserved[32]; | ||
} ch_v_offset[4]; | ||
int32_t digital_en; //Digital enable | ||
int32_t d_ch_en[16]; //D0-D15 channel enable | ||
double time_div; //Time base | ||
char reserved9[32]; | ||
double time_delay; //Trigger delay | ||
char reserved10[32]; | ||
uint32_t wave_length; //Number of samples in each analog waveform | ||
double s_rate; //C1-C4 sampling rate | ||
char reserved11[32]; | ||
uint32_t d_wave_length; //Number of samples in each digital waveform | ||
double d_s_rate; //D0-D15 sampling rate | ||
char reserved12[32]; | ||
double ch_probe[4]; //C1-C4 probe factor | ||
int8_t data_width; //0:1 Byte, 1:2 Bytes | ||
int8_t byte_order; //0:LSB, 1:MSB | ||
char reserved13[6]; | ||
int32_t num_hori_div; //Number of horizontal divisions | ||
int32_t ch_codes_per_div[4];//C1-C4 codes per division | ||
int32_t math_en[4]; //Math1-Math4 channel enable | ||
struct { //Math1-Math4 vertical gain | ||
double value; | ||
char reserved[32]; | ||
} math_v_gain[4]; | ||
struct { //Math1-Math4 vertical offset | ||
double value; | ||
char reserved[32]; | ||
} math_v_offset[4]; | ||
uint32_t math_wave_length[4];//Math1-Math4 number of samples | ||
double math_s_interval[4]; //Math1-Math4 sampling interval | ||
int32_t math_codes_per_div; //Math1-Math4 codes per division | ||
}; | ||
#pragma pack(pop) | ||
|
||
protected: | ||
void OnFileNameChanged(); | ||
}; | ||
|
||
#endif |
Oops, something went wrong.