Skip to content

Commit a99646e

Browse files
committed
Improve examples, fix typos
1 parent cd77d41 commit a99646e

File tree

8 files changed

+93
-44
lines changed

8 files changed

+93
-44
lines changed

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ addlslexample(ReceiveDataInChunks cpp)
3636
addlslexample(ReceiveDataSimple cpp)
3737
addlslexample(ReceiveStringMarkers cpp)
3838
addlslexample(ReceiveStringMarkersC c)
39+
addlslexample(SendData cpp)
3940
addlslexample(SendDataC c)
4041
addlslexample(SendDataInChunks cpp)
4142
addlslexample(SendDataSimple cpp)

examples/ReceiveDataC.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,28 @@
55
* Example program that demonstrates how to resolve a specific stream on the lab network and how to
66
* connect to it in order to receive data.
77
*/
8+
#define NCHANS 8
9+
810

911
int main(int argc, char *argv[]) {
1012

1113
unsigned k, t; /* channel index */
1214
lsl_streaminfo info; /* the streaminfo returned by the resolve call */
1315
lsl_inlet inlet; /* a stream inlet to get samples from */
1416
int errcode; /* error code (lsl_lost_error or timeouts) */
15-
float cursample[8]; /* array to hold our current sample */
17+
float cursample[NCHANS]; /* array to hold our current sample */
1618
double timestamp; /* time stamp of the current sample (in sender time) */
1719

1820
/* resolve the stream of interest (result array: info, array capacity: 1 element, type shall be
1921
* EEG, resolve at least 1 stream, wait forever if necessary) */
2022
printf("Now waiting for an EEG stream...\n");
2123
lsl_resolve_byprop(&info, 1, "type", "EEG", 1, LSL_FOREVER);
2224

25+
/* These next two variables aren't used for anything in this example.
26+
* They simply demonstrate how to use streaminfo getters. */
27+
lsl_channel_format_t fmt = lsl_get_channel_format(info);
28+
double srate = lsl_get_nominal_srate(info);
29+
2330
/* make an inlet to read data from the stream (buffer max. 300 seconds of data, no preference
2431
* regarding chunking, automatic recovery enabled) */
2532
inlet = lsl_create_inlet(info, 300, LSL_NO_PREFERENCE, 1);
@@ -33,9 +40,10 @@ int main(int argc, char *argv[]) {
3340
for (t = 0; t < 100000000; t++) {
3441
/* get the next sample form the inlet (read into cursample, 8 values, wait forever if
3542
* necessary) and return the timestamp if we got something */
36-
timestamp = lsl_pull_sample_f(inlet, cursample, 8, LSL_FOREVER, &errcode);
43+
timestamp = lsl_pull_sample_f(inlet, cursample, NCHANS, LSL_FOREVER, &errcode);
3744

3845
/* print the data */
46+
printf("%.2f", timestamp);
3947
for (k = 0; k < 8; ++k) printf("\t%.2f", cursample[k]);
4048
printf("\n");
4149
}

examples/ReceiveDataInChunks.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,21 @@
88
int main(int argc, char **argv) {
99
std::cout << "ReceiveDataInChunks" << std::endl;
1010
std::cout << "ReceiveDataInChunks StreamName max_buflen flush" << std::endl;
11+
std::cout << "- max_buffered -- duration in sec (or x100 samples if samplerate is 0) to buffer "
12+
"in the receiver"
13+
<< std::endl;
14+
std::cout
15+
<< "- flush -- set non-zero to flush data instead of pulling; useful for testing throughput"
16+
<< std::endl;
1117

1218
try {
1319

1420
std::string name{argc > 1 ? argv[1] : "MyAudioStream"};
15-
int32_t max_buflen = argc > 2 ? std::stol(argv[2]) : 360;
21+
double max_buffered = argc > 2 ? std::stod(argv[2]) : 360.;
1622
bool flush = argc > 3;
1723
// resolve the stream of interest & make an inlet
18-
lsl::stream_inlet inlet(lsl::resolve_stream("name", name).at(0), max_buflen);
24+
lsl::stream_info inlet_info = lsl::resolve_stream("name", name).at(0);
25+
lsl::stream_inlet inlet(inlet_info, max_buffered);
1926

2027
// Use set_postprocessing to get the timestamps in a common base clock.
2128
// Do not use if this application will record timestamps to disk -- it is better to

examples/ReceiveDataSimple.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <iostream>
12
#include <lsl_cpp.h>
23
#include <vector>
34

@@ -16,7 +17,10 @@ int main(int argc, char **argv) {
1617

1718
// receive data & time stamps forever (not displaying them here)
1819
std::vector<float> sample;
19-
while (true) inlet.pull_sample(sample);
20+
while (true) {
21+
double timestamp = inlet.pull_sample(sample);
22+
std::cout << timestamp << "\t" << sample[0] << "\t" << sample[1] << "..." << std::endl;
23+
}
2024

2125
return 0;
2226
}

examples/SendData.cpp

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#include "lsl_cpp.h"
2+
#include <array>
3+
#include <chrono>
24
#include <iostream>
35
#include <stdlib.h>
4-
#include <time.h>
5-
using namespace std;
6+
#include <thread>
67

78
/**
89
* This example program offers an 8-channel stream, float-formatted, that resembles EEG data.
@@ -16,54 +17,73 @@ using namespace std;
1617
const char *channels[] = {"C3", "C4", "Cz", "FPz", "POz", "CPz", "O1", "O2"};
1718

1819
int main(int argc, char *argv[]) {
19-
string name, type;
20-
if (argc != 3) {
21-
cout << "This opens a stream under some user-defined name and with a user-defined content "
22-
"type."
23-
<< endl;
24-
cout << "Please enter the stream name and the stream type (e.g. \"BioSemi EEG\" (without "
25-
"the quotes)):"
26-
<< endl;
27-
cin >> name >> type;
20+
std::string name, type;
21+
if (argc < 3) {
22+
std::cout
23+
<< "This opens a stream under some user-defined name and with a user-defined content "
24+
"type."
25+
<< std::endl;
26+
std::cout << "SendData Name Type [n_channels=8] [srate=100] [max_buffered=360]"
27+
<< std::endl;
28+
std::cout
29+
<< "Please enter the stream name and the stream type (e.g. \"BioSemi EEG\" (without "
30+
"the quotes)):"
31+
<< std::endl;
32+
std::cin >> name >> type;
2833
} else {
2934
name = argv[1];
3035
type = argv[2];
3136
}
37+
int n_channels = argc > 3 ? std::stol(argv[3]) : 8;
38+
n_channels = n_channels < 8 ? 8 : n_channels;
39+
int samplingrate = argc > 4 ? std::stol(argv[4]) : 100;
40+
int max_buffered = argc > 5 ? std::stol(argv[5]) : 360;
3241

3342
try {
3443

3544
// make a new stream_info (100 Hz)
36-
lsl::stream_info info(name, type, 8, 100, lsl::cf_float32, string(name) += type);
45+
lsl::stream_info info(
46+
name, type, n_channels, samplingrate, lsl::cf_float32, std::string(name) += type);
3747

3848
// add some description fields
39-
info.desc().append_child_value("manufacturer", "BioSemi");
49+
info.desc().append_child_value("manufacturer", "LSL");
4050
lsl::xml_element chns = info.desc().append_child("channels");
41-
for (int k = 0; k < 8; k++)
51+
for (int k = 0; k < n_channels; k++)
4252
chns.append_child("channel")
43-
.append_child_value("label", channels[k])
53+
.append_child_value("label", k < 8 ? channels[k] : "Chan-" + std::to_string(k + 1))
4454
.append_child_value("unit", "microvolts")
45-
.append_child_value("type", "EEG");
55+
.append_child_value("type", type);
4656

4757
// make a new outlet
48-
lsl::stream_outlet outlet(info);
58+
lsl::stream_outlet outlet(info, 0, max_buffered);
59+
std::vector<float> sample(n_channels, 0.0);
60+
61+
// Your device might have its own timer. Or you can decide how often to poll
62+
// your device, as we do here.
63+
int32_t sample_dur_us = 1000000 / (samplingrate > 0 ? samplingrate : 100);
64+
auto t_start = std::chrono::high_resolution_clock::now();
65+
auto next_sample_time = t_start;
4966

5067
// send data forever
51-
cout << "Now sending data... " << endl;
68+
std::cout << "Now sending data... " << std::endl;
5269
double starttime = ((double)clock()) / CLOCKS_PER_SEC;
5370
for (unsigned t = 0;; t++) {
54-
55-
// wait a bit and create random data
56-
while (((double)clock()) / CLOCKS_PER_SEC < starttime + t * 0.01)
57-
;
58-
float sample[8];
71+
// Create random data for the first 8 channels.
5972
for (int c = 0; c < 8; c++) sample[c] = (float)((rand() % 1500) / 500.0 - 1.5);
73+
// For the remaining channels, fill them with a sample counter (wraps at 1M).
74+
std::fill(sample.begin() + 8, sample.end(), t % 1000000);
75+
76+
// Wait until the next expected sample time.
77+
next_sample_time += std::chrono::microseconds(sample_dur_us);
78+
std::this_thread::sleep_until(next_sample_time);
6079

6180
// send the sample
81+
std::cout << sample[0] << "\t" << sample[8] << std::endl;
6282
outlet.push_sample(sample);
6383
}
6484

65-
} catch (std::exception &e) { cerr << "Got an exception: " << e.what() << endl; }
66-
cout << "Press any key to exit. " << endl;
67-
cin.get();
85+
} catch (std::exception &e) { std::cerr << "Got an exception: " << e.what() << std::endl; }
86+
std::cout << "Press any key to exit. " << std::endl;
87+
std::cin.get();
6888
return 0;
6989
}

examples/SendDataInChunks.cpp

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ struct fake_device {
4444
pattern.reserve(pattern_samples * n_channels);
4545
for (auto sample_ix = 0; sample_ix < pattern_samples; ++sample_ix) {
4646
for (auto chan_ix = 0; chan_ix < n_channels; ++chan_ix) {
47+
// sin(2*pi*f*t), where f cycles from 1 Hz to Nyquist: srate / 2
48+
double f = (chan_ix + 1) % (int)(srate / 2);
4749
pattern.emplace_back(
4850
offset_0 + chan_ix * offset_step +
49-
magnitude * static_cast<int16_t>(sin(M_PI * chan_ix * sample_ix / n_channels)));
51+
magnitude * static_cast<int16_t>(sin(2 * M_PI * f * sample_ix / srate)));
5052
}
5153
}
5254
last_time = std::chrono::steady_clock::now();
@@ -64,15 +66,15 @@ struct fake_device {
6466
return output;
6567
}
6668

67-
std::size_t get_data(std::vector<int16_t> &buffer) {
69+
std::size_t get_data(std::vector<int16_t> &buffer, bool nodata = false) {
6870
auto now = std::chrono::steady_clock::now();
6971
auto elapsed_nano =
7072
std::chrono::duration_cast<std::chrono::nanoseconds>(now - last_time).count();
7173
int64_t elapsed_samples = std::size_t(elapsed_nano * srate * 1e-9); // truncate OK.
7274
elapsed_samples = std::min(elapsed_samples, (int64_t)(buffer.size() / n_channels));
73-
if (false) {
75+
if (nodata) {
7476
// The fastest but no patterns.
75-
memset(&buffer[0], 23, buffer.size() * sizeof buffer[0]);
77+
// memset(&buffer[0], 23, buffer.size() * sizeof buffer[0]);
7678
} else {
7779
std::size_t end_sample = head + elapsed_samples;
7880
std::size_t nowrap_samples = std::min(pattern_samples - head, elapsed_samples);
@@ -96,24 +98,28 @@ int main(int argc, char **argv) {
9698
std::string name{argc > 1 ? argv[1] : "MyAudioStream"}, type{argc > 2 ? argv[2] : "Audio"};
9799
int samplingrate = argc > 3 ? std::stol(argv[3]) : 44100; // Here we specify srate, but typically this would come from the device.
98100
int n_channels = argc > 4 ? std::stol(argv[4]) : 2; // Here we specify n_chans, but typically this would come from theh device.
99-
int32_t max_buffered = argc > 5 ? std::stol(argv[5]) : 360;
101+
double max_buffered = argc > 5 ? std::stod(argv[5]) : 360.;
100102
int32_t chunk_rate = argc > 6 ? std::stol(argv[6]) : 10; // Chunks per second.
101103
int32_t chunk_samples = samplingrate > 0 ? std::max((samplingrate / chunk_rate), 1) : 100; // Samples per chunk.
102104
int32_t chunk_duration = 1000 / chunk_rate; // Milliseconds per chunk
103105

104106
try {
105107
// Prepare the LSL stream.
106-
lsl::stream_info info(name, type, n_channels, samplingrate, lsl::cf_int16);
107-
lsl::stream_outlet outlet(info, 0, max_buffered);
108+
lsl::stream_info info(
109+
name, type, n_channels, samplingrate, lsl::cf_int16, "example-SendDataInChunks");
108110
lsl::xml_element desc = info.desc();
109111
desc.append_child_value("manufacturer", "LSL");
110112
lsl::xml_element chns = desc.append_child("channels");
111113
for (int c = 0; c < n_channels; c++) {
112114
lsl::xml_element chn = chns.append_child("channel");
113115
chn.append_child_value("label", "Chan-" + std::to_string(c));
114116
chn.append_child_value("unit", "microvolts");
115-
chn.append_child_value("type", "EEG");
117+
chn.append_child_value("type", type);
116118
}
119+
int32_t buf_samples = max_buffered * samplingrate;
120+
lsl::stream_outlet outlet(info, chunk_samples, buf_samples);
121+
info = outlet.info(); // Refresh info with whatever the outlet captured.
122+
std::cout << "Stream UID: " << info.uid() << std::endl;
117123

118124
// Create a connection to our device.
119125
fake_device my_device(n_channels, (float)samplingrate);
@@ -126,7 +132,8 @@ int main(int argc, char **argv) {
126132

127133
// Your device might have its own timer. Or you can decide how often to poll
128134
// your device, as we do here.
129-
auto next_chunk_time = std::chrono::high_resolution_clock::now();
135+
auto t_start = std::chrono::high_resolution_clock::now();
136+
auto next_chunk_time = t_start;
130137
for (unsigned c = 0;; c++) {
131138
// wait a bit
132139
next_chunk_time += std::chrono::milliseconds(chunk_duration);
@@ -137,9 +144,10 @@ int main(int argc, char **argv) {
137144

138145
// send it to the outlet. push_chunk_multiplexed is one of the more complicated approaches.
139146
// other push_chunk methods are easier but slightly slower.
140-
outlet.push_chunk_multiplexed(chunk_buffer.data(), returned_samples * n_channels, 0.0, true);
147+
double ts = lsl::local_clock();
148+
outlet.push_chunk_multiplexed(
149+
chunk_buffer.data(), returned_samples * n_channels, ts, true);
141150
}
142-
143151
} catch (std::exception &e) { std::cerr << "Got an exception: " << e.what() << std::endl; }
144152
std::cout << "Press any key to exit. " << std::endl;
145153
std::cin.get();

include/lsl_cpp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ class stream_outlet {
499499
}
500500

501501
/** Push a pointer to raw numeric data as one sample into the outlet.
502-
* This is the lowest-level function; performns no checking whatsoever. Can not be used for
502+
* This is the lowest-level function; performs no checking whatsoever. Cannot be used for
503503
* variable-size / string-formatted channels.
504504
* @param sample A pointer to the raw sample data to push.
505505
* @param timestamp Optionally the capture time of the sample, in agreement with local_clock();

src/tcp_server.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,13 @@ class tcp_server : public std::enable_shared_from_this<tcp_server> {
6363
* Initiate teardown of IO processes.
6464
*
6565
* The actual teardown will be performed by the IO thread that runs the operations of
66-
* thisserver.
66+
* this server.
6767
*/
6868
void end_serving();
6969

7070
private:
7171
friend class client_session;
72+
7273
/// Start accepting a new connection.
7374
void accept_next_connection();
7475

0 commit comments

Comments
 (0)