-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathImpedanceMeasurement.h
More file actions
168 lines (128 loc) · 6.02 KB
/
ImpedanceMeasurement.h
File metadata and controls
168 lines (128 loc) · 6.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#ifndef EGIAMP_IMPEDANCEMEASUREMENT_H
#define EGIAMP_IMPEDANCEMEASUREMENT_H
#include "AmpServerConnection.h"
#include "AmpServerProtocol.h"
#include "LSLStreamer.h"
#include "SensorLayout.h"
#include <atomic>
#include <chrono>
#include <deque>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
namespace egiamp {
// Callback types
using ImpedanceCallback = std::function<void(int channel, float impedanceKOhms)>;
using ImpedanceStatusCallback = std::function<void(const std::string&)>;
// Timing parameters for impedance measurement
// Updated automatically by setSampleRate().
struct ImpedanceTiming {
double commandTime = 0.03; // Time for command to travel to amp (seconds)
double settleTime = 0.6; // Time before measuring (seconds)
double filterTime = 0.5; // Measurement/collection duration (seconds)
int peakToPeakSampleCount = 100; // Samples to use for peak-to-peak calculation
};
// Result for a single channel measurement
struct ChannelImpedance {
int channel; // 0-based channel index
float impedanceKOhms; // Calculated impedance in kilo-ohms
float amplitude; // Measured peak-to-peak amplitude
bool valid; // Whether measurement was successful
};
class ImpedanceMeasurement {
public:
ImpedanceMeasurement(AmpServerConnection& connection, int amplifierId);
~ImpedanceMeasurement();
ImpedanceMeasurement(const ImpedanceMeasurement&) = delete;
ImpedanceMeasurement& operator=(const ImpedanceMeasurement&) = delete;
// Configuration
void setTiming(const ImpedanceTiming& timing) { timing_ = timing; }
void setSampleRate(int rate);
void setChannelCount(int count) { channelCount_ = count; }
void setScalingFactor(float factor) { scalingFactor_ = factor; }
void setNetSize(int netSize);
// Override the ideal signal for all channels (disables per-channel calibration)
void setIdealSignal(float idealSignal) { idealSignal_ = idealSignal; }
// Callbacks
void setStatusCallback(ImpedanceStatusCallback cb) { statusCallback_ = std::move(cb); }
void setImpedanceCallback(ImpedanceCallback cb) { impedanceCallback_ = std::move(cb); }
// Set up amplifier in impedance measurement state (initial state)
bool setupImpedanceState();
// Reset amplifier to default acquisition state
bool resetToDefaultState();
// Start continuous impedance scanning (runs in background thread)
// Cycles through all channels repeatedly until stop() is called
bool startContinuousScan(LSLStreamer& impedanceStreamer);
// Stop scanning
void stop();
// Check if currently scanning
bool isScanning() const { return scanning_; }
// Feed a sample into the measurement buffer (called from data reader thread)
void feedSample(const PacketFormat2& packet);
// Measure a single channel (blocking) - for testing/debugging
ChannelImpedance measureChannel(int channel);
// Measure reference channel
ChannelImpedance measureReference();
// Measure COM channel (NA400/NA410 only)
ChannelImpedance measureCOM();
// Measure all channels in a tiling set simultaneously
std::vector<ChannelImpedance> measureTilingSet(const TilingSet& ts);
private:
void emitStatus(const std::string& message);
bool sendCommand(const std::string& cmd, int channel, const std::string& value);
// Turn measurement on/off for a channel
// When on=false: drive signal OFF, 10K resistor ON (measuring)
// When on=true: drive signal ON, 10K resistor OFF (driving)
void setChannelDriving(int channel, bool driving);
void setReferenceDriving(bool driving);
void setCOMDriving(bool driving);
// Collect a buffer of raw packets for the measurement duration
std::deque<PacketFormat2> collectSampleBuffer();
// Extract samples for a single channel from a packet buffer
std::vector<float> extractChannelSamples(const std::deque<PacketFormat2>& packets, int channel);
// Legacy single-channel collect (used by measureChannel)
std::vector<float> collectSamples(int channel, int sampleCount);
// Calculate peak-to-peak amplitude from samples
float calculatePeakToPeak(const std::vector<float>& samples);
// Calibrate per-channel ideal signals by measuring in the all-driving state.
// Must be called after setupImpedanceState() and with data flowing.
bool calibrate();
// Calculate impedance from amplitude (uses per-channel calibrated ideal if available)
float calculateImpedance(float amplitude, int channel = -1);
// Scanning thread function
void scanThread(LSLStreamer& impedanceStreamer);
AmpServerConnection& connection_;
int amplifierId_;
ImpedanceTiming timing_;
int sampleRate_ = 1000;
int channelCount_ = 256;
float scalingFactor_ = 0.0f;
float idealSignal_ = 0.0f; // 0 = use calibrated or DEFAULT_IDEAL_SIGNAL
float meanIdealSignal_ = 0.0f; // Mean across all normal channels from calibration
// Per-channel calibrated ideal signals (measured in all-driving state).
std::vector<float> calibratedIdealSignals_;
// Net size and sensor layout for tiling-based scanning
int netSize_ = 0;
const SensorLayout* layout_ = nullptr;
// Sample buffer for collecting measurement data
std::mutex sampleMutex_;
std::deque<PacketFormat2> sampleBuffer_;
std::atomic<bool> collectingSamples_{false};
// Scanning state
std::atomic<bool> scanning_{false};
std::atomic<bool> stopFlag_{false};
std::unique_ptr<std::thread> scanThread_;
std::unique_ptr<std::thread> publishThread_;
// Current impedance values for all channels (updated as measured)
// Initialized to MAX_IMPEDANCE to indicate "not yet measured"
std::vector<float> currentImpedances_;
std::mutex impedancesMutex_;
// Publishing thread function (pushes at 1 Hz)
void publishThread(LSLStreamer& impedanceStreamer);
ImpedanceStatusCallback statusCallback_;
ImpedanceCallback impedanceCallback_;
};
} // namespace egiamp
#endif // EGIAMP_IMPEDANCEMEASUREMENT_H