Skip to content

Commit

Permalink
Add V2/V4 Siglent BIN import filter
Browse files Browse the repository at this point in the history
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
hansemro committed Aug 4, 2023
1 parent 148899e commit 2733371
Show file tree
Hide file tree
Showing 5 changed files with 390 additions and 0 deletions.
1 change: 1 addition & 0 deletions scopeprotocols/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ set(SCOPEPROTOCOLS_SOURCES
SDCmdDecoder.cpp
SDDataDecoder.cpp
SDRAMDecoderBase.cpp
SiglentBINImportFilter.cpp
SNRFilter.cpp
SParameterCascadeFilter.cpp
SParameterDeEmbedFilter.cpp
Expand Down
284 changes: 284 additions & 0 deletions scopeprotocols/SiglentBINImportFilter.cpp
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();
}
103 changes: 103 additions & 0 deletions scopeprotocols/SiglentBINImportFilter.h
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
Loading

0 comments on commit 2733371

Please sign in to comment.