Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
wuwbobo2021 authored Oct 19, 2022
1 parent 114b5d3 commit 7754c10
Show file tree
Hide file tree
Showing 12 changed files with 1,757 additions and 384 deletions.
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,19 @@ MKDIR = mkdir
CP = copy
RM = del /Q
RMDIR = rmdir /S /Q
CPPFLAGS += -mwindows
run_demo = $(target_demo)
else
MKDIR = mkdir -p
CP = cp
RM = rm -f
RMDIR = rm -f -r
run_demo = ./$(target_demo)
endif

cpp_options = -I$(includedir) `pkg-config gtkmm-3.0 --cflags --libs` -latomic $(CPPFLAGS)
headers = $(foreach h, $(wildcard *.h), $(includedir_subdir)/$(h))
objects = circularbuffer.o plottingarea.o recorder.o frontend.o
objects = circularbuffer.o plotarea.o recorder.o frontend.o

$(target): $(headers) $(objects) $(libdir)
$(AR) rcs $@ $(objects)
Expand All @@ -46,9 +49,9 @@ $(includedir_subdir)/%.h: $(includedir_subdir) %.h
$(CP) $(@F) $<

demo: $(target_demo)
./$<
$(run_demo)

.PHONY: clean
clean:
-$(RMDIR) lib include
-$(RM) *.o target_demo
-$(RM) *.o $(target_demo)
39 changes: 27 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ make demo
```
You can modify `demo.cpp` to change the wave form and make other adjustments, like speed, buffer size or axis-y range.

Install:
## Install
```
sudo make -e prefix=/usr
```
Expand All @@ -22,25 +22,40 @@ For 32-bit Windows:
2. Download `msys2-i686-latest.sfx.exe`, place it under a short path and extract it;
3. Follow the instruments in <https://wiki.gnome.org/Projects/gtkmm/MSWindows>.

Add `<msys32>/bin` to environment variable `PATH`, then MSYS2-compiled programs can be executed outside MSYS2 shell. Add linker flag `-mwindows` to hide the console window.

## Classes
`AxisRange`: Closed range between two `float` values. It supports many operations, including mapping of a given value to another range. All of it's functions are inlined.
### ValueRange, IndexRange
Closed range between two values. It supports many operations, including mapping of a given value to another range. All of it's functions are inlined. `ValueRange` is implemented by two `float` variables, while `IndexRange` is implemented by two `unsigned long int` variables. Implicit conversions between them are supported.

### CircularBuffer
Where the data should be pushed back to update the graph in `PlottingArea`. After it becomes full, it discards an item each time a new item is pushed into, but it avoids moving every item in the memory region. It's functions are thread-safe, and most of its simple functions are inlined.

Optimized algorithms calculating min/max/average values are implemented here, and spike detection is enabled by default so that spikes can be treated specially to avoid flickering of spikes when the x-axis index step for data plotting is adjusted for a wide index range.

`CircularBuffer`: Where the data should be pushed back to update the graph in `PlottingArea`. After it becomes full, it discards an item each time a new item is pushed into, but it avoids moving every item in the memory region. Optimized algorithms calculating min/max/average values are implemented here, and spike detection is enabled by default so that spikes can be treated specially to avoid flickering of spikes when the x-axis index step for data plotting is adjusted for a wide index range. It is thread-safe, and most of its simple functions are inlined.
### PlotArea
Implements a graph box for a single buffer without scroll box. It only supports a single variable, and values should be pushed into its buffer manually. Axis ranges can be set either automatically or manually, and the grid with tick values can be either fixed or auto-adjusted. For x-axis index range, goto-end mode and extend mode are available.

`PlottingArea`: Implements a graph box for a single buffer without scroll box. It only supports a single variable, and values should be pushed into its buffer manually. Axis ranges can be set either automatically or manually, and the grid with tick values can be either fixed or auto-adjusted. For x-axis index range, goto-end mode and extend mode are available.
Notice: `PlotArea` cannot receive button press event and button release event by itself. If needed, put it inside a `Gtk::EventBox` which handles these events.

`VariableAccessPtr`: Pointer of an variable or a function which has a `void*` parameter and returns a `float` value. Pointer of a member function of class `T` which returns a `float` value and has no extra parameters can be created by:
### VariableAccessPtr
Pointer of an variable or a function which has a `void*` parameter and returns a `float` value. Pointer of a member function of class `T` which returns a `float` value and has no extra parameters can be created by:
```
MemberFuncPtr<typename T, float (T::*F)()>(T* pobj)
```

`Recorder`: Packs multiple plotting areas and a scroll box for x-axis. It accepts a group of `VariableAccessPtr` pointers from which the data is read, then creates multiple buffers multiple plotting areas for these variables. After it is started, it reads and pushs the data into the buffers in given interval, and the unit of axis-x values is set to seconds. Provides zoom in/out (by left/right mouse button clicking on it) and CSV file opening/saving features.
### Recorder
Packs multiple plotting areas and a scroll box for x-axis. It accepts a group of `VariableAccessPtr` pointers from which the data is read, then creates multiple buffers multiple plotting areas for these variables. After it is started, it reads and pushs the data into the buffers in given interval, and the unit of axis-x values is set to seconds. It provides zoom in/out (by left/right mouse button clicking on it) and CSV file opening/saving features.

`Recorder` is not capable of loading a block of data at once. In this case, it can still be used to show data (alias `RecordView` can be used for this purpose). Do not call `Recorder::start()`, but load data into each buffer manually, then call `Recorder::refresh_view()`. Call `Recorder::clear()` to clear them. In case of the amount of data in the buffers are not equal, that of the buffer for the first variable makes sense. To avoid writing invalid non-zero data into the CSV file, call `CircularBuffer::erase()` for each buffer after calling `Recorder::clear()`.

### Frontend
Provides a simplest interface to create a Gtk application and a window for the recorder. Call its member function `open()` to create a new thread for Gtk, then it will run until it is destructed or its member function `close()` is called; call `run()` to join the Gtk thread, then it will run until the window is closed.

`Frontend`: Provides a simple interface to create a Gtk application and a window for the recorder. It runs in a seperate thread for Gtk, until it is destructed or its member function `close()` is being called. Call its member function `run()` to join the Gtk thread, then it will run until the window is closed. Notice: Through function `recorder()` you can get a reference of the `Recorder` object as a Gtk Widget after the window is opened, but the object itself will be destructed when the window is being closed.
Notice:
1. Another `Gtk::Application` cannot be created after the `Frontend` has opened, and running multiple `Frontend` is not possible.
2. Through function `recorder()` you can get a reference of the `Recorder` object as a Gtk widget after the window is opened, but the object itself will be destructed when the window is being closed.

## Known Issues
1. On Windows, segmentation fault will be produced when the thread created by `Frontend` exits. Use `Frontend::run()` instead of `Frontend::open()` (which creates a new thread for `Gtk::Application::run()`) on Windows, especially if you need to do necessary things after the window is closed by the user.
2. Fast refresh rate (25~50 Hz for example) of the graph may cause heavy CPU load (at least on a computer without GPU): it seems that processing of signal `draw` costs CPU even if nothing is to be drawn in `on_draw()`, and it becomes serious when the graph area is expanded (with window maximized). Keeping a Cairo context in `PlottingArea` instead of emitting the signal `draw` which creates and provides the Cairo context for drawing might work, but is probably error-prone because that violates the GTK drawing model, and `Gdk::Window::create_cairo_context()` is deprecated long ago.
3. The record process can be interrupted by the environment, this causes missing of data and unsmooth curves on the graph. It works very well on XFCE, and is acceptable on GNOME and KDE, but the unsmooth effect can be significant on Windows that the delay can sometimes exceed 20 ms. Limited by software timer accuracy, it is IMPOSSIBLE for `Recorder` to keep its data sampling frequency higher than 10 kHz (0.1 ms interval). The higher the frequency, the lower the stability.
4. `Recorder` is not capable of loading a block of data at once. In this case, it can still be used to show data: do not call `Recorder::start()`, but load data into each buffer manually, then call `Recorder::refresh_view()` to refresh the graph. In case of the amount of data in the buffers are not equal, that of the buffer for the first variable makes sense. To avoid writing invalid non-zero data into the CSV file, call `CircularBuffer::erase()` for each buffer after calling `Recorder::clear()`.
5. It has not been migrated to `gtkmm-4.0`, partly because newest distributions of Debian and Ubuntu has not provided this version of the library.
1. The record process of `Recorder` can be interrupted by the environment, this causes missing of data and unsmooth curves on the graph. It works well on XFCE, and is acceptable on GNOME and KDE, but the unsmooth effect can be significant on Windows that the delay can sometimes exceed 20 ms. Limited by software timer accuracy, it is IMPOSSIBLE for `Recorder` to keep its data sampling frequency higher than 10 kHz (0.1 ms interval). The higher the frequency, the lower the stability.
2. It has not been migrated to `gtkmm-4.0`, partly because newest distributions of Debian and Ubuntu has not provided this version of the library.
Loading

0 comments on commit 7754c10

Please sign in to comment.