• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 17270491352

27 Aug 2025 02:57PM UTC coverage: 65.333%. Remained the same
17270491352

push

github

paulmthompson
Merge branch 'main' of https://github.com/paulmthompson/WhiskerToolbox

352 of 628 new or added lines in 92 files covered. (56.05%)

357 existing lines in 24 files now uncovered.

26429 of 40453 relevant lines covered (65.33%)

1119.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/DataManager/loaders/binary_loaders.hpp
1
#ifndef BINARY_LOADERS_HPP
2
#define BINARY_LOADERS_HPP
3

4
#include <chrono>
5
#include <filesystem>
6
#include <fstream>
7
#include <iostream>
8
#include <stdexcept>
9
#include <type_traits>
10
#include <vector>
11

12
namespace Loader {
13

14
struct BinaryAnalogOptions {
15
    std::string file_path;
16
    size_t header_size_bytes = 0;
17
    size_t num_channels = 1;
18
};
19

20
/**
21
 * @brief readBinaryFile
22
 *
23
 * Reads a binary file and returns the data as a vector of type T
24
 *
25
 * @param file_path Path to the binary file
26
 * @param header_size_bytes (optional) Number of bytes to skip at the beginning of the file
27
 * @return std::vector<T> Vector of data read from the file
28
 */
29
template<typename T>
30
inline std::vector<T> readBinaryFile(BinaryAnalogOptions const & options) {
×
31

32
    auto t1 = std::chrono::steady_clock::now();
×
33

34
    std::ifstream file(options.file_path, std::ios::binary | std::ios::ate);// seeks to end
×
35

36
    if (!file) {
×
37
        std::cout << "Cannot open file: " << options.file_path << std::endl;
×
38
        return std::vector<T>();
×
39
    }
40

41
    std::streampos const file_size = file.tellg();
×
42

43
    size_t const data_size_bytes = static_cast<size_t>(file_size) - options.header_size_bytes;
×
44
    size_t num_samples = data_size_bytes / sizeof(T);
×
45

46
    std::vector<T> data(num_samples);
×
47

48
    file.seekg(static_cast<std::ios::off_type>(options.header_size_bytes), std::ios::beg);
×
49
    file.read(reinterpret_cast<char *>(data.data()), static_cast<std::streamsize>(data_size_bytes));
×
50

51
    auto t2 = std::chrono::steady_clock::now();
×
52

53
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
×
54

55
    std::cout << "Total time to load " << options.file_path << ": " << elapsed.count() << std::endl;
×
56

57
    return data;
×
58
}
×
59

60
template<typename T>
61
inline std::vector<std::vector<T>> readBinaryFileMultiChannel(BinaryAnalogOptions const & options) {
×
62

63
    auto t1 = std::chrono::steady_clock::now();
×
64

65
    if (options.num_channels <= 0) {
×
66
        std::cout << "Channels cannot be less than 1" << std::endl;
×
67
        return std::vector<std::vector<T>>();
×
68
    }
69

70
    std::ifstream file(options.file_path, std::ios::binary | std::ios::ate);// Opens file at end
×
71

72
    if (!file) {
×
73
        std::cout << "Cannot open file: " << options.file_path << std::endl;
×
74
        return std::vector<std::vector<T>>();
×
75
    }
76

77
    std::streampos const file_size_bytes = file.tellg();
×
NEW
78
    if (file_size_bytes < static_cast<std::streamoff>(options.header_size_bytes)) {
×
79
        std::cout << "File size is smaller than header size" << std::endl;
×
80
        return std::vector<std::vector<T>>();
×
81
    }
82

83
    size_t const data_size_bytes = static_cast<size_t>(file_size_bytes) - options.header_size_bytes;
×
84
    size_t bytes_per_sample_set = options.num_channels * sizeof(T);
×
85

86
    if (data_size_bytes % bytes_per_sample_set != 0) {
×
87
        std::cout << "Warning: The bytes in data is not a multiple of number of channels" << std::endl;
×
88
    }
89

90
    size_t num_samples_per_channel = data_size_bytes / bytes_per_sample_set;
×
91

92
    std::vector<std::vector<T>> data;
×
93
    data.resize(options.num_channels);
×
94

95
    for (size_t i = 0; i < options.num_channels; i++) {
×
96
        //data[i].reserve(num_samples_per_channel);
97
        data[i].resize(num_samples_per_channel);
×
98
    }
99

100
    file.seekg(static_cast<std::ios::off_type>(options.header_size_bytes), std::ios::beg);
×
101

102
    std::vector<T> time_slice_buffer(options.num_channels);
×
103

104
    size_t current_time_index = 0;
×
NEW
105
    while (file.read(reinterpret_cast<char *>(&time_slice_buffer[0]), static_cast<std::streamsize>(sizeof(T) * options.num_channels))) {
×
106
        for (size_t i = 0; i < options.num_channels; ++i) {
×
107
            //data[i].push_back(time_slice_buffer[i]);
108
            data[i][current_time_index] = time_slice_buffer[i];
×
109
        }
110
        current_time_index++;
×
111
    }
112

113
    auto t2 = std::chrono::steady_clock::now();
×
114

115
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
×
116

117
    std::cout << "Total time to load " << elapsed.count() << std::endl;
×
118
    return data;
×
119
}
×
120

121

122
/**
123
 * @brief extractDigitalData
124
 *
125
 * This reads a vector of unsigned ints where
126
 * each bit represents a digital channel and extracts
127
 * the data for a specific channel (1 or 0)
128
 *
129
 * @param data Vector of data
130
 * @param channel Channel to extract
131
 * @return std::vector<int> Vector of digital data
132
 */
133
template<typename T, typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, int>::type = 0>
134
std::vector<int> extractDigitalData(std::vector<T> const & data, int channel) {
×
135

136
    T const ttl_mask = 1 << channel;
×
137

138
    std::cout << "Extracting digital data from " << data.size() << " samples" << std::endl;
×
139

140
    std::vector<int> digital_data;
×
141
    for (auto & d: data) {
×
142
        digital_data.push_back((ttl_mask & d) > 0);
×
143
    }
144
    return digital_data;
×
145
}
×
146

147
inline std::vector<float> extractEvents(std::vector<int> const & digital_data, std::string const & transition) {
×
148

149
    // Check if transition is valid
150
    if (transition != "rising" && transition != "falling") {
×
151
        throw std::invalid_argument("Invalid transition type");
×
152
    }
153

154
    std::cout << "Checking " << digital_data.size() << " timestamps for events" << std::endl;
×
155
    std::vector<float> events;
×
156
    for (std::size_t i = 1; i < digital_data.size(); i++) {
×
157
        if (
×
158
                ((digital_data[i] == 1) && (digital_data[i - 1] == 0) && (transition == "rising")) ||
×
159
                ((digital_data[i] == 0) && (digital_data[i - 1] == 1) && (transition == "falling"))) {
×
160
            events.push_back(static_cast<float>(i));
×
161
        }
162
    }
163
    return events;
×
164
}
×
165

166
inline std::vector<std::pair<float, float>> extractIntervals(std::vector<int> const & digital_data, std::string const & transition) {
×
167

168
    // Check if transition is valid
169
    if (transition != "rising" && transition != "falling") {
×
170
        throw std::invalid_argument("Invalid transition type");
×
171
    }
172

173
    std::string const & start_transition = transition;
×
174
    std::string const end_transition = transition == "rising" ? "falling" : "rising";
×
175

176
    bool in_interval = false;
×
177
    float start_time = 0;
×
178

179
    std::cout << "Checking " << digital_data.size() << " timestamps for intervals" << std::endl;
×
180
    std::vector<std::pair<float, float>> intervals;
×
181

182

183
    for (std::size_t i = 1; i < digital_data.size(); i++) {
×
184
        if (!in_interval &&
×
185
            ((start_transition == "rising" && digital_data[i] == 1 && digital_data[i - 1] == 0) ||
×
186
             (start_transition == "falling" && digital_data[i] == 0 && digital_data[i - 1] == 1))) {
×
187

188
            start_time = static_cast<float>(i);
×
189
            in_interval = true;
×
190

191
        } else if (in_interval &&
×
192
                   ((end_transition == "rising" && digital_data[i] == 1 && digital_data[i - 1] == 0) ||
×
193
                    (end_transition == "falling" && digital_data[i] == 0 && digital_data[i - 1] == 1))) {
×
194

195
            intervals.emplace_back(start_time, i);
×
196
            in_interval = false;
×
197
        }
198
    }
199

200
    return intervals;
×
201
}
×
202

203
}// namespace Loader
204

205
#endif//BINARY_LOADERS_HPP
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc