Binary Export Format - Logic 2
In Logic 2, both digital and analog binary files start with the same header, which can be used to determine the type of data stored in the file.
  • All multi-byte values are in little endian byte-order
  • Floating point values are IEE754
Digital and analog binary files have the same initial header that can be used to determine whether it is a valid Saleae binary export file, the version, and whether it contains analog or digital data.
1
byte[8] identifier;
2
int32 version;
3
int32 type;
Copied!
For Saleae binary exports, the identifier will always be <SALEAE>. The current version is 0.
Types: 0 - Digital 1 - Analog
The remaining format is based on the type of the data.

Digital

File structure (starting with the shared header above)
1
byte[8] identifier;
2
int32 version;
3
int32 type;
4
uint32 initial_state;
5
double begin_time;
6
double end_time;
7
uint64 num_transitions;
8
for each transition in num_transitions
9
double transition_time;
Copied!

Analog

File structure (starting with the shared header above)
1
byte[8] identifier;
2
int32 version;
3
int32 type;
4
double begin_time;
5
uint64 sample_rate;
6
uint64 downsample;
7
uint64 num_samples;
8
for each sample in num_samples:
9
float voltage;
Copied!

Sample Python code for reading binary data

Digital data
1
import array
2
import struct
3
import sys
4
from collections import namedtuple
5
6
TYPE_DIGITAL = 0
7
TYPE_ANALOG = 1
8
expected_version = 0
9
10
DigitalData = namedtuple('DigitalData', ('initial_state', 'begin_time', 'end_time', 'num_transitions', 'transition_times'))
11
12
def parse_digital(f):
13
# Parse header
14
identifier = f.read(8)
15
if identifier != b"<SALEAE>":
16
raise Exception("Not a saleae file")
17
18
version, datatype = struct.unpack('=ii', f.read(8))
19
20
if version != expected_version or datatype != TYPE_DIGITAL:
21
raise Exception("Unexpected data type: {}".format(datatype))
22
23
# Parse digital-specific data
24
initial_state, begin_time, end_time, num_transitions = struct.unpack('=iddq', f.read(28))
25
26
# Parse transition times
27
transition_times = array.array('d')
28
transition_times.fromfile(f, num_transitions)
29
30
return DigitalData(initial_state, begin_time, end_time, num_transitions, transition_times)
31
32
33
if __name__ == '__main__':
34
filename = sys.argv[1]
35
print("Opening " + filename)
36
37
with open(filename, 'rb') as f:
38
data = parse_digital(f)
39
40
# Print out all digital data
41
initial_state_str = 'low' if data.initial_state == 0 else 'high'
42
print('Initial state: ' + initial_state_str)
43
print('Begin time: {0:.6f}'.format(data.begin_time))
44
print('End time: {0:.6f}'.format(data.end_time))
45
print("Num transitions: {}".format(data.num_transitions))
46
47
cur_state = data.initial_state
48
49
print(" {0:>20} {1}".format("Time", "Bit State"))
50
print(" {0:>20.6f} {1}".format(data.begin_time, 'low' if cur_state == 0 else 'high'))
51
52
for time in data.transition_times:
53
# This is a transition, flip the bit state
54
cur_state = 0 if cur_state else 1
55
print(" {0:>20.6f} {1}".format(time, 'low' if cur_state == 0 else 'high'))
Copied!
Example output:
1
Opening binary4/digital_5.bin
2
Identifier: <SALEAE>
3
Version: 0
4
Datatype: 0
5
Initial state: low
6
Begin time: 0.000000
7
End time: 0.033341
8
Num transitions: 1031
9
Time Bit State
10
0.000000 low
11
0.000009 high
12
0.000028 low
13
0.000037 high
14
0.000099 low
15
0.000139 high
16
0.000168 low
17
0.000171 high
18
0.000228 low
19
0.000289 high
20
0.000332 low
21
...
Copied!
Analog data
1
import array
2
import struct
3
import sys
4
from collections import namedtuple
5
6
TYPE_DIGITAL = 0
7
TYPE_ANALOG = 1
8
expected_version = 0
9
10
AnalogData = namedtuple('AnalogData', ('begin_time', 'sample_rate', 'downsample', 'num_samples', 'samples'))
11
12
def parse_analog(f):
13
# Parse header
14
identifier = f.read(8)
15
if identifier != b"<SALEAE>":
16
raise Exception("Not a saleae file")
17
18
version, datatype = struct.unpack('=ii', f.read(8))
19
20
if version != expected_version or datatype != TYPE_ANALOG:
21
raise Exception("Unexpected data type: {}".format(datatype))
22
23
# Parse analog-specific data
24
begin_time, sample_rate, downsample, num_samples = struct.unpack('=dqqq', f.read(32))
25
26
# Parse samples
27
samples = array.array("f")
28
samples.fromfile(f, num_samples)
29
30
return AnalogData(begin_time, sample_rate, downsample, num_samples, samples)
31
32
33
if __name__ == '__main__':
34
filename = sys.argv[1]
35
print("Opening " + filename)
36
37
with open(filename, 'rb') as f:
38
data = parse_analog(f)
39
40
# Print out all analog data
41
print("Begin time: {}".format(data.begin_time))
42
print("Sample rate: {}".format(data.sample_rate))
43
print("Downsample: {}".format(data.downsample))
44
print("Number of samples: {}".format(data.num_samples))
45
46
print(" {0:>20} {1:>10}".format("Time", "Voltage"))
47
48
for idx, voltage in enumerate(data.samples):
49
sample_num = idx * data.downsample
50
time = data.begin_time + (float(sample_num) / data.sample_rate)
51
print(" {0:>20.10f} {1:>10.3f}".format(time, voltage))
Copied!
Example output:
1
Opening binary_export/analog_5.bin
2
Identifier: <SALEAE>
3
Version: 0
4
Datatype: 1
5
Begin time: 0.0
6
Sample rate: 50000000
7
Downsample: 1
8
Number of samples: 1667072
9
Time Voltage
10
0.0000000000 -0.002
11
0.0000000200 0.784
12
0.0000000400 1.560
13
0.0000000600 2.332
14
0.0000000800 3.089
15
0.0000001000 3.827
16
0.0000001200 4.540
17
0.0000001400 5.223
18
0.0000001600 5.873
19
0.0000001800 6.493
20
...
Copied!

3rd Party Binary Parser

A community user has generously shared their C-based binary parser for digital channels! Their GitHub repository for it can be found here: saleae-binparser

Logic 1.x

If you are using Logic 1.x, please refer to the articles below.
Last modified 1mo ago