Skip to content

Commit 2733371

Browse files
committed
Add V2/V4 Siglent BIN import filter
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
1 parent 148899e commit 2733371

File tree

5 files changed

+390
-0
lines changed

5 files changed

+390
-0
lines changed

scopeprotocols/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ set(SCOPEPROTOCOLS_SOURCES
131131
SDCmdDecoder.cpp
132132
SDDataDecoder.cpp
133133
SDRAMDecoderBase.cpp
134+
SiglentBINImportFilter.cpp
134135
SNRFilter.cpp
135136
SParameterCascadeFilter.cpp
136137
SParameterDeEmbedFilter.cpp
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
/***********************************************************************************************************************
2+
* *
3+
* libscopeprotocols *
4+
* *
5+
* Copyright (c) 2012-2023 Andrew D. Zonenberg and contributors *
6+
* All rights reserved. *
7+
* *
8+
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
9+
* following conditions are met: *
10+
* *
11+
* * Redistributions of source code must retain the above copyright notice, this list of conditions, and the *
12+
* following disclaimer. *
13+
* *
14+
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the *
15+
* following disclaimer in the documentation and/or other materials provided with the distribution. *
16+
* *
17+
* * Neither the name of the author nor the names of any contributors may be used to endorse or promote products *
18+
* derived from this software without specific prior written permission. *
19+
* *
20+
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
21+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
22+
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
23+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *
24+
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
25+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
26+
* POSSIBILITY OF SUCH DAMAGE. *
27+
* *
28+
***********************************************************************************************************************/
29+
30+
#include "../scopehal/scopehal.h"
31+
#include "SiglentBINImportFilter.h"
32+
33+
using namespace std;
34+
35+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
36+
// Construction / destruction
37+
38+
SiglentBINImportFilter::SiglentBINImportFilter(const string& color)
39+
: ImportFilter(color)
40+
{
41+
m_fpname = "Siglent (V2/V4) BIN File";
42+
m_parameters[m_fpname] = FilterParameter(FilterParameter::TYPE_FILENAME, Unit(Unit::UNIT_COUNTS));
43+
m_parameters[m_fpname].m_fileFilterMask = "*.bin";
44+
m_parameters[m_fpname].m_fileFilterName = "V2/V4 Siglent binary waveform files (*.bin)";
45+
m_parameters[m_fpname].signal_changed().connect(sigc::mem_fun(*this, &SiglentBINImportFilter::OnFileNameChanged));
46+
}
47+
48+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
49+
// Accessors
50+
51+
string SiglentBINImportFilter::GetProtocolName()
52+
{
53+
return "Siglent (V2/V4) BIN Import";
54+
}
55+
56+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
57+
// Actual decoder logic
58+
59+
void SiglentBINImportFilter::OnFileNameChanged()
60+
{
61+
//Wipe anything we may have had in the past
62+
ClearStreams();
63+
64+
auto fname = m_parameters[m_fpname].ToString();
65+
if(fname.empty())
66+
return;
67+
68+
//Set waveform timestamp to file timestamp
69+
time_t timestamp = 0;
70+
int64_t fs = 0;
71+
GetTimestampOfFile(fname, timestamp, fs);
72+
73+
string f = ReadFile(fname);
74+
uint32_t fpos = 0;
75+
76+
FileHeader fh;
77+
f.copy((char*)&fh, sizeof(FileHeader), fpos);
78+
fpos += sizeof(FileHeader);
79+
80+
switch(fh.version)
81+
{
82+
case 2:
83+
break;
84+
case 4:
85+
fpos += 4;
86+
break;
87+
default:
88+
LogError("Unsupported version (%d) in file header\n", fh.version);
89+
return;
90+
}
91+
92+
LogDebug("Version: %d\n", fh.version);
93+
94+
//Parse waveform header
95+
WaveHeader wh;
96+
f.copy((char*)&wh, sizeof(WaveHeader), fpos);
97+
fpos += sizeof(WaveHeader);
98+
99+
for(int i = 0; i < 4; i++)
100+
{
101+
LogDebug("ch%d_en: %d\n", i+1, wh.ch_en[i]);
102+
LogDebug("ch%d_v_gain: %f\n", i+1, wh.ch_v_gain[i].value);
103+
LogDebug("ch%d_v_offset: %f\n", i+1, wh.ch_v_offset[i].value);
104+
LogDebug("ch%d_probe: %f\n", i+1, wh.ch_probe[i]);
105+
LogDebug("ch%d_codes_per_div: %d\n", i+1, wh.ch_codes_per_div[i]);
106+
}
107+
108+
LogDebug("digital_en: %d\n", wh.digital_en);
109+
for(int i = 0; i < 16; i++)
110+
{
111+
LogDebug("d%d_ch_en: %d\n", i, wh.d_ch_en[i]);
112+
}
113+
114+
LogDebug("time_div: %f\n", wh.time_div);
115+
LogDebug("time_delay: %f\n", wh.time_delay);
116+
LogDebug("wave_length: %d\n", wh.wave_length);
117+
LogDebug("s_rate: %f\n", wh.s_rate);
118+
LogDebug("d_wave_length: %d\n", wh.d_wave_length);
119+
LogDebug("d_s_rate: %f\n", wh.d_s_rate);
120+
121+
LogDebug("data_width: %d\n", wh.data_width);
122+
LogDebug("byte_order: %d\n", wh.byte_order);
123+
LogDebug("num_hori_div: %d\n", wh.num_hori_div);
124+
125+
for(int i = 0; i < 4; i++)
126+
{
127+
LogDebug("math%d_en: %d\n", i+1, wh.math_en[i]);
128+
LogDebug("math%d_v_gain: %f\n", i+1, wh.math_v_gain[i].value);
129+
LogDebug("math%d_v_offset: %f\n", i+1, wh.math_v_offset[i].value);
130+
LogDebug("math%d_wave_length: %d\n", i+1, wh.math_wave_length[i]);
131+
LogDebug("math%d_s_interval: %f\n", i+1, wh.math_s_interval[i]);
132+
}
133+
LogDebug("math_codes_per_div: %d\n", wh.math_codes_per_div);
134+
135+
switch(fh.version)
136+
{
137+
case 2:
138+
fpos = 0x800;
139+
break;
140+
case 4:
141+
fpos = 0x1000;
142+
break;
143+
default:
144+
LogError("Unsupported version (%d) in file header\n", fh.version);
145+
return;
146+
}
147+
148+
//Process analog data
149+
uint32_t data_width = wh.data_width + 1; // number of bytes
150+
int32_t center_code = (1 << (8*data_width - 1)) - 1;
151+
152+
uint32_t wave_idx = 0;
153+
for(int i = 0; i < 4; i++)
154+
{
155+
if(wh.ch_en[i] == 1)
156+
{
157+
string name = string("C") + to_string(i+1);
158+
AddStream(Unit(Unit::UNIT_VOLTS), name, Stream::STREAM_TYPE_ANALOG);
159+
auto wfm = new UniformAnalogWaveform;
160+
wfm->m_timescale = FS_PER_SECOND / wh.s_rate;
161+
wfm->m_startTimestamp = timestamp * FS_PER_SECOND;
162+
wfm->m_startFemtoseconds = fs;
163+
wfm->m_triggerPhase = 0;
164+
wfm->PrepareForCpuAccess();
165+
SetData(wfm, m_streams.size() - 1);
166+
167+
LogDebug("Waveform[%d]: %s\n", wave_idx, name.c_str());
168+
double v_gain = wh.ch_v_gain[i].value * wh.ch_probe[i] / wh.ch_codes_per_div[i];
169+
LogDebug("\tv_gain: %f\n", v_gain);
170+
LogDebug("\tcenter: %d\n", center_code);
171+
172+
if(data_width == 2)
173+
{
174+
for(size_t j = 0; j < wh.wave_length; j++)
175+
{
176+
uint16_t* sample = (uint16_t*)(f.c_str() + fpos);
177+
float value = (((int32_t)*sample - center_code)) * v_gain - wh.ch_v_offset[i].value;
178+
wfm->m_samples.push_back(value);
179+
fpos += 2;
180+
}
181+
}
182+
else
183+
{
184+
for(size_t j = 0; j < wh.wave_length; j++)
185+
{
186+
uint8_t* sample = (uint8_t*)(f.c_str() + fpos);
187+
float value = ((int32_t)*sample - center_code) * v_gain - wh.ch_v_offset[i].value;
188+
wfm->m_samples.push_back(value);
189+
fpos += 1;
190+
}
191+
}
192+
193+
wfm->MarkModifiedFromCpu();
194+
AutoscaleVertical(wave_idx);
195+
wave_idx += 1;
196+
}
197+
}
198+
199+
//Process math data
200+
for(int i = 0; i < 4; i++)
201+
{
202+
if(wh.math_en[i] == 1)
203+
{
204+
string name = string("Math") + to_string(i+1);
205+
AddStream(Unit(Unit::UNIT_VOLTS), name, Stream::STREAM_TYPE_ANALOG);
206+
auto wfm = new UniformAnalogWaveform;
207+
wfm->m_timescale = wh.math_s_interval[i] * FS_PER_SECOND;
208+
wfm->m_startTimestamp = timestamp * FS_PER_SECOND;
209+
wfm->m_startFemtoseconds = fs;
210+
wfm->m_triggerPhase = 0;
211+
wfm->PrepareForCpuAccess();
212+
SetData(wfm, m_streams.size() - 1);
213+
214+
LogDebug("Waveform[%d]: %s\n", wave_idx, name.c_str());
215+
double v_gain = wh.math_v_gain[i].value / wh.math_codes_per_div;
216+
LogDebug("\tv_gain: %f\n", v_gain);
217+
LogDebug("\tcenter: %d\n", center_code);
218+
219+
if(data_width == 2)
220+
{
221+
for(size_t j = 0; j < wh.wave_length; j++)
222+
{
223+
uint16_t* sample = (uint16_t*)(f.c_str() + fpos);
224+
float value = (((int32_t)*sample - center_code)) * v_gain - wh.math_v_offset[i].value;
225+
wfm->m_samples.push_back(value);
226+
fpos += 2;
227+
}
228+
}
229+
else
230+
{
231+
for(size_t j = 0; j < wh.wave_length; j++)
232+
{
233+
uint8_t* sample = (uint8_t*)(f.c_str() + fpos);
234+
float value = ((int32_t)*sample - center_code) * v_gain - wh.math_v_offset[i].value;
235+
wfm->m_samples.push_back(value);
236+
fpos += 1;
237+
}
238+
}
239+
240+
wfm->MarkModifiedFromCpu();
241+
AutoscaleVertical(wave_idx);
242+
wave_idx += 1;
243+
}
244+
}
245+
246+
//Process digital data
247+
if(wh.digital_en)
248+
{
249+
for(int i = 0; i < 16; i++)
250+
{
251+
if(wh.d_ch_en[i] == 1)
252+
{
253+
string name = string("D") + to_string(i);
254+
AddStream(Unit(Unit::UNIT_VOLTS), name, Stream::STREAM_TYPE_DIGITAL);
255+
auto wfm = new UniformDigitalWaveform;
256+
wfm->m_timescale = FS_PER_SECOND / wh.d_s_rate;
257+
wfm->m_startTimestamp = timestamp * FS_PER_SECOND;
258+
wfm->m_startFemtoseconds = fs;
259+
wfm->m_triggerPhase = 0;
260+
wfm->PrepareForCpuAccess();
261+
SetData(wfm, m_streams.size() - 1);
262+
263+
LogDebug("Waveform[%d]: %s\n", wave_idx, name.c_str());
264+
for(size_t j = 0; j < (wh.d_wave_length / 8); j++)
265+
{
266+
uint8_t samples = *(uint8_t*)(f.c_str() + fpos);
267+
for(int k = 0; k < 8; k++)
268+
{
269+
bool value = samples & 0x1;
270+
samples >>= 1;
271+
wfm->m_samples.push_back(value);
272+
}
273+
fpos += 1;
274+
}
275+
276+
wfm->MarkModifiedFromCpu();
277+
AutoscaleVertical(wave_idx);
278+
wave_idx += 1;
279+
}
280+
}
281+
}
282+
283+
m_outputsChangedSignal.emit();
284+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/***********************************************************************************************************************
2+
* *
3+
* libscopeprotocols *
4+
* *
5+
* Copyright (c) 2012-2023 Andrew D. Zonenberg and contributors *
6+
* All rights reserved. *
7+
* *
8+
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
9+
* following conditions are met: *
10+
* *
11+
* * Redistributions of source code must retain the above copyright notice, this list of conditions, and the *
12+
* following disclaimer. *
13+
* *
14+
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the *
15+
* following disclaimer in the documentation and/or other materials provided with the distribution. *
16+
* *
17+
* * Neither the name of the author nor the names of any contributors may be used to endorse or promote products *
18+
* derived from this software without specific prior written permission. *
19+
* *
20+
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
21+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
22+
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
23+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *
24+
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
25+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
26+
* POSSIBILITY OF SUCH DAMAGE. *
27+
* *
28+
***********************************************************************************************************************/
29+
30+
/**
31+
@file
32+
@author Andrew D. Zonenberg
33+
@brief Declaration of SiglentBINImportFilter
34+
*/
35+
#ifndef SiglentBINImportFilter_h
36+
#define SiglentBINImportFilter_h
37+
38+
class SiglentBINImportFilter : public ImportFilter
39+
{
40+
public:
41+
SiglentBINImportFilter(const std::string& color);
42+
43+
static std::string GetProtocolName();
44+
45+
PROTOCOL_DECODER_INITPROC(SiglentBINImportFilter)
46+
47+
//Siglent binary capture structs
48+
#pragma pack(push, 1)
49+
struct FileHeader
50+
{
51+
uint32_t version; //File format version
52+
};
53+
54+
// V2/V4 wave header
55+
struct WaveHeader
56+
{
57+
int32_t ch_en[4]; //C1-C4 channel enable
58+
struct { //C1-C4 vertical gain
59+
double value;
60+
char reserved[32];
61+
} ch_v_gain[4];
62+
struct { //C1-C4 vertical offset
63+
double value;
64+
char reserved[32];
65+
} ch_v_offset[4];
66+
int32_t digital_en; //Digital enable
67+
int32_t d_ch_en[16]; //D0-D15 channel enable
68+
double time_div; //Time base
69+
char reserved9[32];
70+
double time_delay; //Trigger delay
71+
char reserved10[32];
72+
uint32_t wave_length; //Number of samples in each analog waveform
73+
double s_rate; //C1-C4 sampling rate
74+
char reserved11[32];
75+
uint32_t d_wave_length; //Number of samples in each digital waveform
76+
double d_s_rate; //D0-D15 sampling rate
77+
char reserved12[32];
78+
double ch_probe[4]; //C1-C4 probe factor
79+
int8_t data_width; //0:1 Byte, 1:2 Bytes
80+
int8_t byte_order; //0:LSB, 1:MSB
81+
char reserved13[6];
82+
int32_t num_hori_div; //Number of horizontal divisions
83+
int32_t ch_codes_per_div[4];//C1-C4 codes per division
84+
int32_t math_en[4]; //Math1-Math4 channel enable
85+
struct { //Math1-Math4 vertical gain
86+
double value;
87+
char reserved[32];
88+
} math_v_gain[4];
89+
struct { //Math1-Math4 vertical offset
90+
double value;
91+
char reserved[32];
92+
} math_v_offset[4];
93+
uint32_t math_wave_length[4];//Math1-Math4 number of samples
94+
double math_s_interval[4]; //Math1-Math4 sampling interval
95+
int32_t math_codes_per_div; //Math1-Math4 codes per division
96+
};
97+
#pragma pack(pop)
98+
99+
protected:
100+
void OnFileNameChanged();
101+
};
102+
103+
#endif

0 commit comments

Comments
 (0)