Skip to content

Commit b0f1967

Browse files
authored
Merge pull request #20 from harp-tech/data-interface
Simplify wording and structure of logging article
2 parents 4c338eb + afac722 commit b0f1967

File tree

3 files changed

+19
-74
lines changed

3 files changed

+19
-74
lines changed

articles/logging.md

Lines changed: 13 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,35 @@
11
# Logging
22

3-
As any other device that is used to record and control an experiment, data streams from Harp devices can also be easily logged in real time. In this case, the decision as to what to log is somewhat easy. Since all the communication between the peripheral and the host is made via a sequence of [`HarpMessage`](xref:Bonsai.Harp.HarpMessage) objects, this is the only piece of information we need to reconstruct the state of the experiment (as seen by the Harp device at least) at any given point in time.
3+
Data streams from Harp devices can be easily logged in real time. Since all communication between a device and the host is made via a sequence of [`HarpMessage`](xref:Bonsai.Harp.HarpMessage) objects, this is the only piece of information we need to reconstruct the state of the experiment, as seen by the Harp device, at any given point in time.
44

5-
Moreover, since all Harp messages follow a simple binary protocol, they can be efficiently (both in time and space) logged into disk using a simple flat binary file. The next sections will cover how to do this and discuss current recommendations for logging data streams from Harp devices.
5+
Moreover, since all Harp messages follow a simple [binary protocol](../protocol/BinaryProtocol-8bit.md), they can be efficiently logged into disk using a flat binary file. The next sections will cover how to do this and discuss current recommendations for logging data streams from Harp devices.
66

7-
## MessageWriter
7+
## Log to a single binary file
88

9-
Since [Harp](https://harp-tech.org/About/How-HARP-works/binary_protocol.html) is a binary protocol, any `HarpMessage` can be logged by simply saving its raw binary representation. The binary representation (as a `byte[]`) can be accessed via the [`MessageBytes`](xref:Bonsai.Harp.HarpMessage.MessageBytes) property. This means we could record the entire raw binary stream by feeding the sequence of message bytes to a binary logger.
9+
Any `HarpMessage` can be logged by simply saving its raw binary representation. This raw binary representation can be accessed as a `byte[]` via the [`MessageBytes`](xref:Bonsai.Harp.HarpMessage.MessageBytes) property. This means we can record the entire raw binary stream by simply passing the sequence of message bytes to a binary logger.
1010

1111
The `Bonsai.Harp` package provides a dedicated [`MessageWriter`](xref:Bonsai.Harp.MessageWriter) operator that easily encapsulates this functionality:
1212

1313
:::workflow
1414
![LogAllMessages](~/workflows/log-all-messages.bonsai)
1515
:::
1616

17-
Since all logging takes place on top of any `HarpMessage` stream, the writers can also be used to log multiple devices in parallel, log filtered streams (e.g. after applying [`FilterRegister`](xref:Bonsai.Harp.FilterRegister)) or even save host-generated commands (e.g. messages generated by a [`CreateMessage`](xref:Bonsai.Harp.CreateMessage) operator).
17+
Since all logging takes place on top of arbitrary `HarpMessage` streams, the `MessageWriter` operator can be used to log multiple devices in parallel, log message streams filtered using the [`FilterRegister`](xref:Bonsai.Harp.FilterRegister) operator, or even save host-generated commands such as messages generated by the [`CreateMessage`](xref:Bonsai.Harp.CreateMessage) operator.
1818

19-
## GroupByRegister
19+
> [!Warning]
20+
> While logging all Harp messages to a single flat binary file is very easy, it is not always the most convenient way to log data for post-processing and analysis, as it requires examining the header of each and every message to determine how to process its contents.
2021
21-
While logging all Harp messages to a single binary is very easy, it is not always the most convenient way to log data. For instance, if one is interested in logging only a subset of messages (e.g. only the `ADC` messages), then the previous approach would require a post-processing step to filter out the messages of interest.
22+
## Log to separate files per register
2223

23-
Furthermore, each address has potentially different data formats (e.g. `U8` vs `U16`) or even different lengths if array registers are involved. This can make it very tedious to parse and analyze a binary file offline, since we will have to examine the header of each and every message in the file to determine how to extract its contents.
24+
We can use the [`GroupByRegister`](xref:Bonsai.Harp.GroupByRegister) operator to automatically split the single Harp message stream into independent sub-streams for each device register address. When `MessageWriter` receives such a split message sequence, it will automatically generate one independent file for each register.
2425

25-
This analysis could be entirely eliminated if we knew that all messages in the binary file had the same format. For any Harp device, the payload stored in a specific register will have a fixed type and length. This means that to ensure our simplifying assumption it is enough to save each message from a specific register into a different file.
26-
27-
The [`GroupByRegister`](xref:Bonsai.Harp.GroupByRegister) operator is designed to automatically split the single Harp message stream into independent sub-streams for each device register address. There are many applications of this advanced operator, but the most common one is to demultiplex register messages before applying the `MessageWriter` operator. If `MessageWriter` receives a grouped message sequence, it will automatically generate one independent file for each register.
28-
29-
If we do this, we can ensure that all messages on each single file will have the same format and length and can thus be read and parsed in a single bulk operation. A simple implementation of this pattern is shown below:
26+
Since the payload stored in any single register always has the same format and size, this is enough to ensure we can read and parse the entire single-register binary file in one bulk operation. A simple implementation of this pattern is shown below:
3027

3128
:::workflow
3229
![LogDemux](~/workflows/log-demux.bonsai)
3330
:::
3431

35-
The single-register log files can then be loaded using the following Python routine:
36-
37-
```python
38-
import numpy as np
39-
import pandas as pd
40-
41-
_SECONDS_PER_TICK = 32e-6
42-
_payloadtypes = {
43-
1 : np.dtype(np.uint8),
44-
2 : np.dtype(np.uint16),
45-
4 : np.dtype(np.uint32),
46-
8 : np.dtype(np.uint64),
47-
129 : np.dtype(np.int8),
48-
130 : np.dtype(np.int16),
49-
132 : np.dtype(np.int32),
50-
136 : np.dtype(np.int64),
51-
68 : np.dtype(np.float32)
52-
}
53-
54-
def read_harp_bin(file):
55-
56-
data = np.fromfile(file, dtype=np.uint8)
57-
58-
if len(data) == 0:
59-
return None
60-
61-
stride = data[1] + 2
62-
length = len(data) // stride
63-
payloadsize = stride - 12
64-
payloadtype = _payloadtypes[data[4] & ~0x10]
65-
elementsize = payloadtype.itemsize
66-
payloadshape = (length, payloadsize // elementsize)
67-
seconds = np.ndarray(length, dtype=np.uint32, buffer=data, offset=5, strides=stride)
68-
ticks = np.ndarray(length, dtype=np.uint16, buffer=data, offset=9, strides=stride)
69-
seconds = ticks * _SECONDS_PER_TICK + seconds
70-
payload = np.ndarray(
71-
payloadshape,
72-
dtype=payloadtype,
73-
buffer=data, offset=11,
74-
strides=(stride, elementsize))
75-
76-
if payload.shape[1] == 1:
77-
ret_pd = pd.DataFrame(payload, index=seconds, columns= ["Value"])
78-
ret_pd.index.names = ['Seconds']
79-
80-
else:
81-
ret_pd = pd.DataFrame(payload, index=seconds)
82-
ret_pd.index.names = ['Seconds']
32+
These single-register log files can then be loaded using the [Data Interface](python.md). The `device.yml` metadata file for each device is always available in the root folder of its source repository.
8333

84-
return ret_pd
85-
```
34+
> [!Tip]
35+
> The `device.yml` metadata file for each device can also be recovered at runtime using the `GetMetadata` operator included with most device packages and saved using the [`WriteAllText`](xref:Bonsai.IO.WriteAllText) operator.

articles/toc.yml

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
- name: Get started
2-
- name: Basic Concepts
3-
href: about.md
4-
- name: Quick Start
5-
href: ../index.md
2+
- href: about.md
3+
- href: ../index.md
64
- name: Protocol
75
- name: Binary Protocol
86
href: ../protocol/BinaryProtocol-8bit.md
@@ -11,12 +9,9 @@
119
- name: Synchronization Clock
1210
href: ../protocol/SynchronizationClock.md
1311
- name: Control Interface
14-
- name: Operators
15-
href: operators.md
16-
- name: Firmware
17-
href: firmware.md
18-
- name: Logging
19-
href: logging.md
12+
- href: operators.md
13+
- href: firmware.md
14+
- href: logging.md
2015
- name: Message Manipulation
2116
href: message-manipulation.md
2217
- name: Data Interface

index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ All [Harp Devices](./protocol/whoami.md) implement the [Harp Protocol](./protoco
1818

1919
A high-level interface will usually be available for the specific Harp device you are using. To install them, first change the package manager **Package source** to `nuget.org`. Then, in the search bar, look for your device by typing: `harp.<device>`. For instance, for the [Harp Behavior](xref:Harp.Behavior) board, you should find the following package:
2020

21-
<img alt="Installing a Harp device package" src="~/images/behavior-package.png" style="max-height:450px;object-fit:contain" />
21+
<p><img alt="Installing a Harp device package" src="~/images/behavior-package.png" style="max-height:450px;object-fit:contain" /></p>
2222

2323
The device nodes should now be available in the Bonsai Toolbox and you can start using them in your workflows. See [Operators](./articles/operators.md) for examples of how to manipulate and control Harp devices.
2424

0 commit comments

Comments
 (0)