Compare commits

...

76 commits

Author SHA1 Message Date
Marcello Mamino
72faef14ff Version 0.5.0 2017-06-24 16:25:44 +02:00
Marcello Mamino
dcb888c027 Instructions for fedora 2017-06-24 16:14:26 +02:00
Marcello Mamino
99e0354b3d Fix for file icon 2017-06-23 20:56:44 +02:00
Marcello Mamino
bf853a0278 Killed make_zip and Makefile.old 2017-06-23 19:39:37 +02:00
Marcello Mamino
a1000b5df9 Various c cleanups 2017-06-23 19:35:41 +02:00
Marcello Mamino
38a3bb273b Fix for -Wall -Wextra 2017-06-23 02:25:53 +02:00
Marcello Mamino
6925a043fe More tweaks to build system 2017-06-23 02:09:10 +02:00
Marcello Mamino
43539ba773 Added /deb and /msi to .gitignore 2017-06-23 01:29:48 +02:00
Marcello Mamino
0b62fc2abe Tweaks to autotools magic, deb package builds 2017-06-23 01:27:41 +02:00
Marcello Mamino
a9d29b6d34 Can create msi under w10 2017-06-21 16:11:26 -07:00
Marcello Mamino
6a75e41d87 Edited README 2017-06-20 19:54:40 +02:00
Stefan Talpalaru
ff96e9b41a
remove extra empty line 2017-06-20 03:00:02 +02:00
Stefan Talpalaru
11c00939ab
remove tg-timer-lt reference from the man page 2017-06-20 02:58:42 +02:00
Stefan Talpalaru
de3c8bb4c9
nicer windres check 2017-06-20 02:51:36 +02:00
Stefan Talpalaru
be215a39c3
"make dist" related fixes 2017-06-20 02:42:19 +02:00
Stefan Talpalaru
b7e4f5f2ef
remove the gzip-ed man page 2017-06-20 02:41:39 +02:00
Stefan Talpalaru
674ae06bdf
set the GTK window icon 2017-06-20 02:28:40 +02:00
Stefan Talpalaru
924b00f892
shorter version 2017-06-20 02:15:54 +02:00
Stefan Talpalaru
b003324772
fix desktopdir 2017-06-20 02:13:43 +02:00
Stefan Talpalaru
1c1469ba53
Autotools in the "test" branch 2017-06-20 02:00:27 +02:00
Marcello Mamino
6b6c6e9870 Updated README, version 0.4.99 2017-06-10 23:22:23 +02:00
Marcello Mamino
fe4a1ce9f4 Document icon cleanup 2017-06-10 12:39:50 +02:00
Marcello Mamino
6497c21ff8 Added workaround for XP 2017-06-10 12:03:39 +02:00
Marcello Mamino
7e6dbcc1ae Got rid of gtk file chooser native: full of bugs 2017-06-10 01:43:43 -07:00
Marcello Mamino
b89e761d0c More filename madness on XP 2017-06-09 23:55:53 +02:00
Marcello Mamino
244cf19065 Trying to fix filenames in windows 2017-06-09 14:45:47 -07:00
Marcello Mamino
fb6ddddd3a Document icon cleanup 2017-06-09 22:18:06 +02:00
Marcello Mamino
032966ef57 Fixed bug loading stock images in XP 2017-06-09 22:07:38 +02:00
Marcello Mamino
7ab8abe3f9 Cleanup in scan_snapshot() 2017-06-09 09:41:06 +02:00
Marcello Mamino
a40262cc8a Trying harder to register .tgj under windows 2017-06-08 21:53:33 +02:00
Marcello Mamino
73d9e4096f Trying to register tgj extension under windows 2017-06-08 21:23:31 +02:00
Marcello Mamino
101ff3f0e2 Fixed make test 2017-06-08 02:34:17 +02:00
Marcello Mamino
72b129c83d Open files on startup 2017-06-08 02:25:03 +02:00
Marcello Mamino
be33fb01e5 Modified file format for forward compatibility 2017-06-08 00:14:40 +02:00
Marcello Mamino
870162aec8 Got rid of separate light version 2017-06-07 23:39:21 +02:00
Marcello Mamino
cbcc8703e3 Parametrized lightness 2017-06-07 20:13:07 +02:00
Marcello Mamino
8cf2f0e84d Icon for documents 2017-06-07 16:53:14 +02:00
Marcello Mamino
31c8392d47 Cleanup to remove warning 2017-06-06 22:39:50 +02:00
Marcello Mamino
23ae6374e7 File handling on windows + cleanup 2017-06-06 13:33:56 -07:00
Marcello Mamino
db017bdfac Check for file not found, etc. 2017-06-05 14:29:51 +02:00
Marcello Mamino
27f04ade85 Merge branch 'master' into test 2017-06-05 00:51:36 +02:00
Marcello Mamino
02840e9d8a More tweaks for XP, both installers work! 2017-06-05 00:49:46 +02:00
Marcello Mamino
611e337acd Tweaks for XP 2017-06-04 23:41:54 +02:00
Marcello Mamino
9446783a5c Fix to make_msi.sh, msi installer works! 2017-06-04 01:34:55 -07:00
Marcello Mamino
2e7436b58e Msi installer: removed stock images 2017-06-04 10:20:34 +02:00
Marcello Mamino
fecae48d14 Tweaks for MSYS 2017-06-03 01:06:36 +02:00
Marcello Mamino
1bfd9977bd Fix for reading files with empty event list 2017-06-03 00:52:03 +02:00
Marcello Mamino
60f4ba9357 Fix for hexadecimal floats in windows 2017-06-01 21:11:27 +02:00
Marcello Mamino
70ccc91c9e Do not use native file chooser on GTK+ < 3.20.0 2017-06-01 01:41:25 +02:00
Marcello Mamino
f0a365f70c Populated menu, load and save dialogs 2017-06-01 01:02:40 +02:00
Marcello Mamino
ea170b1378 Cleanup in config.c 2017-05-30 19:45:41 +02:00
Marcello Mamino
5fa5489be9 Cleaning up memory management 2017-05-30 00:37:45 +02:00
Marcello Mamino
0e9a7c138f Load and Save working 2017-05-29 03:05:56 +02:00
Marcello Mamino
ec8c20a995 Still fiddling with .travis.yml 2017-05-27 20:21:32 +02:00
Marcello Mamino
2d8f7518b7 Trying to get travis to build on Ubuntu 14... 2017-05-27 20:15:12 +02:00
Marcello Mamino
d4e12190ef Fixed bug in locales with decimal separator != '.' 2017-05-27 01:40:38 +02:00
Marcello Mamino
00847ec8f3 libgtk-3-dev (>= 3.14) in deb package dependecies 2017-05-27 01:39:14 +02:00
Marcello Mamino
df66b09b09 Corrected .travis.yml and README.md for GTK+3 2017-05-26 01:26:18 +02:00
Marcello Mamino
1285c57eaf Fixed bug with make test, version 0.4.1-test 2017-05-26 01:11:41 +02:00
Marcello Mamino
1031f9814b Experimenting with the serializer 2017-05-26 00:57:24 +02:00
Marcello Mamino
eff8f91e27 Updated windows installer 2017-05-25 15:10:13 +02:00
Marcello Mamino
0baa32ac2c Stock images for windows 2017-05-25 14:20:10 +02:00
Marcello Mamino
d1e6493ee6 Added app menu 2017-05-24 22:26:29 +02:00
Marcello Mamino
05326fac38 Use GtkApplication framework 2017-05-23 19:13:06 +02:00
Marcello Mamino
9f6100eb2e Fixed regression in last commit 2017-05-21 18:16:56 +02:00
Marcello Mamino
ff480c0158 Beginning port to gtk3 2017-05-21 16:45:08 +02:00
Marcello Mamino
911c06b199 Added serializer, untested 2017-05-21 09:32:30 +02:00
Marcello Mamino
f1f8507c73 Snapshot name editable 2017-05-19 00:59:33 +02:00
Marcello Mamino
9f99994136 Fixed bug with drag&drop 2017-05-18 01:48:50 +02:00
Marcello Mamino
8c8edaa99a Snapshot tabs essentially working, missing names. 2017-05-18 01:24:18 +02:00
Marcello Mamino
c92f544b55 Cleanup 2017-05-17 02:04:17 +02:00
Marcello Mamino
0c7323746a First experiment with multiple tabs 2017-05-17 01:34:48 +02:00
AlexWithBeard
ba2d3fc297 Add Compiling on Fedora section 2017-02-11 18:08:30 +00:00
Marcello Mamino
27dd6efb23 Gray out clear button in calibrate mode 2016-12-31 19:37:59 +01:00
Marcello Mamino
81130f1498 Separated output panel 2016-12-31 19:06:16 +01:00
Marcello Mamino
99b8ab1ae6 Added snapshot infrastructure 2016-12-31 10:05:18 +01:00
62 changed files with 5284 additions and 1385 deletions

48
.gitignore vendored
View file

@ -1,3 +1,51 @@
.*
!.gitignore
!.travis.yml
!.valgrind.supp
# Object files
*.o
*.ko
*.obj
*.elf
build
tg-timer.res
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
tg-timer
tg-timer-dbg
tg-timer-prf
tg-timer-vlg
# autotools
Makefile
Makefile.in
/autom4te.cache
/aclocal.m4
/compile
/configure
/depcomp
/install-sh
/missing
/stamp-h1
/config.*
/libtool
/ltmain.sh
/test-driver
/test-suite.log
.libs
.deps
.dirstamp
*.log
*.trs
*.tar.gz
# build directories
/deb
/msi

View file

@ -1,5 +1,8 @@
language: c
sudo: false
dist: trusty
os:
- linux
- osx
@ -15,7 +18,7 @@ env:
addons:
apt:
packages:
- libgtk2.0-dev
- libgtk-3-dev
- libjack-jackd2-dev
- portaudio19-dev
- libfftw3-dev
@ -23,7 +26,7 @@ addons:
before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
brew update;
brew install gtk+;
brew install gtk+3;
brew install portaudio;
brew install fftw;
fi
@ -34,8 +37,10 @@ before_script:
sh -e /etc/init.d/xvfb start;
fi;
if [[ "$TARGET" == "test" ]]; then
mkdir ~/.config;
fi
mkdir -p ~/.config;
fi;
script:
make "$TARGET"
./autogen.sh;
./configure;
make "$TARGET";

13
.valgrind.supp Normal file
View file

@ -0,0 +1,13 @@
{
libasound1
Memcheck:Cond
obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0
}
{
libasound2
Memcheck:Param
ioctl(generic)
fun:ioctl
obj:/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0
}

View file

@ -1,81 +0,0 @@
VERSION := $(shell cat version)
CC ?= gcc
BUILDDIR ?= build
PREFIX ?= /usr/local
ifneq ($(shell uname -s),Darwin)
LDFLAGS += -Wl,--as-needed
endif
PACKAGES := gtk+-2.0 gthread-2.0 portaudio-2.0 fftw3f
CFLAGS += -Wall -O3 -ffast-math -DVERSION='"$(VERSION)"' `pkg-config --cflags $(PACKAGES)`
LDFLAGS += -lm -lpthread `pkg-config --libs $(PACKAGES)`
SRCDIR := src
CFILES := $(wildcard $(SRCDIR)/*.c)
HFILES := $(wildcard $(SRCDIR)/*.h)
ifeq ($(OS),Windows_NT)
LDFLAGS += -mwindows
DEBUG_LDFLAGS := -mconsole
EXT := .exe
RESFILE := $(BUILDDIR)/tg-timer.res
else
DEBUG_FLAGS :=
EXT :=
RESFILE :=
endif
all: $(BUILDDIR)/tg$(EXT) $(BUILDDIR)/tg-lt$(EXT)
.PHONY: all
debug: $(BUILDDIR)/tg-dbg$(EXT) $(BUILDDIR)/tg-lt-dbg$(EXT)
.PHONY: debug
profile: $(BUILDDIR)/tg-prf$(EXT) $(BUILDDIR)/tg-lt-prf$(EXT)
.PHONY: profile
test: $(BUILDDIR)/tg-dbg$(EXT)
$(BUILDDIR)/tg-dbg test
.PHONY: test
$(BUILDDIR)/tg-timer.res: icons/tg-timer.rc icons/tg-timer.ico
windres icons/tg-timer.rc -O coff -o $(BUILDDIR)/tg-timer.res
define TARGET
$(BUILDDIR)/$(1)_%.o: $(SRCDIR)/%.c $(HFILES)
$(CC) -c $(CFLAGS) -DPROGRAM_NAME='"$(1)"' $(2) $$< -o $$@
$(BUILDDIR)/$(1)$(EXT): $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/$(1)_%.o,$(CFILES)) $(RESFILE)
$(CC) -o $(BUILDDIR)/$(1)$(EXT) $$^ $(LDFLAGS) $(3)
ifeq ($(4),strip)
strip $(BUILDDIR)/$(1)$(EXT)
endif
endef
$(eval $(call TARGET,tg,,,strip))
$(eval $(call TARGET,tg-lt,-DLIGHT,,strip))
$(eval $(call TARGET,tg-dbg,-ggdb -DDEBUG,$(DEBUG_LDFLAGS),))
$(eval $(call TARGET,tg-lt-dbg,-ggdb -DDEBUG -DLIGHT,$(DEBUG_LDFLAGS),))
$(eval $(call TARGET,tg-prf,-pg,,))
$(eval $(call TARGET,tg-lt-prf,-DLIGHT -pg,,))
ICONSIZES := $(foreach SIZE, $(shell cat icons/sizes), $(SIZE)x$(SIZE))
$(ICONSIZES): %: icons/%/tg-timer.png
install -D -m 0644 $< $(PREFIX)/share/icons/hicolor/$@/apps/tg-timer.png
.PHONY: $(ICONSIZES)
install: all $(ICONSIZES)
install -D -m 0755 $(BUILDDIR)/tg$(EXT) $(PREFIX)/bin/tg-timer$(EXT)
install -D -m 0755 $(BUILDDIR)/tg-lt$(EXT) $(PREFIX)/bin/tg-timer-lt$(EXT)
install -D -m 0644 icons/tg-timer.desktop $(PREFIX)/share/applications/tg-timer.desktop
install -D -m 0644 icons/tg-timer-lt.desktop $(PREFIX)/share/applications/tg-timer-lt.desktop
install -D -m 0644 docs/tg-timer.1.gz $(PREFIX)/share/man/man1/tg-timer.1.gz
ln -s tg-timer.1.gz $(PREFIX)/share/man/man1/tg-timer-lt.1.gz
.PHONY: install
clean:
rm -rf $(BUILDDIR)/*
.PHONY: clean

77
Makefile.am Normal file
View file

@ -0,0 +1,77 @@
SUBDIRS = icons
bin_PROGRAMS = tg-timer
EXTRA_PROGRAMS = tg-timer-dbg \
tg-timer-prf \
tg-timer-vlg
MOSTLYCLEANFILES = $(EXTRA_PROGRAMS)
tg_timer_SOURCES = src/algo.c \
src/audio.c \
src/computer.c \
src/config.c \
src/interface.c \
src/output_panel.c \
src/serializer.c \
src/tg.h
tg_timer_dbg_SOURCES = $(tg_timer_SOURCES)
tg_timer_prf_SOURCES = $(tg_timer_SOURCES)
tg_timer_vlg_SOURCES = $(tg_timer_SOURCES)
LIBS = $(GTK_LIBS) \
$(GTHREAD_LIBS) \
$(PORTAUDIO_LIBS) \
$(FFTW_LIBS) \
-lpthread \
-lm
AM_CPPFLAGS = -DPROGRAM_NAME=\"Tg\" -DVERSION=\"$(PACKAGE_VERSION)\"
tg_timer_dbg_CPPFLAGS = $(AM_CPPFLAGS) -DDEBUG
AM_CFLAGS = $(GTK_CFLAGS) \
$(GTHREAD_CFLAGS) \
$(PORTAUDIO_CFLAGS) \
$(FFTW_CFLAGS)
tg_timer_dbg_CFLAGS = $(AM_CFLAGS) -ggdb
tg_timer_prf_CFLAGS = $(AM_CFLAGS) -pg
tg_timer_vlg_CFLAGS = $(AM_CFLAGS) -g
tg_timer_LDFLAGS = $(AM_LDFLAGS)
tg_timer_dbg_LDFLAGS = $(AM_LDFLAGS)
tg_timer_prf_LDFLAGS = $(AM_LDFLAGS)
tg_timer_vlg_LDFLAGS = $(AM_LDFLAGS)
if BE_WINDOWS
if HAVE_WINDRES
tg_timer_SOURCES += icons/tg-timer.rc
endif
tg_timer_LDFLAGS += -mwindows
tg_timer_dbg_LDFLAGS += -mconsole
tg_timer_prf_LDFLAGS += -mconsole
tg_timer_vlg_LDFLAGS += -mconsole
endif
desktopdir = $(datadir)/applications
dist_desktop_DATA = icons/tg-timer.desktop
mimedir = $(datadir)/mime/packages
dist_mime_DATA = icons/tg-timer.xml
dist_man_MANS = docs/tg-timer.1
EXTRA_DIST = autogen.sh \
icons \
packaging \
LICENSE \
README.md
.rc.o:
$(WINDRES) $< -O coff -o $@
test: tg-timer-dbg
./tg-timer-dbg test
.PHONY: test
valgrind: tg-timer-vlg
valgrind --leak-check=full -v --num-callers=99 --suppressions=.valgrind.supp ./$^
.PHONY: valgrind

View file

@ -1,8 +1,9 @@
# A program for timing mechanical watches [![Build Status](https://travis-ci.org/vacaboja/tg.svg?branch=master)](https://travis-ci.org/vacaboja/tg)
The program tg is copyright (C) 2015 by Marcello Mamino, and it is
distributed under the GNU GPL license version 2. The full source code of
tg is available at https://github.com/vacaboja/tg
The program tg is distributed under the GNU GPL license version 2. The full
source code of tg is available at
[https://github.com/vacaboja/tg](https://github.com/vacaboja/tg) and its
copyright belongs to the respective contributors.
Tg is in development, and there is still no manual. Some info can be found
in this
@ -43,20 +44,66 @@ You can now launch tg by typing
Binary .deb packages can be downloaded from https://tg.ciovil.li
### Compiling from sources
## Compiling from sources
The source code of tg can probably be built by any C99 compiler, however
only gcc and clang have been tested. You need the following libraries:
gtk2, portaudio2, fftw3 (all available as open-source).
gtk+3, portaudio2, fftw3 (all available as open-source).
Release build:
```sh
git clone https://github.com/vacaboja/tg.git
cd tg
./autogen.sh
./configure
make
```
Debug build:
```sh
make tg-timer-dbg
```
### Compiling on Windows
It is suggested to use the msys2 platform. First install msys2 according
to the instructions at [http://www.msys2.org](http://www.msys2.org). Then
issue the following commands.
```sh
pacman -S mingw-w64-x86_64-gcc make pkg-config mingw-w64-x86_64-gtk3 mingw-w64-x86_64-portaudio mingw-w64-x86_64-fftw git autoconf automake libtool
git clone https://github.com/vacaboja/tg.git
cd tg
./autogen.sh
./configure
make
```
### Compiling on Debian
To compile tg on Debian
sudo apt-get install libgtk2.0-dev libjack-jackd2-dev portaudio19-dev libfftw3-dev git
git clone https://github.com/vacaboja/tg.git
cd tg
make
```sh
sudo apt-get install libgtk-3-dev libjack-jackd2-dev portaudio19-dev libfftw3-dev git autoconf automake libtool
git clone https://github.com/vacaboja/tg.git
cd tg
./autogen.sh
./configure
make
```
The package libjack-jackd2-dev is not necessary, it only works around a
known bug (https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=718221).
### Compiling on Fedora
To compile tg on Fedora
```sh
sudo dnf install fftw-devel portaudio-devel gtk3-devel autoconf automake libtool
git clone https://github.com/vacaboja/tg.git
cd tg
./autogen.sh
./configure
make
```

1580
autogen.sh Executable file

File diff suppressed because it is too large Load diff

2
build/.gitignore vendored
View file

@ -1,2 +0,0 @@
*
!.gitignore

34
configure.ac Normal file
View file

@ -0,0 +1,34 @@
define([tg_version], m4_normalize(m4_include([version])))
AC_CONFIG_MACRO_DIRS([m4])
AC_INIT([Tg], [tg_version], [vacaboja@gmail.com], [tg-timer], [https://github.com/vacaboja/tg])
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
AC_PROG_CC
AC_CHECK_LIB([pthread], [pthread_mutex_init], [], [AC_MSG_ERROR([pthread not found])])
AC_CHECK_LIB([m], [sqrt], [], [AC_MSG_ERROR([libm not found])])
PKG_CHECK_MODULES([GTK], [gtk+-3.0 glib-2.0])
PKG_CHECK_MODULES([PORTAUDIO], [portaudio-2.0])
PKG_CHECK_MODULES([FFTW], [fftw3f])
AC_CHECK_TOOL([WINDRES], [windres])
AM_CONDITIONAL([HAVE_WINDRES], [test x$WINDRES != x])
AM_CONDITIONAL([BE_WINDOWS], [test x$OS = xWindows_NT])
AM_COND_IF([BE_WINDOWS], [AC_CONFIG_LINKS([icons/tg-timer.ico:icons/tg-timer.ico])])
CC_CHECK_LDFLAGS([-Wl,--as-needed], [AC_SUBST([AM_LDFLAGS], [-Wl,--as-needed])], [])
CC_CHECK_CFLAGS_APPEND([-Wall -Wextra], [], [])
AC_OUTPUT([Makefile icons/Makefile])
AC_MSG_RESULT([
$PACKAGE_NAME $VERSION
=====
prefix: ${prefix}
compiler: ${CC}
cflags: ${CFLAGS} ${AM_CFLAGS}
ldflags: ${LDFLAGS} ${AM_LDFLAGS}
])

View file

@ -4,7 +4,6 @@ tg \- a program for timing mechanincal watches
.SH SYNOPSIS
.nf
.B tg-timer
.B tg-timer-lt
.fi
.SH DESCRIPTION
Tg (tg-timer) is a program to evaluate the performance of mechanical watch

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 975 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 471 B

19
icons/Makefile.am Normal file
View file

@ -0,0 +1,19 @@
iconsdir = $(datadir)/icons/hicolor
nobase_dist_icons_DATA = \
16x16/apps/tg-timer.png \
22x22/apps/tg-timer.png \
32x32/apps/tg-timer.png \
48x48/apps/tg-timer.png \
64x64/apps/tg-timer.png \
128x128/apps/tg-timer.png \
256x256/apps/tg-timer.png \
scalable/apps/tg-timer.svg \
16x16/mimetypes/application-x-tg-timer-data.png \
22x22/mimetypes/application-x-tg-timer-data.png \
32x32/mimetypes/application-x-tg-timer-data.png \
48x48/mimetypes/application-x-tg-timer-data.png \
64x64/mimetypes/application-x-tg-timer-data.png \
128x128/mimetypes/application-x-tg-timer-data.png \
256x256/mimetypes/application-x-tg-timer-data.png \
scalable/mimetypes/application-x-tg-timer-data.svg

View file

@ -2,9 +2,10 @@
for A in `cat sizes`;
do
if test ! -d $[A]x$[A]
then
mkdir $[A]x$[A]
fi
inkscape -z -e $[A]x$[A]/tg-timer.png -w $A -h $A scalable/tg-timer.svg
mkdir -p ${A}x${A}/apps
mkdir -p ${A}x${A}/mimetypes
inkscape -z -e ${A}x${A}/apps/tg-timer.png -w $A -h $A scalable/apps/tg-timer.svg &>/dev/null
optipng -quiet ${A}x${A}/apps/tg-timer.png
inkscape -z -e ${A}x${A}/mimetypes/application-x-tg-timer-data.png -w $A -h $A scalable/application-x-tg-timer-data.svg &>/dev/null
optipng -quiet ${A}x${A}/mimetypes/application-x-tg-timer-data.png
done

View file

@ -2,6 +2,10 @@
SIZES=`cat sizes`
PNGS=`for A in $SIZES; do echo $[A]x$[A]/tg-timer.png; done`
PNGS=`for A in $SIZES; do echo ${A}x${A}/apps/tg-timer.png; done`
icotool -c -o tg-timer.ico $PNGS
PNGS=`for A in $SIZES; do echo ${A}x${A}/mimetypes/application-x-tg-timer-data.png; done`
icotool -c -o tg-document.ico $PNGS

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,279 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg144"
version="1.1"
width="48px"
height="48px"
sodipodi:docname="tg-document.svg"
inkscape:version="0.92.1 r15371">
<sodipodi:namedview
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1020"
id="namedview47"
showgrid="false"
showborder="false"
inkscape:zoom="6.9532168"
inkscape:cx="25.922372"
inkscape:cy="22.442674"
inkscape:window-x="0"
inkscape:window-y="31"
inkscape:window-maximized="1"
inkscape:current-layer="svg144" />
<metadata
id="metadata148">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs104">
<radialGradient
r="15.82"
gradientTransform="matrix(1.076,0,0,0.285,56.929661,32.427119)"
cx="24.31"
cy="42.1"
gradientUnits="userSpaceOnUse"
id="b">
<stop
id="stop79"
offset="0" />
<stop
id="stop81"
offset="1"
stop-opacity="0" />
</radialGradient>
<radialGradient
xlink:href="#linearGradient2222"
r="86.699997"
gradientTransform="scale(0.961,1.041)"
cx="33.970001"
cy="35.740002"
gradientUnits="userSpaceOnUse"
id="f">
<stop
id="stop84"
offset="0"
stop-color="#fafafa" />
<stop
id="stop86"
offset="1"
stop-color="#bbb" />
</radialGradient>
<radialGradient
r="37.75"
gradientTransform="matrix(.968 0 0 1.033 3.35.65)"
cx="8.82"
cy="3.76"
gradientUnits="userSpaceOnUse"
id="g">
<stop
id="stop89"
offset="0"
stop-color="#a3a3a3" />
<stop
id="stop91"
offset="1"
stop-color="#4c4c4c" />
</radialGradient>
<radialGradient
r="38.2"
gradientTransform="matrix(.968 0 0 1.033 3.35.65)"
cx="8.14"
cy="7.27"
gradientUnits="userSpaceOnUse"
id="e">
<stop
id="stop94"
offset="0"
stop-color="#fff" />
<stop
id="stop96"
offset="1"
stop-color="#f8f8f8" />
</radialGradient>
<radialGradient
r="1.21"
cx="10.1"
cy="18.82"
gradientUnits="userSpaceOnUse"
id="c">
<stop
id="stop99"
offset="0"
stop-color="#f0f0f0" />
<stop
id="stop101"
offset="1"
stop-color="#9a9a9a" />
</radialGradient>
<linearGradient
id="linearGradient2180">
<stop
offset="0"
style="stop-color:#ffffff;stop-opacity:1"
id="stop2182" />
<stop
offset="1"
style="stop-color:#ffffff;stop-opacity:0"
id="stop2184" />
</linearGradient>
<linearGradient
id="linearGradient2222">
<stop
offset="0"
style="stop-color:#5187d6;stop-opacity:1"
id="stop2224" />
<stop
offset="1"
style="stop-color:#1e4580;stop-opacity:1"
id="stop2227" />
</linearGradient>
<radialGradient
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.531667,1.4182182,-1.8951832,-2.046786,20.931759,20.852827)"
r="17.940001"
fy="10.45207"
fx="0.77357841"
cy="10.45207"
cx="0.77357841"
id="radialGradient4858"
xlink:href="#linearGradient2222" />
<linearGradient
y2="24.238262"
x2="12.499243"
y1="12.53757"
x1="8.8207808"
gradientTransform="matrix(2.8905003,0,0,1.7995248,-3.3500053,-18.415934)"
gradientUnits="userSpaceOnUse"
id="linearGradient3845-7"
xlink:href="#linearGradient2180" />
<radialGradient
r="37.75"
gradientTransform="matrix(0.968,0,0,1.033,3.3500009,0.65000099)"
cx="8.8199997"
cy="3.76"
gradientUnits="userSpaceOnUse"
id="g-7">
<stop
id="stop89-8"
offset="0"
stop-color="#a3a3a3" />
<stop
id="stop91-4"
offset="1"
stop-color="#4c4c4c" />
</radialGradient>
</defs>
<rect
style="fill-opacity:1;fill:url(#radialGradient4858);stroke:none"
id="rect108"
x="6.6"
y="3.65"
width="34.88"
height="41"
rx="1.2"
stroke="url(#g)"
fill="url(#f)" />
<g
style="stroke-width:1.06454599"
transform="matrix(0.94000018,0,0,0.93873543,-52.813919,-10.009373)"
id="g5030">
<path
style="fill:#ecffd9;fill-opacity:1;fill-rule:evenodd;stroke:#c3ea9b;stroke-width:1.06454599;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.41618497"
d="m 69.689357,47.100885 h -6.16187 v 1.245308 h 7.898291 l 1.438618,-6.050635 3.543037,14.016067 3.283344,-11.071321 2.961859,6.049287 3.977354,-3.593643 H 99.633964 V 45.588404 H 86.178659 l -3.243941,3.212614 -3.351636,-6.814017 -3.07983,8.365012 -3.344545,-13.162931 z"
id="path2188-3" />
<path
id="path3088-6"
d="m 68.794008,46.156452 h -5.266521 v 3.119652 l 8.79364,-0.127907 0.671176,-3.113401 2.898722,11.18944 1.134847,0.197791 3.048626,-10.116651 2.578138,5.542056 4.361075,-4.20413 12.620253,-0.127907 -0.76744,-4.000458 -12.687865,-0.127907 -3.116034,3.246745 -3.479543,-7.971582 -2.696109,7.100014 -3.600359,-12.55597 z"
style="opacity:0.38068183;fill:#ecffd9;fill-opacity:1;fill-rule:evenodd;stroke:#c3ea9b;stroke-width:1.06454635;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.41618497" />
</g>
<rect
style="stroke:#d3d7cf;stroke-opacity:1"
id="rect110"
fill="none"
x="7.66"
y="4.58"
stroke="url(#e)"
width="32.78"
height="39"
rx=".2" />
<rect
style="fill:none;fill-opacity:1;stroke:#888a85;stroke-opacity:1"
id="rect108-5"
x="6.5999999"
y="3.6500001"
width="34.880001"
height="41"
rx="1.2"
ry="1.2" />
<g
id="g4564"
transform="translate(58.000001)">
<ellipse
ry="12"
rx="12.000001"
cy="18"
cx="-31"
id="path4540"
style="opacity:1;fill:#d3d7cf;fill-opacity:1;fill-rule:evenodd;stroke:#d3d7cf;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<ellipse
ry="10.999999"
rx="11"
cy="18.000002"
cx="-31"
id="path4540-6"
style="opacity:1;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<g
style="stroke-width:1.07276595"
transform="matrix(0.93221519,0,0,0.93212416,-56.170349,1.2244571)"
id="g4523">
<path
inkscape:connector-curvature="0"
id="path5018"
d="m 27.000001,6.1961087 c -6.515886,0 -11.803181,5.2884593 -11.799271,11.8015803 -0.0033,6.512582 5.283777,11.800426 11.799271,11.800426 6.515885,0 11.804337,-5.28846 11.800426,-11.801582 0.0036,-6.512582 -5.284931,-11.8004243 -11.800426,-11.8004243 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#f0dd5b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.07276595;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<circle
r="1.7750911"
id="path4239-2"
style="fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1.27121437"
cx="27.000338"
cy="17.996984" />
<path
inkscape:connector-curvature="0"
id="path4241-97"
d="m 33.485556,10.292916 a 1.1835124,1.1835124 0 0 0 -0.813583,0.358254 l -6.508669,6.508666 a 1.1835124,1.1835124 0 1 0 1.673393,1.673394 l 6.508666,-6.508667 a 1.1835124,1.1835124 0 0 0 -0.859807,-2.031647 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.29106379;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
inkscape:connector-curvature="0"
id="path4241-9-3"
d="m 22.846565,12.659704 a 1.1835124,1.1835124 0 0 0 -0.82514,2.031647 l 4.141879,4.141879 a 1.1835124,1.1835124 0 1 0 1.673393,-1.673394 l -4.141879,-4.141878 a 1.1835124,1.1835124 0 0 0 -0.848253,-0.358254 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#888a85;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.29106379;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
</g>
<path
id="rect2178-1"
d="M 27.370146,4.9981407 A 13.000134,12.995402 0 0 0 14.556207,14.187934 c 2.633113,0.620371 5.525529,0.979998 8.561116,0.979998 5.921411,0 11.304067,-1.300023 15.273176,-3.420748 A 13.000134,12.995402 0 0 0 27.370146,4.9981407 Z"
style="opacity:0.43181817;fill:url(#linearGradient3845-7);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1" />
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

BIN
icons/tg-document.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

View file

@ -1,8 +0,0 @@
[Desktop Entry]
Type=Application
Name=Tg-lt
Comment=Software-based timegrapher (light)
Exec=tg-timer-lt
Icon=tg-timer
Terminal=false
Categories=Utility;

View file

@ -2,7 +2,8 @@
Type=Application
Name=Tg
Comment=Software-based timegrapher
Exec=tg-timer
Exec=tg-timer %F
Icon=tg-timer
Terminal=false
Categories=Utility;
Categories=Utility
MimeType=application/x-tg-timer-data

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 KiB

After

Width:  |  Height:  |  Size: 363 KiB

10
icons/tg-timer.xml Normal file
View file

@ -0,0 +1,10 @@
<?xml version="1.0"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="application/x-tg-timer-data">
<comment>Watch timing data</comment>
<magic>
<match type="string" offset="0" value="Ltg-timer-version;"/>
</magic>
<glob pattern="*.tgj"/>
</mime-type>
</mime-info>

311
m4/attributes.m4 Normal file
View file

@ -0,0 +1,311 @@
dnl Macros to check the presence of generic (non-typed) symbols.
dnl Copyright (c) 2006-2008 Diego Pettenò <flameeyes@gmail.com>
dnl Copyright (c) 2006-2008 xine project
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
dnl the Free Software Foundation; either version 2, or (at your option)
dnl any later version.
dnl
dnl This program is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
dnl GNU General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this program; if not, write to the Free Software
dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
dnl 02110-1301, USA.
dnl
dnl As a special exception, the copyright owners of the
dnl macro gives unlimited permission to copy, distribute and modify the
dnl configure scripts that are the output of Autoconf when processing the
dnl Macro. You need not follow the terms of the GNU General Public
dnl License when using or distributing such scripts, even though portions
dnl of the text of the Macro appear in them. The GNU General Public
dnl License (GPL) does govern all other use of the material that
dnl constitutes the Autoconf Macro.
dnl
dnl This special exception to the GPL applies to versions of the
dnl Autoconf Macro released by this project. When you make and
dnl distribute a modified version of the Autoconf Macro, you may extend
dnl this special exception to the GPL to apply to your modified version as
dnl well.
dnl Check if the flag is supported by compiler
dnl CC_CHECK_CFLAGS_SILENT([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
AC_DEFUN([CC_CHECK_CFLAGS_SILENT], [
AC_CACHE_VAL(AS_TR_SH([cc_cv_cflags_$1]),
[ac_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $1"
AC_COMPILE_IFELSE([int a;],
[eval "AS_TR_SH([cc_cv_cflags_$1])='yes'"],
[eval "AS_TR_SH([cc_cv_cflags_$1])='no'"])
CFLAGS="$ac_save_CFLAGS"
])
AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes],
[$2], [$3])
])
dnl Check if the flag is supported by compiler (cacheable)
dnl CC_CHECK_CFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
AC_DEFUN([CC_CHECK_CFLAGS], [
AC_CACHE_CHECK([if $CC supports $1 flag],
AS_TR_SH([cc_cv_cflags_$1]),
CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here!
)
AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes],
[$2], [$3])
])
dnl CC_CHECK_CFLAG_APPEND(FLAG, [action-if-found], [action-if-not-found])
dnl Check for CFLAG and appends them to CFLAGS if supported
AC_DEFUN([CC_CHECK_CFLAG_APPEND], [
AC_CACHE_CHECK([if $CC supports $1 flag],
AS_TR_SH([cc_cv_cflags_$1]),
CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here!
)
AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes],
[CFLAGS="$CFLAGS $1"; DEBUG_CFLAGS="$DEBUG_CFLAGS $1"; $2], [$3])
])
dnl CC_CHECK_CFLAGS_APPEND([FLAG1 FLAG2], [action-if-found], [action-if-not])
AC_DEFUN([CC_CHECK_CFLAGS_APPEND], [
for flag in $1; do
CC_CHECK_CFLAG_APPEND($flag, [$2], [$3])
done
])
dnl Check if the flag is supported by linker (cacheable)
dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND])
AC_DEFUN([CC_CHECK_LDFLAGS], [
AC_CACHE_CHECK([if $CC supports $1 flag],
AS_TR_SH([cc_cv_ldflags_$1]),
[ac_save_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS $1"
AC_LINK_IFELSE([int main() { return 1; }],
[eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"],
[eval "AS_TR_SH([cc_cv_ldflags_$1])="])
LDFLAGS="$ac_save_LDFLAGS"
])
AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes],
[$2], [$3])
])
dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for
dnl the current linker to avoid undefined references in a shared object.
AC_DEFUN([CC_NOUNDEFINED], [
dnl We check $host for which systems to enable this for.
AC_REQUIRE([AC_CANONICAL_HOST])
case $host in
dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads
dnl are requested, as different implementations are present; to avoid problems
dnl use -Wl,-z,defs only for those platform not behaving this way.
*-freebsd* | *-openbsd*) ;;
*)
dnl First of all check for the --no-undefined variant of GNU ld. This allows
dnl for a much more readable commandline, so that people can understand what
dnl it does without going to look for what the heck -z defs does.
for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do
CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"])
break
done
;;
esac
AC_SUBST([LDFLAGS_NOUNDEFINED])
])
dnl Check for a -Werror flag or equivalent. -Werror is the GCC
dnl and ICC flag that tells the compiler to treat all the warnings
dnl as fatal. We usually need this option to make sure that some
dnl constructs (like attributes) are not simply ignored.
dnl
dnl Other compilers don't support -Werror per se, but they support
dnl an equivalent flag:
dnl - Sun Studio compiler supports -errwarn=%all
AC_DEFUN([CC_CHECK_WERROR], [
AC_CACHE_CHECK(
[for $CC way to treat warnings as errors],
[cc_cv_werror],
[CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror],
[CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])])
])
])
AC_DEFUN([CC_CHECK_ATTRIBUTE], [
AC_REQUIRE([CC_CHECK_WERROR])
AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))],
AS_TR_SH([cc_cv_attribute_$1]),
[ac_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $cc_cv_werror"
AC_COMPILE_IFELSE([$3],
[eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"],
[eval "AS_TR_SH([cc_cv_attribute_$1])='no'"])
CFLAGS="$ac_save_CFLAGS"
])
AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes],
[AC_DEFINE(
AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1,
[Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))]
)
$4],
[$5])
])
AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [
CC_CHECK_ATTRIBUTE(
[constructor],,
[void __attribute__((constructor)) ctor() { int a; }],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_FORMAT], [
CC_CHECK_ATTRIBUTE(
[format], [format(printf, n, n)],
[void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [
CC_CHECK_ATTRIBUTE(
[format_arg], [format_arg(printf)],
[char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [
CC_CHECK_ATTRIBUTE(
[visibility_$1], [visibility("$1")],
[void __attribute__((visibility("$1"))) $1_function() { }],
[$2], [$3])
])
AC_DEFUN([CC_ATTRIBUTE_NONNULL], [
CC_CHECK_ATTRIBUTE(
[nonnull], [nonnull()],
[void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_UNUSED], [
CC_CHECK_ATTRIBUTE(
[unused], ,
[void some_function(void *foo, __attribute__((unused)) void *bar);],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [
CC_CHECK_ATTRIBUTE(
[sentinel], ,
[void some_function(void *foo, ...) __attribute__((sentinel));],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [
CC_CHECK_ATTRIBUTE(
[deprecated], ,
[void some_function(void *foo, ...) __attribute__((deprecated));],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_ALIAS], [
CC_CHECK_ATTRIBUTE(
[alias], [weak, alias],
[void other_function(void *foo) { }
void some_function(void *foo) __attribute__((weak, alias("other_function")));],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_MALLOC], [
CC_CHECK_ATTRIBUTE(
[malloc], ,
[void * __attribute__((malloc)) my_alloc(int n);],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_PACKED], [
CC_CHECK_ATTRIBUTE(
[packed], ,
[struct astructure { char a; int b; long c; void *d; } __attribute__((packed));],
[$1], [$2])
])
AC_DEFUN([CC_ATTRIBUTE_CONST], [
CC_CHECK_ATTRIBUTE(
[const], ,
[int __attribute__((const)) twopow(int n) { return 1 << n; } ],
[$1], [$2])
])
AC_DEFUN([CC_FLAG_VISIBILITY], [
AC_REQUIRE([CC_CHECK_WERROR])
AC_CACHE_CHECK([if $CC supports -fvisibility=hidden],
[cc_cv_flag_visibility],
[cc_flag_visibility_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $cc_cv_werror"
CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden],
cc_cv_flag_visibility='yes',
cc_cv_flag_visibility='no')
CFLAGS="$cc_flag_visibility_save_CFLAGS"])
AS_IF([test "x$cc_cv_flag_visibility" = "xyes"],
[AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1,
[Define this if the compiler supports the -fvisibility flag])
$1],
[$2])
])
AC_DEFUN([CC_FUNC_EXPECT], [
AC_REQUIRE([CC_CHECK_WERROR])
AC_CACHE_CHECK([if compiler has __builtin_expect function],
[cc_cv_func_expect],
[ac_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $cc_cv_werror"
AC_COMPILE_IFELSE(
[int some_function() {
int a = 3;
return (int)__builtin_expect(a, 3);
}],
[cc_cv_func_expect=yes],
[cc_cv_func_expect=no])
CFLAGS="$ac_save_CFLAGS"
])
AS_IF([test "x$cc_cv_func_expect" = "xyes"],
[AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1,
[Define this if the compiler supports __builtin_expect() function])
$1],
[$2])
])
AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [
AC_REQUIRE([CC_CHECK_WERROR])
AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported],
[cc_cv_attribute_aligned],
[ac_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $cc_cv_werror"
for cc_attribute_align_try in 64 32 16 8 4 2; do
AC_COMPILE_IFELSE([
int main() {
static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0;
return c;
}], [cc_cv_attribute_aligned=$cc_attribute_align_try; break])
done
CFLAGS="$ac_save_CFLAGS"
])
if test "x$cc_cv_attribute_aligned" != "x"; then
AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned],
[Define the highest alignment supported])
fi
])

View file

@ -1,5 +1,5 @@
tg-timer (0.4.0-1) UNRELEASED; urgency=medium
tg-timer (0.5.0-1) UNRELEASED; urgency=medium
* We don't keep a changelog.
-- Marcello Mamino <vacaboja@gmail.com> Thu, 20 Oct 2016 00:29:29 +0200
-- Marcello Mamino <vacaboja@gmail.com> Sat, 24 Jun 2017 16:24:08 +0200

View file

@ -3,7 +3,7 @@ Maintainer: Marcello Mamino <vacaboja@gmail.com>
Section: misc
Priority: optional
Standards-Version: 3.9.8
Build-Depends: debhelper (>= 9), libgtk2.0-dev (>= 2.24), portaudio19-dev (>=19), libfftw3-dev (>=3.3)
Build-Depends: debhelper (>= 9), libgtk-3-dev (>= 3.14), portaudio19-dev (>=19), libfftw3-dev (>=3.3)
Package: tg-timer
Architecture: any

View file

@ -3,7 +3,7 @@
override_dh_auto_test:
override_dh_auto_install:
PREFIX=$$(pwd)/debian/tg-timer/usr $(MAKE) install
$(MAKE) DESTDIR=$$(pwd)/debian/tg-timer prefix=/usr install
%:
dh $@

View file

@ -7,10 +7,13 @@ cd "$DIR"/..
VERSION=`cat version`
cd build
./autogen.sh
./configure
make dist
rm -rf deb
mkdir deb
cp tg-timer_"$VERSION".tar.gz deb/tg-timer_"$VERSION".orig.tar.gz
cp tg-timer-"$VERSION".tar.gz deb/tg-timer_"$VERSION".orig.tar.gz
cd deb

View file

@ -2,35 +2,36 @@
if test ! -d "$1"
then
echo Usage: $0 dll-dir
echo Usage: $0 resources-dir
exit 1
fi
DIR=`dirname "${BASH_SOURCE[0]}"`
ABSDIR=`cd "$DIR"; pwd`
DLLS=`cd "$1"; pwd`
RESOURCES=`cd "$1"; pwd`
TARGET="$ABSDIR/../build/msi"
TARGET="$ABSDIR/../msi"
cd "$ABSDIR"/..
VERSION=`cat version`
make
rm -rf "$TARGET"
mkdir -p "$TARGET"
cd "$TARGET"
../configure
make
mv "$TARGET/tg-timer.exe" "$TARGET/tg.exe"
cp "$ABSDIR/tg-timer.wxs" "$TARGET"
cp "$ABSDIR/LICENSE.rtf" "$TARGET"
cp "$ABSDIR/../README.md" "$TARGET"
cp "$ABSDIR/../LICENSE" "$TARGET"
cp "$ABSDIR/../build/tg.exe" "$TARGET"
cp "$ABSDIR/../build/tg-lt.exe" "$TARGET"
cp "$DLLS"/* "$TARGET"
heat dir "$DLLS" -srd -gg -sreg -dr INSTALLDIR -cg Dlls -out "$TARGET/Dlls.wxs"
cp "$ABSDIR/../icons/tg-document.ico" "$TARGET"
cp -r "$RESOURCES"/* "$TARGET"
heat dir "$RESOURCES" -srd -gg -sreg -dr INSTALLDIR -cg Resources -out "$TARGET/Resources.wxs"
cd "$TARGET"
candle tg-timer.wxs Dlls.wxs
light -out tg-timer_${VERSION}.msi -ext WixUIExtension tg-timer.wixobj Dlls.wixobj
candle tg-timer.wxs Resources.wxs
light -out tg-timer_${VERSION}.msi -ext WixUIExtension tg-timer.wixobj Resources.wixobj

View file

@ -1,10 +0,0 @@
#!/bin/bash
DIR=`dirname "${BASH_SOURCE[0]}"`
ABSDIR=`cd "$DIR"; pwd`
cd "$DIR"/..
VERSION=`cat version`
tar czf build/tg-timer_"$VERSION".tar.gz * --exclude=".*" --exclude="build/*" --exclude="packaging" --xform="s|\\(.*\\)|tg-timer-$VERSION/\1|"

View file

@ -1,32 +0,0 @@
#!/bin/bash
if test ! -d "$1"
then
echo Usage: $0 dll-dir
exit 1
fi
DIR=`dirname "${BASH_SOURCE[0]}"`
ABSDIR=`cd "$DIR"; pwd`
DLLS=`cd "$1"; pwd`
cd "$ABSDIR"/..
VERSION=`cat version`
NAME="tg-timer_$VERSION"
TARGET="$ABSDIR/../build/$NAME"
make
rm -rf "$TARGET"
mkdir -p "$TARGET"
cp "$ABSDIR/../README.md" "$TARGET"
cp "$ABSDIR/../LICENSE" "$TARGET"
cp "$ABSDIR/../build/tg.exe" "$TARGET"
cp "$ABSDIR/../build/tg-lt.exe" "$TARGET"
cp "$DLLS"/* "$TARGET"
cd build
rm -f "${NAME}.zip"
7z a -tzip "${NAME}.zip" "${NAME}"/*

View file

@ -1,10 +1,10 @@
<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Product Name='Tg' Id='f4b9cf89-06fc-4c90-bdc8-24d2bcc77ef9' UpgradeCode='088fdac7-fc91-4881-a2db-eed04425a207'
Language='1033' Codepage='1252' Version='0.4.0' Manufacturer='Marcello Mamino'>
<Product Name='Tg' Id='e725c262-348c-4e31-86ff-234893330f82' UpgradeCode='088fdac7-fc91-4881-a2db-eed04425a207'
Language='1033' Codepage='1252' Version='0.5.0' Manufacturer='Marcello Mamino'>
<Package Id='*' Keywords='Installer'
Description='Tg 0.4.0 Installer'
Description='Tg 0.5.0 Installer'
Manufacturer='Marcello Mamino'
InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' />
@ -17,25 +17,24 @@
<Directory Id='ProgramFilesFolder' Name='PFiles'>
<Directory Id='INSTALLDIR' Name='tg-timer'>
<Component Id='MainExecutable' Guid='fa5c0b97-a6ce-44a3-a917-90a644ea0f84'>
<Component Id='MainExecutable' Guid='c8210ac7-1900-4339-9825-a6d0694ecc6b'>
<File Id='TgEXE' Name='tg.exe' DiskId='1' Source='tg.exe' KeyPath='yes'>
<Shortcut Id='startmenuTg' Directory='ProgramMenuDir' Name='Tg 0.4.0' WorkingDirectory='INSTALLDIR' Icon='tg.exe' IconIndex='0' Advertise='yes' />
<Shortcut Id='desktopTg' Directory='DesktopFolder' Name='Tg 0.4.0' WorkingDirectory='INSTALLDIR' Icon='tg.exe' IconIndex='0' Advertise='yes' />
<Shortcut Id='startmenuTg' Directory='ProgramMenuDir' Name='Tg 0.5.0' WorkingDirectory='INSTALLDIR' Icon='tg.exe' IconIndex='0' Advertise='yes' />
<Shortcut Id='desktopTg' Directory='DesktopFolder' Name='Tg 0.5.0' WorkingDirectory='INSTALLDIR' Icon='tg.exe' IconIndex='0' Advertise='yes' />
</File>
<File Id='TgDocumentICO' Name='tg-document.ico' Source='tg-document.ico' />
<ProgId Id='TgjFile' Description='Tg data file' Icon='TgDocumentICO'>
<Extension Id='tgj' ContentType='application/x-tg-timer-data'>
<Verb Id='open' Command='Open' TargetFile='TgEXE' Argument='"%1"' />
</Extension>
</ProgId>
</Component>
<Component Id='LightExecutable' Guid='de7b2b2b-f7ed-434d-b182-a6d915783e2f'>
<File Id='TgLtEXE' Name='tg-lt.exe' DiskId='1' Source='tg-lt.exe' KeyPath='yes'>
<Shortcut Id='startmenuTgLt' Directory='ProgramMenuDir' Name='Tg lt 0.4.0' WorkingDirectory='INSTALLDIR' Icon='tg.exe' IconIndex='0' Advertise='yes' />
<Shortcut Id='desktopTgLt' Directory='DesktopFolder' Name='Tg lt 0.4.0' WorkingDirectory='INSTALLDIR' Icon='tg.exe' IconIndex='0' Advertise='yes' />
</File>
</Component>
<Component Id='License' Guid='49fcd7cf-94ba-4192-a0ca-501a72b4ac33'>
<Component Id='License' Guid='bae8c647-10b0-43b2-9373-7ce83d066062'>
<File Id='License' Name='LICENSE' DiskId='1' Source='LICENSE' KeyPath='yes' />
</Component>
<Component Id='Readme' Guid='542ed91a-6411-4183-9926-301416309136'>
<Component Id='Readme' Guid='6e6ae226-5a2b-4604-97fa-c931ccedab73'>
<File Id='Readme' Name='README.md' DiskId='1' Source='README.md' KeyPath='yes' />
</Component>
@ -43,8 +42,8 @@
</Directory>
<Directory Id='ProgramMenuFolder' Name='Programs'>
<Directory Id='ProgramMenuDir' Name='Tg 0.4.0'>
<Component Id='ProgramMenuDir' Guid='9f8a890f-71f7-4f4c-b596-4ec402c0dfe5'>
<Directory Id='ProgramMenuDir' Name='Tg 0.5.0'>
<Component Id='ProgramMenuDir' Guid='1c065e58-79d8-4bb7-a3b6-364e597137fd'>
<RemoveFolder Id='ProgramMenuDir' On='uninstall' />
<RegistryValue Root='HKCU' Key='Software\[Manufacturer]\[ProductName]' Type='string' Value='' KeyPath='yes' />
</Component>
@ -56,10 +55,9 @@
<Feature Id='Complete' Level='1' ConfigurableDirectory='INSTALLDIR'>
<ComponentRef Id='MainExecutable' />
<ComponentRef Id='LightExecutable' />
<ComponentRef Id='License' />
<ComponentRef Id='Readme' />
<ComponentGroupRef Id='Dlls' />
<ComponentGroupRef Id='Resources' />
<ComponentRef Id='ProgramMenuDir' />
</Feature>

View file

@ -22,13 +22,12 @@
<Shortcut Id='startmenuTg' Directory='ProgramMenuDir' Name='Tg #VERSION#' WorkingDirectory='INSTALLDIR' Icon='tg.exe' IconIndex='0' Advertise='yes' />
<Shortcut Id='desktopTg' Directory='DesktopFolder' Name='Tg #VERSION#' WorkingDirectory='INSTALLDIR' Icon='tg.exe' IconIndex='0' Advertise='yes' />
</File>
</Component>
<Component Id='LightExecutable' Guid='#UUID#'>
<File Id='TgLtEXE' Name='tg-lt.exe' DiskId='1' Source='tg-lt.exe' KeyPath='yes'>
<Shortcut Id='startmenuTgLt' Directory='ProgramMenuDir' Name='Tg lt #VERSION#' WorkingDirectory='INSTALLDIR' Icon='tg.exe' IconIndex='0' Advertise='yes' />
<Shortcut Id='desktopTgLt' Directory='DesktopFolder' Name='Tg lt #VERSION#' WorkingDirectory='INSTALLDIR' Icon='tg.exe' IconIndex='0' Advertise='yes' />
</File>
<File Id='TgDocumentICO' Name='tg-document.ico' Source='tg-document.ico' />
<ProgId Id='TgjFile' Description='Tg data file' Icon='TgDocumentICO'>
<Extension Id='tgj' ContentType='application/x-tg-timer-data'>
<Verb Id='open' Command='Open' TargetFile='TgEXE' Argument='"%1"' />
</Extension>
</ProgId>
</Component>
<Component Id='License' Guid='#UUID#'>
@ -56,10 +55,9 @@
<Feature Id='Complete' Level='1' ConfigurableDirectory='INSTALLDIR'>
<ComponentRef Id='MainExecutable' />
<ComponentRef Id='LightExecutable' />
<ComponentRef Id='License' />
<ComponentRef Id='Readme' />
<ComponentGroupRef Id='Dlls' />
<ComponentGroupRef Id='Resources' />
<ComponentRef Id='ProgramMenuDir' />
</Feature>

View file

@ -22,21 +22,21 @@ struct filter {
double a0,a1,a2,b1,b2;
};
int fl_cmp(const void *a, const void *b)
static int fl_cmp(const void *a, const void *b)
{
float x = *(float*)a;
float y = *(float*)b;
return x<y ? -1 : x>y ? 1 : 0;
}
int int_cmp(const void *a, const void *b)
static int int_cmp(const void *a, const void *b)
{
int x = *(int*)a;
int y = *(int*)b;
return x<y ? -1 : x>y ? 1 : 0;
}
void make_hp(struct filter *f, double freq)
static void make_hp(struct filter *f, double freq)
{
double K = tan(M_PI * freq);
double norm = 1 / (1 + K * sqrt(2) + K * K);
@ -47,7 +47,7 @@ void make_hp(struct filter *f, double freq)
f->b2 = (1 - K * sqrt(2) + K * K) * norm;
}
void make_lp(struct filter *f, double freq)
static void make_lp(struct filter *f, double freq)
{
double K = tan(M_PI * freq);
double norm = 1 / (1 + K * sqrt(2) + K * K);
@ -58,7 +58,7 @@ void make_lp(struct filter *f, double freq)
f->b2 = (1 - K * sqrt(2) + K * K) * norm;
}
void run_filter(struct filter *f, float *buff, int size)
static void run_filter(struct filter *f, float *buff, int size)
{
int i;
double z1 = 0, z2 = 0;
@ -98,25 +98,61 @@ void setup_buffers(struct processing_buffers *b)
b->events = malloc(EVENTS_MAX * sizeof(uint64_t));
b->ready = 0;
#ifdef DEBUG
b->debug = fftwf_malloc(b->sample_count * sizeof(float));
b->debug_size = b->sample_count;
b->debug = fftwf_malloc(b->debug_size * sizeof(float));
#endif
}
void pb_destroy(struct processing_buffers *b)
{
fftwf_free(b->samples);
free(b->samples_sc);
free(b->waveform);
free(b->waveform_sc);
fftwf_free(b->fft);
fftwf_free(b->sc_fft);
fftwf_free(b->tic_wf);
fftwf_free(b->slice_wf);
fftwf_free(b->tic_fft);
fftwf_free(b->slice_fft);
free(b->tic_c);
fftwf_destroy_plan(b->plan_a);
fftwf_destroy_plan(b->plan_b);
fftwf_destroy_plan(b->plan_c);
fftwf_destroy_plan(b->plan_d);
fftwf_destroy_plan(b->plan_e);
fftwf_destroy_plan(b->plan_f);
fftwf_destroy_plan(b->plan_g);
free(b->hpf);
free(b->lpf);
free(b->events);
#ifdef DEBUG
fftwf_free(b->debug);
#endif
}
struct processing_buffers *pb_clone(struct processing_buffers *p)
{
struct processing_buffers *new = malloc(sizeof(struct processing_buffers));
new->sample_rate = p->sample_rate;
new->waveform = malloc(new->sample_rate * sizeof(float));
memcpy(new->waveform, p->waveform, new->sample_rate * sizeof(float));
new->events = malloc(EVENTS_MAX * sizeof(uint64_t));
memcpy(new->events, p->events, EVENTS_MAX * sizeof(uint64_t));
new->sample_count = ceil(p->period);
new->waveform = malloc(new->sample_count * sizeof(float));
memcpy(new->waveform, p->waveform, new->sample_count * sizeof(float));
if(p->events) {
new->events = malloc(EVENTS_MAX * sizeof(uint64_t));
memcpy(new->events, p->events, EVENTS_MAX * sizeof(uint64_t));
} else
new->events = NULL;
#ifdef DEBUG
new->debug = malloc(p->sample_count * sizeof(float));
memcpy(new->debug, p->debug, p->sample_count * sizeof(float));
new->debug_size = p->debug_size;
if(p->debug) {
new->debug = malloc(new->debug_size * sizeof(float));
memcpy(new->debug, p->debug, new->debug_size * sizeof(float));
} else
new->debug = NULL;
#endif
new->sample_count = p->sample_count;
new->sample_rate = p->sample_rate;
new->period = p->period;
new->sigma = p->sigma;
new->be = p->be;
@ -141,7 +177,7 @@ void pb_destroy_clone(struct processing_buffers *p)
free(p);
}
float vmax(float *v, int a, int b, int *i_max)
static float vmax(float *v, int a, int b, int *i_max)
{
float max = v[a];
if(i_max) *i_max = a;
@ -155,7 +191,7 @@ float vmax(float *v, int a, int b, int *i_max)
return max;
}
void noise_suppressor(struct processing_buffers *p)
static void noise_suppressor(struct processing_buffers *p)
{
float *a = p->samples_sc;
float *b = p->samples_sc + p->sample_count;
@ -189,15 +225,13 @@ void noise_suppressor(struct processing_buffers *p)
}
}
void prepare_data(struct processing_buffers *b, int run_noise_suppressor)
static void prepare_data(struct processing_buffers *b, int run_noise_suppressor)
{
int i;
memset(b->samples + b->sample_count, 0, b->sample_count * sizeof(float));
run_filter(b->hpf, b->samples, b->sample_count);
#ifndef LIGHT
if(run_noise_suppressor) noise_suppressor(b);
#endif
for(i=0; i < b->sample_count; i++)
b->samples[i] = fabs(b->samples[i]);
@ -227,7 +261,7 @@ void prepare_data(struct processing_buffers *b, int run_noise_suppressor)
#endif
}
int peak_detector(float *buff, int a, int b)
static int peak_detector(float *buff, int a, int b)
{
int i_max;
double max = vmax(buff, a, b+1, &i_max);
@ -262,7 +296,7 @@ int peak_detector(float *buff, int a, int b)
return i_max;
}
double estimate_period(struct processing_buffers *p)
static double estimate_period(struct processing_buffers *p)
{
int first_estimate;
vmax(p->samples_sc, p->sample_rate / 12, p->sample_rate, &first_estimate);
@ -301,7 +335,7 @@ double estimate_period(struct processing_buffers *p)
} else return estimate;
}
int compute_period(struct processing_buffers *b, int bph)
static int compute_period(struct processing_buffers *b, int bph)
{
double estimate;
if(bph)
@ -352,7 +386,7 @@ int compute_period(struct processing_buffers *b, int bph)
return 0;
}
float tmean(float *x, int n)
static float tmean(float *x, int n)
{
if(n>16) {
qsort(x,16,sizeof(float),fl_cmp);
@ -378,7 +412,7 @@ float tmean(float *x, int n)
return sum/(n*4/5);
}
void compute_phase(struct processing_buffers *p, double period)
static void compute_phase(struct processing_buffers *p, double period)
{
int i;
double x = 0, y = 0;
@ -400,7 +434,7 @@ void compute_phase(struct processing_buffers *p, double period)
p->phase = period * (M_PI + atan2(y,x)) / (2 * M_PI);
}
void compute_waveform(struct processing_buffers *p, int wf_size)
static void compute_waveform(struct processing_buffers *p, int wf_size)
{
int i;
for(i=0; i<2*p->sample_rate; i++)
@ -428,7 +462,7 @@ void compute_waveform(struct processing_buffers *p, int wf_size)
p->waveform_max = vmax(p->waveform, 0, wf_size, &p->waveform_max_i);
}
void prepare_waveform(struct processing_buffers *p)
static void prepare_waveform(struct processing_buffers *p)
{
compute_phase(p,p->period/2);
compute_waveform(p,ceil(p->period));
@ -440,13 +474,13 @@ void prepare_waveform(struct processing_buffers *p)
fftwf_execute(p->plan_d);
}
void prepare_waveform_cal(struct processing_buffers *p)
static void prepare_waveform_cal(struct processing_buffers *p)
{
compute_phase(p,p->sample_rate);
compute_waveform(p,p->sample_rate);
}
void smooth(float *in, float *out, int window, int size)
static void smooth(float *in, float *out, int window, int size)
{
int i;
double k = 1 - (1. / window);
@ -471,7 +505,7 @@ void smooth(float *in, float *out, int window, int size)
}
}
int compute_parameters(struct processing_buffers *p)
static int compute_parameters(struct processing_buffers *p)
{
int tic_to_toc = peak_detector(p->waveform_sc,
floor(p->period/2)-p->sample_rate/50,
@ -518,7 +552,7 @@ int compute_parameters(struct processing_buffers *p)
return 0;
}
void do_locate_events(int *events, struct processing_buffers *p, float *waveform, int last, int offset, int count)
static void do_locate_events(int *events, struct processing_buffers *p, float *waveform, int last, int offset, int count)
{
int i;
memset(p->tic_wf, 0, p->sample_rate * sizeof(float));
@ -552,7 +586,7 @@ void do_locate_events(int *events, struct processing_buffers *p, float *waveform
}
}
void locate_events(struct processing_buffers *p)
static void locate_events(struct processing_buffers *p)
{
int count = 1 + ceil((p->timestamp - p->events_from) / p->period);
if(count <= 0 || 2*count >= EVENTS_MAX) {
@ -580,7 +614,7 @@ void locate_events(struct processing_buffers *p)
p->events[j] = 0;
}
void compute_amplitude(struct processing_buffers *p, double la)
static void compute_amplitude(struct processing_buffers *p, double la)
{
int i,j,k;
@ -630,15 +664,17 @@ void compute_amplitude(struct processing_buffers *p, double la)
} else
goto next_threshold;
}
double tic_amp = la * .5 / sin(M_PI * tic_pulse / p->period);
double toc_amp = la * .5 / sin(M_PI * toc_pulse / p->period);
double tic_amp_abs = .5 / sin(M_PI * tic_pulse / p->period);
double toc_amp_abs = .5 / sin(M_PI * toc_pulse / p->period);
double tic_amp = la * tic_amp_abs;
double toc_amp = la * toc_amp_abs;
if(135 < tic_amp && tic_amp < 360 && 135 < toc_amp && toc_amp < 360 && fabs(tic_amp - toc_amp) < 60) {
p->amp = (tic_amp + toc_amp) / 2;
p->amp = (tic_amp_abs + toc_amp_abs) / 2;
p->tic_pulse = tic_pulse;
p->toc_pulse = toc_pulse;
p->be = p->period/2 - fabs(p->toc - p->tic + p->tic_pulse - p->toc_pulse);
debug("amp: be = %.1f\n",fabs(p->be)*1000/p->sample_rate);
debug("amp = %f\n", p->amp);
debug("amp = %f\n", la * p->amp);
break;
} else
debug("amp rejected\n");
@ -655,7 +691,14 @@ void setup_cal_data(struct calibration_data *cd)
cd->events = malloc(cd->size * sizeof(uint64_t));
}
int add_sample_cal(struct processing_buffers *p, struct calibration_data *cd)
void cal_data_destroy(struct calibration_data *cd)
{
free(cd->times);
free(cd->phases);
free(cd->events);
}
static int add_sample_cal(struct processing_buffers *p, struct calibration_data *cd)
{
int i;
double phase = -1;
@ -694,7 +737,7 @@ int add_sample_cal(struct processing_buffers *p, struct calibration_data *cd)
return 0;
}
void compute_cal(struct calibration_data *cd, int sample_rate)
static void compute_cal(struct calibration_data *cd)
{
int i;
double x = 0, y = 0;
@ -724,11 +767,11 @@ void compute_cal(struct calibration_data *cd, int sample_rate)
cd->state = delta * 3600 * 24 < 0.1 ? 1 : -1;
}
void process(struct processing_buffers *p, int bph, double la)
void process(struct processing_buffers *p, int bph, double la, int light)
{
prepare_data(p,1);
prepare_data(p, !light);
p->ready = !compute_period(p,bph);
if(p->period >= p->sample_rate / 2) {
if(p->ready && p->period >= p->sample_rate / 2) {
debug("Detected period too long\n");
p->ready = 0;
}
@ -763,6 +806,6 @@ int process_cal(struct processing_buffers *p, struct calibration_data *cd)
if(add_sample_cal(p,cd))
return 1;
if(cd->wp == cd->size && cd->state == 0)
compute_cal(cd, p->sample_rate);
compute_cal(cd);
return 0;
}

View file

@ -24,13 +24,16 @@ int write_pointer = 0;
uint64_t timestamp = 0;
pthread_mutex_t audio_mutex;
int paudio_callback(const void *input_buffer,
void *output_buffer,
unsigned long frame_count,
const PaStreamCallbackTimeInfo *time_info,
PaStreamCallbackFlags status_flags,
void *data)
static int paudio_callback(const void *input_buffer,
void *output_buffer,
unsigned long frame_count,
const PaStreamCallbackTimeInfo *time_info,
PaStreamCallbackFlags status_flags,
void *data)
{
UNUSED(output_buffer);
UNUSED(time_info);
UNUSED(status_flags);
unsigned long i;
long channels = (long)data;
int wp = write_pointer;
@ -93,13 +96,8 @@ int start_portaudio(int *nominal_sample_rate, double *real_sample_rate)
goto error;
const PaStreamInfo *info = Pa_GetStreamInfo(stream);
#ifdef LIGHT
*nominal_sample_rate = PA_SAMPLE_RATE / 2;
*real_sample_rate = info->sampleRate / 2;
#else
*nominal_sample_rate = PA_SAMPLE_RATE;
*real_sample_rate = info->sampleRate;
#endif
#ifdef DEBUG
end:
#endif
@ -123,46 +121,36 @@ int terminate_portaudio()
return 0;
}
uint64_t get_timestamp()
uint64_t get_timestamp(int light)
{
pthread_mutex_lock(&audio_mutex);
#ifdef LIGHT
uint64_t ts = timestamp / 2;
#else
uint64_t ts = timestamp;
#endif
uint64_t ts = light ? timestamp / 2 : timestamp;
pthread_mutex_unlock(&audio_mutex);
return ts;
}
void fill_buffers(struct processing_buffers *p)
static void fill_buffers(struct processing_buffers *p, int light)
{
pthread_mutex_lock(&audio_mutex);
uint64_t ts = timestamp;
int wp = write_pointer;
pthread_mutex_unlock(&audio_mutex);
if(wp < 0 || wp >= PA_BUFF_SIZE) wp = 0;
#ifdef LIGHT
if(wp % 2) wp--;
ts /= 2;
#endif
if(light) {
if(wp % 2) wp--;
ts /= 2;
}
int i;
for(i=0; i<NSTEPS; i++) {
int j,k;
p[i].timestamp = ts;
#ifdef LIGHT
k = wp - 2*p[i].sample_count;
#else
k = wp - p[i].sample_count;
#endif
if(light) k = wp - 2*p[i].sample_count;
else k = wp - p[i].sample_count;
if(k < 0) k += PA_BUFF_SIZE;
for(j=0; j < p[i].sample_count; j++) {
p[i].samples[j] = pa_buffers[0][k] + pa_buffers[1][k];
#ifdef LIGHT
k += 2;
#else
k++;
#endif
k += light ? 2 : 1;
if(k >= PA_BUFF_SIZE) k -= PA_BUFF_SIZE;
}
}
@ -171,14 +159,14 @@ void fill_buffers(struct processing_buffers *p)
int analyze_pa_data(struct processing_data *pd, int bph, double la, uint64_t events_from)
{
struct processing_buffers *p = pd->buffers;
fill_buffers(p);
fill_buffers(p, pd->is_light);
int i;
debug("\nSTART OF COMPUTATION CYCLE\n\n");
for(i=0; i<NSTEPS; i++) {
p[i].last_tic = pd->last_tic;
p[i].events_from = events_from;
process(&p[i], bph, la);
process(&p[i], bph, la, pd->is_light);
if( !p[i].ready ) break;
debug("step %d : %f +- %f\n",i,p[i].period/p[i].sample_rate,p[i].sigma/p[i].sample_rate);
}
@ -193,7 +181,7 @@ int analyze_pa_data(struct processing_data *pd, int bph, double la, uint64_t eve
int analyze_pa_data_cal(struct processing_data *pd, struct calibration_data *cd)
{
struct processing_buffers *p = pd->buffers;
fill_buffers(p);
fill_buffers(p, pd->is_light);
int i,j;
debug("\nSTART OF CALIBRATION CYCLE\n\n");

304
src/computer.c Normal file
View file

@ -0,0 +1,304 @@
/*
tg
Copyright (C) 2015 Marcello Mamino
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "tg.h"
static int count_events(struct snapshot *s)
{
int i, cnt = 0;
if(!s->events_count) return 0;
for(i = s->events_wp; s->events[i];) {
cnt++;
if(--i < 0) i = s->events_count - 1;
if(i == s->events_wp) break;
}
return cnt;
}
struct snapshot *snapshot_clone(struct snapshot *s)
{
struct snapshot *t = malloc(sizeof(struct snapshot));
memcpy(t,s,sizeof(struct snapshot));
if(s->pb) t->pb = pb_clone(s->pb);
t->events_count = count_events(s);
if(t->events_count) {
t->events_wp = t->events_count - 1;
t->events = malloc(t->events_count * sizeof(uint64_t));
int i, j;
for(i = t->events_wp, j = s->events_wp; i >= 0; i--) {
t->events[i] = s->events[j];
if(--j < 0) j = s->events_count - 1;
}
} else {
t->events_wp = 0;
t->events = NULL;
}
return t;
}
void snapshot_destroy(struct snapshot *s)
{
if(s->pb) pb_destroy_clone(s->pb);
free(s->events);
free(s);
}
static int guess_bph(double period)
{
double bph = 7200 / period;
double min = bph;
int i,ret;
ret = 0;
for(i=0; preset_bph[i]; i++) {
double diff = fabs(bph - preset_bph[i]);
if(diff < min) {
min = diff;
ret = i;
}
}
return preset_bph[ret];
}
static void compute_update_cal(struct computer *c)
{
c->actv->signal = analyze_pa_data_cal(c->pdata, c->cdata);
if(c->actv->pb) {
pb_destroy_clone(c->actv->pb);
c->actv->pb = NULL;
}
c->actv->cal_state = c->cdata->state;
c->actv->cal_percent = 100*c->cdata->wp/c->cdata->size;
if(c->cdata->state == 1)
c->actv->cal_result = round(10 * c->cdata->calibration);
}
static void compute_update(struct computer *c)
{
int signal = analyze_pa_data(c->pdata, c->actv->bph, c->actv->la, c->actv->events_from);
struct processing_buffers *p = c->pdata->buffers;
int i;
for(i=0; i<NSTEPS && p[i].ready; i++);
for(i--; i>=0 && p[i].sigma > p[i].period / 10000; i--);
if(i>=0) {
if(c->actv->pb) pb_destroy_clone(c->actv->pb);
c->actv->pb = pb_clone(&p[i]);
c->actv->is_old = 0;
c->actv->signal = i == NSTEPS-1 && p[i].amp < 0 ? signal-1 : signal;
} else {
c->actv->is_old = 1;
c->actv->signal = -signal;
}
}
static void compute_events_cal(struct computer *c)
{
struct calibration_data *d = c->cdata;
struct snapshot *s = c->actv;
int i;
for(i=d->wp-1; i >= 0 &&
d->events[i] > s->events[s->events_wp];
i--);
for(i++; i<d->wp; i++) {
if(d->events[i] / s->nominal_sr <= s->events[s->events_wp] / s->nominal_sr)
continue;
if(++s->events_wp == s->events_count) s->events_wp = 0;
s->events[s->events_wp] = d->events[i];
debug("event at %llu\n",s->events[s->events_wp]);
}
s->events_from = get_timestamp(s->is_light);
}
static void compute_events(struct computer *c)
{
struct snapshot *s = c->actv;
struct processing_buffers *p = c->actv->pb;
if(p && !s->is_old) {
uint64_t last = s->events[s->events_wp];
int i;
for(i=0; i<EVENTS_MAX && p->events[i]; i++)
if(p->events[i] > last + floor(p->period / 4)) {
if(++s->events_wp == s->events_count) s->events_wp = 0;
s->events[s->events_wp] = p->events[i];
debug("event at %llu\n",s->events[s->events_wp]);
}
s->events_from = p->timestamp - ceil(p->period);
} else {
s->events_from = get_timestamp(s->is_light);
}
}
void compute_results(struct snapshot *s)
{
s->sample_rate = s->nominal_sr * (1 + (double) s->cal / (10 * 3600 * 24));
if(s->pb) {
s->guessed_bph = s->bph ? s->bph : guess_bph(s->pb->period / s->sample_rate);
s->rate = (7200/(s->guessed_bph * s->pb->period / s->sample_rate) - 1)*24*3600;
s->be = fabs(s->pb->be) * 1000 / s->sample_rate;
s->amp = s->la * s->pb->amp; // 0 = not available
if(s->amp < 135 || s->amp > 360)
s->amp = 0;
} else
s->guessed_bph = s->bph ? s->bph : DEFAULT_BPH;
}
static void *computing_thread(void *void_computer)
{
struct computer *c = void_computer;
for(;;) {
pthread_mutex_lock(&c->mutex);
while(!c->recompute)
pthread_cond_wait(&c->cond, &c->mutex);
if(c->recompute > 0) c->recompute = 0;
int calibrate = c->calibrate;
c->actv->bph = c->bph;
c->actv->la = c->la;
void (*callback)(void *) = c->callback;
void *callback_data = c->callback_data;
pthread_mutex_unlock(&c->mutex);
if(c->recompute < 0) {
if(callback) callback(callback_data);
break;
}
if(calibrate && !c->actv->calibrate) {
c->cdata->wp = 0;
c->cdata->state = 0;
c->actv->cal_state = 0;
c->actv->cal_percent = 0;
}
if(calibrate != c->actv->calibrate)
memset(c->actv->events,0,c->actv->events_count*sizeof(uint64_t));
c->actv->calibrate = calibrate;
if(c->actv->calibrate) {
compute_update_cal(c);
compute_events_cal(c);
} else {
compute_update(c);
compute_events(c);
}
pthread_mutex_lock(&c->mutex);
if(c->curr)
snapshot_destroy(c->curr);
if(c->clear_trace) {
if(!calibrate)
memset(c->actv->events,0,c->actv->events_count*sizeof(uint64_t));
c->clear_trace = 0;
}
c->curr = snapshot_clone(c->actv);
pthread_mutex_unlock(&c->mutex);
if(callback) callback(callback_data);
}
debug("Terminating computation thread\n");
return NULL;
}
void computer_destroy(struct computer *c)
{
int i;
for(i=0; i<NSTEPS; i++)
pb_destroy(&c->pdata->buffers[i]);
free(c->pdata->buffers);
free(c->pdata);
cal_data_destroy(c->cdata);
free(c->cdata);
snapshot_destroy(c->actv);
if(c->curr)
snapshot_destroy(c->curr);
pthread_mutex_destroy(&c->mutex);
pthread_cond_destroy(&c->cond);
pthread_join(c->thread, NULL);
free(c);
}
struct computer *start_computer(int nominal_sr, int bph, double la, int cal, int light)
{
if(light) nominal_sr /= 2;
struct processing_buffers *p = malloc(NSTEPS * sizeof(struct processing_buffers));
int first_step = light ? FIRST_STEP_LIGHT : FIRST_STEP;
int i;
for(i=0; i<NSTEPS; i++) {
p[i].sample_rate = nominal_sr;
p[i].sample_count = nominal_sr * (1<<(i+first_step));
setup_buffers(&p[i]);
}
struct processing_data *pd = malloc(sizeof(struct processing_data));
pd->buffers = p;
pd->last_tic = 0;
pd->is_light = light;
struct calibration_data *cd = malloc(sizeof(struct calibration_data));
setup_cal_data(cd);
struct snapshot *s = malloc(sizeof(struct snapshot));
s->timestamp = 0;
s->nominal_sr = nominal_sr;
s->pb = NULL;
s->is_old = 1;
s->calibrate = 0;
s->signal = 0;
s->events_count = EVENTS_COUNT;
s->events = malloc(EVENTS_COUNT * sizeof(uint64_t));
memset(s->events,0,EVENTS_COUNT * sizeof(uint64_t));
s->events_wp = 0;
s->events_from = 0;
s->trace_centering = 0;
s->bph = bph;
s->la = la;
s->cal = cal;
s->is_light = light;
struct computer *c = malloc(sizeof(struct computer));
c->cdata = cd;
c->pdata = pd;
c->actv = s;
c->curr = snapshot_clone(s);
c->recompute = 0;
c->calibrate = 0;
c->clear_trace = 0;
if( pthread_mutex_init(&c->mutex, NULL)
|| pthread_cond_init(&c->cond, NULL)
|| pthread_create(&c->thread, NULL, computing_thread, c)) {
error("Unable to initialize computing thread");
return NULL;
}
return c;
}
void lock_computer(struct computer *c)
{
pthread_mutex_lock(&c->mutex);
}
void unlock_computer(struct computer *c)
{
if(c->recompute)
pthread_cond_signal(&c->cond);
pthread_mutex_unlock(&c->mutex);
}

View file

@ -19,7 +19,7 @@
#include "tg.h"
#if !GLIB_CHECK_VERSION(2,40,0)
gboolean
static gboolean
g_key_file_save_to_file (GKeyFile *key_file,
const gchar *filename,
GError **error)
@ -113,3 +113,10 @@ void save_on_change(struct main_window *w)
CONFIG_FIELDS(CHANGED);
}
void close_config(struct main_window *w)
{
g_key_file_free(w->config_file);
g_free(w->config_file_name);
free(w->conf_data);
}

File diff suppressed because it is too large Load diff

868
src/output_panel.c Normal file
View file

@ -0,0 +1,868 @@
/*
tg
Copyright (C) 2015 Marcello Mamino
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "tg.h"
cairo_pattern_t *black,*white,*red,*green,*blue,*blueish,*yellow;
static void define_color(cairo_pattern_t **gc,double r,double g,double b)
{
*gc = cairo_pattern_create_rgb(r,g,b);
}
void initialize_palette()
{
define_color(&black,0,0,0);
define_color(&white,1,1,1);
define_color(&red,1,0,0);
define_color(&green,0,0.8,0);
define_color(&blue,0,0,1);
define_color(&blueish,0,0,.5);
define_color(&yellow,1,1,0);
}
static void draw_graph(double a, double b, cairo_t *c, struct processing_buffers *p, GtkWidget *da)
{
GtkAllocation temp;
gtk_widget_get_allocation (da, &temp);
int width = temp.width;
int height = temp.height;
int n;
int first = 1;
for(n=0; n<2*width; n++) {
int i = n < width ? n : 2*width - 1 - n;
double x = fmod(a + i * (b-a) / width, p->period);
if(x < 0) x += p->period;
int j = floor(x);
double y;
if(p->waveform[j] <= 0) y = 0;
else y = p->waveform[j] * 0.4 / p->waveform_max;
int k = round(y*height);
if(n < width) k = -k;
if(first) {
cairo_move_to(c,i+.5,height/2+k+.5);
first = 0;
} else
cairo_line_to(c,i+.5,height/2+k+.5);
}
}
#ifdef DEBUG
static void draw_debug_graph(double a, double b, cairo_t *c, struct processing_buffers *p, GtkWidget *da)
{
if(!p->debug) return;
GtkAllocation temp;
gtk_widget_get_allocation (da, &temp);
int width = temp.width;
int height = temp.height;
int i;
float max = 0;
int ai = round(a);
int bi = 1+round(b);
if(ai < 0) ai = 0;
if(bi > p->sample_count) bi = p->sample_count;
for(i=ai; i<bi; i++)
if(p->debug[i] > max)
max = p->debug[i];
int first = 1;
for(i=0; i<width; i++) {
if( round(a + i*(b-a)/width) != round(a + (i+1)*(b-a)/width) ) {
int j = round(a + i*(b-a)/width);
if(j < 0) j = 0;
if(j >= p->sample_count) j = p->sample_count-1;
int k = round((0.1+p->debug[j]/max)*0.8*height);
if(first) {
cairo_move_to(c,i+.5,height-k-.5);
first = 0;
} else
cairo_line_to(c,i+.5,height-k-.5);
}
}
}
#endif
static double amplitude_to_time(double lift_angle, double amp)
{
return asin(lift_angle / (2 * amp)) / M_PI;
}
static double draw_watch_icon(cairo_t *c, int signal, int happy, int light)
{
happy = !!happy;
cairo_set_line_width(c,3);
cairo_set_source(c,happy?green:red);
cairo_move_to(c, OUTPUT_WINDOW_HEIGHT * 0.5, OUTPUT_WINDOW_HEIGHT * 0.5);
cairo_line_to(c, OUTPUT_WINDOW_HEIGHT * 0.75, OUTPUT_WINDOW_HEIGHT * (0.75 - 0.5*happy));
cairo_move_to(c, OUTPUT_WINDOW_HEIGHT * 0.5, OUTPUT_WINDOW_HEIGHT * 0.5);
cairo_line_to(c, OUTPUT_WINDOW_HEIGHT * 0.35, OUTPUT_WINDOW_HEIGHT * (0.65 - 0.3*happy));
cairo_stroke(c);
cairo_arc(c, OUTPUT_WINDOW_HEIGHT * 0.5, OUTPUT_WINDOW_HEIGHT * 0.5, OUTPUT_WINDOW_HEIGHT * 0.4, 0, 2*M_PI);
cairo_stroke(c);
int l = OUTPUT_WINDOW_HEIGHT * 0.8 / (2*NSTEPS - 1);
int i;
cairo_set_line_width(c,1);
for(i = 0; i < signal; i++) {
cairo_move_to(c, OUTPUT_WINDOW_HEIGHT + 0.5*l, OUTPUT_WINDOW_HEIGHT * 0.9 - 2*i*l);
cairo_line_to(c, OUTPUT_WINDOW_HEIGHT + 1.5*l, OUTPUT_WINDOW_HEIGHT * 0.9 - 2*i*l);
cairo_line_to(c, OUTPUT_WINDOW_HEIGHT + 1.5*l, OUTPUT_WINDOW_HEIGHT * 0.9 - (2*i+1)*l);
cairo_line_to(c, OUTPUT_WINDOW_HEIGHT + 0.5*l, OUTPUT_WINDOW_HEIGHT * 0.9 - (2*i+1)*l);
cairo_line_to(c, OUTPUT_WINDOW_HEIGHT + 0.5*l, OUTPUT_WINDOW_HEIGHT * 0.9 - 2*i*l);
cairo_stroke_preserve(c);
cairo_fill(c);
}
if(light) {
int l = OUTPUT_WINDOW_HEIGHT * 0.15;
cairo_set_line_width(c,2);
cairo_move_to(c, OUTPUT_WINDOW_HEIGHT * 0.5 - 0.5*l, OUTPUT_WINDOW_HEIGHT * 0.2);
cairo_line_to(c, OUTPUT_WINDOW_HEIGHT * 0.5 - 0.5*l, OUTPUT_WINDOW_HEIGHT * 0.2 + l);
cairo_line_to(c, OUTPUT_WINDOW_HEIGHT * 0.5 , OUTPUT_WINDOW_HEIGHT * 0.2 + l);
cairo_move_to(c, OUTPUT_WINDOW_HEIGHT * 0.5 - 0.2*l, OUTPUT_WINDOW_HEIGHT * 0.2 + 1);
cairo_line_to(c, OUTPUT_WINDOW_HEIGHT * 0.5 + 0.8*l, OUTPUT_WINDOW_HEIGHT * 0.2 + 1);
cairo_move_to(c, OUTPUT_WINDOW_HEIGHT * 0.5 + 0.3*l, OUTPUT_WINDOW_HEIGHT * 0.2 + 1);
cairo_line_to(c, OUTPUT_WINDOW_HEIGHT * 0.5 + 0.3*l, OUTPUT_WINDOW_HEIGHT * 0.2 + l + 1);
cairo_stroke(c);
}
return OUTPUT_WINDOW_HEIGHT + 3*l;
}
static void cairo_init(cairo_t *c)
{
cairo_set_line_width(c,1);
cairo_set_source(c,black);
cairo_paint(c);
}
static double print_s(cairo_t *c, double x, double y, char *s)
{
cairo_text_extents_t extents;
cairo_move_to(c,x,y);
cairo_show_text(c,s);
cairo_text_extents(c,s,&extents);
x += extents.x_advance;
return x;
}
static double print_number(cairo_t *c, double x, double y, char *s)
{
cairo_text_extents_t extents;
cairo_text_extents(c,"0",&extents);
double z = extents.x_advance;
char t[2];
t[1] = 0;
while((t[0] = *s++)) {
cairo_text_extents(c,t,&extents);
cairo_move_to(c, x + (z - extents.x_advance) / 2, y);
cairo_show_text(c,t);
x += z;
}
return x;
}
static gboolean output_draw_event(GtkWidget *widget, cairo_t *c, struct output_panel *op)
{
UNUSED(widget);
cairo_init(c);
struct snapshot *snst = op->snst;
struct processing_buffers *p = snst->pb;
int old = snst->is_old;
double x = draw_watch_icon(c,snst->signal,snst->calibrate ? snst->signal==NSTEPS : snst->signal, snst->is_light);
cairo_text_extents_t extents;
cairo_set_font_size(c, OUTPUT_FONT);
cairo_text_extents(c,"0",&extents);
double y = (double)OUTPUT_WINDOW_HEIGHT/2 - extents.y_bearing - extents.height/2;
if(snst->calibrate) {
cairo_set_source(c, white);
x = print_s(c,x,y,"cal");
cairo_set_font_size(c, OUTPUT_FONT*2/3);
x = print_s(c,x,y," (");
cairo_move_to(c,x,y);
{
double a = 0;
char *s[] = {"wait", "acq.", "done", "fail", NULL}, **t = s;
for(;*t;t++) {
cairo_text_extents(c,*t,&extents);
if(a < extents.x_advance) a = extents.x_advance;
}
x += a;
}
switch(snst->cal_state) {
case 1:
cairo_set_source(c,green);
cairo_show_text(c,"done");
break;
case 0:
cairo_set_source(c, snst->signal == NSTEPS ? white : yellow);
cairo_show_text(c, snst->signal == NSTEPS ? "acq." : "wait");
break;
case -1:
cairo_set_source(c,red);
cairo_show_text(c,"fail");
break;
}
cairo_set_source(c, white);
x = print_s(c,x,y,")");
cairo_set_font_size(c, OUTPUT_FONT);
char s[20];
switch(snst->cal_state) {
case 1:
sprintf(s, " %s%d.%d",
snst->cal_result < 0 ? "-" : "+",
abs(snst->cal_result) / 10,
abs(snst->cal_result) % 10 );
x = print_s(c,x,y,s);
cairo_set_font_size(c, OUTPUT_FONT*2/3);
x = print_s(c,x,y," s/d");
break;
case 0:
sprintf(s, " %d", snst->cal_percent);
x = print_number(c,x,y,s);
x = print_s(c,x,y," %");
break;
}
} else {
char outputs[8][20];
if(p) {
int rate = round(snst->rate);
double be = snst->be;
char rates[20];
sprintf(rates,"%s%d",rate > 0 ? "+" : rate < 0 ? "-" : "",abs(rate));
sprintf(outputs[0],"%4s",rates);
sprintf(outputs[2]," %4.1f",be);
if(snst->amp > 0)
sprintf(outputs[4]," %3.0f",snst->amp);
else
strcpy(outputs[4]," ---");
} else {
strcpy(outputs[0],"----");
strcpy(outputs[2]," ----");
strcpy(outputs[4]," ---");
}
sprintf(outputs[6]," %d",snst->guessed_bph);
strcpy(outputs[1]," s/d");
strcpy(outputs[3]," ms");
strcpy(outputs[5]," deg");
strcpy(outputs[7]," bph");
int i;
for(i=0; i<8; i++) {
if(i%2) {
cairo_set_source(c, white);
cairo_set_font_size(c, OUTPUT_FONT*2/3);
x = print_s(c,x,y,outputs[i]);
} else {
cairo_set_source(c, i > 4 || !p || !old ? white : yellow);
cairo_set_font_size(c, OUTPUT_FONT);
x = print_number(c,x,y,outputs[i]);
}
}
}
#ifdef DEBUG
{
static GTimer *timer = NULL;
if (!timer) timer = g_timer_new();
else {
char s[100];
sprintf(s," %.2f fps",1./g_timer_elapsed(timer, NULL));
cairo_set_source(c, white);
cairo_set_font_size(c, OUTPUT_FONT);
x = print_s(c,x,y,s);
g_timer_reset(timer);
}
}
#endif
return FALSE;
}
static void expose_waveform(
struct output_panel *op,
GtkWidget *da,
cairo_t *c,
int (*get_offset)(struct processing_buffers*),
double (*get_pulse)(struct processing_buffers*))
{
cairo_init(c);
GtkAllocation temp;
gtk_widget_get_allocation(da, &temp);
int width = temp.width;
int height = temp.height;
gtk_widget_get_allocation(gtk_widget_get_toplevel(da), &temp);
int font = temp.width / 90;
if(font < 12)
font = 12;
int i;
cairo_set_font_size(c,font);
for(i = 1-NEGATIVE_SPAN; i < POSITIVE_SPAN; i++) {
int x = (NEGATIVE_SPAN + i) * width / (POSITIVE_SPAN + NEGATIVE_SPAN);
cairo_move_to(c, x + .5, height / 2 + .5);
cairo_line_to(c, x + .5, height - .5);
if(i%5)
cairo_set_source(c,green);
else
cairo_set_source(c,red);
cairo_stroke(c);
}
cairo_set_source(c,white);
for(i = 1-NEGATIVE_SPAN; i < POSITIVE_SPAN; i++) {
if(!(i%5)) {
int x = (NEGATIVE_SPAN + i) * width / (POSITIVE_SPAN + NEGATIVE_SPAN);
char s[10];
sprintf(s,"%d",i);
cairo_move_to(c,x+font/4,height-font/2);
cairo_show_text(c,s);
}
}
cairo_text_extents_t extents;
cairo_text_extents(c,"ms",&extents);
cairo_move_to(c,width - extents.x_advance - font/4,height-font/2);
cairo_show_text(c,"ms");
struct snapshot *snst = op->snst;
struct processing_buffers *p = snst->pb;
int old = snst->is_old;
double period = p ? p->period / snst->sample_rate : 7200. / snst->guessed_bph;
for(i = 10; i < 360; i+=10) {
if(2*i < snst->la) continue;
double t = period*amplitude_to_time(snst->la,i);
if(t > .001 * NEGATIVE_SPAN) continue;
int x = round(width * (NEGATIVE_SPAN - 1000*t) / (NEGATIVE_SPAN + POSITIVE_SPAN));
cairo_move_to(c, x+.5, .5);
cairo_line_to(c, x+.5, height / 2 + .5);
if(i % 50)
cairo_set_source(c,green);
else
cairo_set_source(c,red);
cairo_stroke(c);
}
double last_x = 0;
cairo_set_source(c,white);
for(i = 50; i < 360; i+=50) {
double t = period*amplitude_to_time(snst->la,i);
if(t > .001 * NEGATIVE_SPAN) continue;
int x = round(width * (NEGATIVE_SPAN - 1000*t) / (NEGATIVE_SPAN + POSITIVE_SPAN));
if(x > last_x) {
char s[10];
sprintf(s,"%d",abs(i));
cairo_move_to(c, x + font/4, font * 3 / 2);
cairo_show_text(c,s);
cairo_text_extents(c,s,&extents);
last_x = x + font/4 + extents.x_advance;
}
}
cairo_text_extents(c,"deg",&extents);
cairo_move_to(c,width - extents.x_advance - font/4,font * 3 / 2);
cairo_show_text(c,"deg");
if(p) {
double span = 0.001 * snst->sample_rate;
int offset = get_offset(p);
double a = offset - span * NEGATIVE_SPAN;
double b = offset + span * POSITIVE_SPAN;
draw_graph(a,b,c,p,da);
cairo_set_source(c,old?yellow:white);
cairo_stroke_preserve(c);
cairo_fill(c);
double pulse = get_pulse(p);
if(pulse > 0) {
int x = round((NEGATIVE_SPAN - pulse / span) * width / (POSITIVE_SPAN + NEGATIVE_SPAN));
cairo_move_to(c, x, 1);
cairo_line_to(c, x, height - 1);
cairo_set_source(c,blue);
cairo_set_line_width(c,2);
cairo_stroke(c);
}
} else {
cairo_move_to(c, .5, height / 2 + .5);
cairo_line_to(c, width - .5, height / 2 + .5);
cairo_set_source(c,yellow);
cairo_stroke(c);
}
}
static int get_tic(struct processing_buffers *p)
{
return p->tic;
}
static int get_toc(struct processing_buffers *p)
{
return p->toc;
}
static double get_tic_pulse(struct processing_buffers *p)
{
return p->tic_pulse;
}
static double get_toc_pulse(struct processing_buffers *p)
{
return p->toc_pulse;
}
static gboolean tic_draw_event(GtkWidget *widget, cairo_t *c, struct output_panel *op)
{
UNUSED(widget);
expose_waveform(op, op->tic_drawing_area, c, get_tic, get_tic_pulse);
return FALSE;
}
static gboolean toc_draw_event(GtkWidget *widget, cairo_t *c, struct output_panel *op)
{
UNUSED(widget);
expose_waveform(op, op->toc_drawing_area, c, get_toc, get_toc_pulse);
return FALSE;
}
static gboolean period_draw_event(GtkWidget *widget, cairo_t *c, struct output_panel *op)
{
UNUSED(widget);
cairo_init(c);
GtkAllocation temp;
gtk_widget_get_allocation (op->period_drawing_area, &temp);
int width = temp.width;
int height = temp.height;
struct snapshot *snst = op->snst;
struct processing_buffers *p = snst->pb;
int old = snst->is_old;
double toc,a=0,b=0;
if(p) {
toc = p->tic < p->toc ? p->toc : p->toc + p->period;
a = ((double)p->tic + toc)/2 - p->period/2;
b = ((double)p->tic + toc)/2 + p->period/2;
cairo_move_to(c, (p->tic - a - NEGATIVE_SPAN*.001*snst->sample_rate) * width/p->period, 0);
cairo_line_to(c, (p->tic - a - NEGATIVE_SPAN*.001*snst->sample_rate) * width/p->period, height);
cairo_line_to(c, (p->tic - a + POSITIVE_SPAN*.001*snst->sample_rate) * width/p->period, height);
cairo_line_to(c, (p->tic - a + POSITIVE_SPAN*.001*snst->sample_rate) * width/p->period, 0);
cairo_set_source(c,blueish);
cairo_fill(c);
cairo_move_to(c, (toc - a - NEGATIVE_SPAN*.001*snst->sample_rate) * width/p->period, 0);
cairo_line_to(c, (toc - a - NEGATIVE_SPAN*.001*snst->sample_rate) * width/p->period, height);
cairo_line_to(c, (toc - a + POSITIVE_SPAN*.001*snst->sample_rate) * width/p->period, height);
cairo_line_to(c, (toc - a + POSITIVE_SPAN*.001*snst->sample_rate) * width/p->period, 0);
cairo_set_source(c,blueish);
cairo_fill(c);
}
int i;
for(i = 1; i < 16; i++) {
int x = i * width / 16;
cairo_move_to(c, x+.5, .5);
cairo_line_to(c, x+.5, height - .5);
if(i % 4)
cairo_set_source(c,green);
else
cairo_set_source(c,red);
cairo_stroke(c);
}
if(p) {
draw_graph(a,b,c,p,op->period_drawing_area);
cairo_set_source(c,old?yellow:white);
cairo_stroke_preserve(c);
cairo_fill(c);
} else {
cairo_move_to(c, .5, height / 2 + .5);
cairo_line_to(c, width - .5, height / 2 + .5);
cairo_set_source(c,yellow);
cairo_stroke(c);
}
return FALSE;
}
static gboolean paperstrip_draw_event(GtkWidget *widget, cairo_t *c, struct output_panel *op)
{
int i;
struct snapshot *snst = op->snst;
uint64_t time = snst->timestamp ? snst->timestamp : get_timestamp(snst->is_light);
double sweep;
int zoom_factor;
double slope = 1000; // detected rate: 1000 -> do not display
if(snst->calibrate) {
sweep = snst->nominal_sr;
zoom_factor = PAPERSTRIP_ZOOM_CAL;
slope = (double) snst->cal * zoom_factor / (10 * 3600 * 24);
} else {
sweep = snst->sample_rate * 3600. / snst->guessed_bph;
zoom_factor = PAPERSTRIP_ZOOM;
if(snst->events_count && snst->events[snst->events_wp])
slope = - snst->rate * zoom_factor / (3600. * 24.);
}
cairo_init(c);
GtkAllocation temp;
gtk_widget_get_allocation (op->paperstrip_drawing_area, &temp);
int width = temp.width;
int height = temp.height;
int stopped = 0;
if( snst->events_count &&
snst->events[snst->events_wp] &&
time > 5 * snst->nominal_sr + snst->events[snst->events_wp]) {
time = 5 * snst->nominal_sr + snst->events[snst->events_wp];
stopped = 1;
}
int strip_width = round(width / (1 + PAPERSTRIP_MARGIN));
cairo_set_line_width(c,1.3);
slope *= strip_width;
if(slope <= 2 && slope >= -2) {
for(i=0; i<4; i++) {
double y = 0;
cairo_move_to(c, (double)width * (i+.5) / 4, 0);
for(;;) {
double x = y * slope + (double)width * (i+.5) / 4;
x = fmod(x, width);
if(x < 0) x += width;
double nx = x + slope * (height - y);
if(nx >= 0 && nx <= width) {
cairo_line_to(c, nx, height);
break;
} else {
double d = slope > 0 ? width - x : x;
y += d / fabs(slope);
cairo_line_to(c, slope > 0 ? width : 0, y);
y += 1;
if(y > height) break;
cairo_move_to(c, slope > 0 ? 0 : width, y);
}
}
}
cairo_set_source(c, blue);
cairo_stroke(c);
}
cairo_set_line_width(c,1);
int left_margin = (width - strip_width) / 2;
int right_margin = (width + strip_width) / 2;
cairo_move_to(c, left_margin + .5, .5);
cairo_line_to(c, left_margin + .5, height - .5);
cairo_move_to(c, right_margin + .5, .5);
cairo_line_to(c, right_margin + .5, height - .5);
cairo_set_source(c, green);
cairo_stroke(c);
double now = sweep*ceil(time/sweep);
double ten_s = snst->sample_rate * 10 / sweep;
double last_line = fmod(now/sweep, ten_s);
int last_tenth = floor(now/(sweep*ten_s));
for(i=0;;i++) {
double y = 0.5 + round(last_line + i*ten_s);
if(y > height) break;
cairo_move_to(c, .5, y);
cairo_line_to(c, width-.5, y);
cairo_set_source(c, (last_tenth-i)%6 ? green : red);
cairo_stroke(c);
}
cairo_set_source(c,stopped?yellow:white);
for(i = snst->events_wp;;) {
if(!snst->events_count || !snst->events[i]) break;
double event = now - snst->events[i] + snst->trace_centering + sweep * PAPERSTRIP_MARGIN / (2 * zoom_factor);
int column = floor(fmod(event, (sweep / zoom_factor)) * strip_width / (sweep / zoom_factor));
int row = floor(event / sweep);
if(row >= height) break;
cairo_move_to(c,column,row);
cairo_line_to(c,column+1,row);
cairo_line_to(c,column+1,row+1);
cairo_line_to(c,column,row+1);
cairo_line_to(c,column,row);
cairo_fill(c);
if(column < width - strip_width && row > 0) {
column += strip_width;
row -= 1;
cairo_move_to(c,column,row);
cairo_line_to(c,column+1,row);
cairo_line_to(c,column+1,row+1);
cairo_line_to(c,column,row+1);
cairo_line_to(c,column,row);
cairo_fill(c);
}
if(--i < 0) i = snst->events_count - 1;
if(i == snst->events_wp) break;
}
cairo_set_source(c,white);
cairo_set_line_width(c,2);
cairo_move_to(c, left_margin + 3, height - 20.5);
cairo_line_to(c, right_margin - 3, height - 20.5);
cairo_stroke(c);
cairo_set_line_width(c,1);
cairo_move_to(c, left_margin + .5, height - 20.5);
cairo_line_to(c, left_margin + 5.5, height - 15.5);
cairo_line_to(c, left_margin + 5.5, height - 25.5);
cairo_line_to(c, left_margin + .5, height - 20.5);
cairo_fill(c);
cairo_move_to(c, right_margin + .5, height - 20.5);
cairo_line_to(c, right_margin - 4.5, height - 15.5);
cairo_line_to(c, right_margin - 4.5, height - 25.5);
cairo_line_to(c, right_margin + .5, height - 20.5);
cairo_fill(c);
char s[100];
cairo_text_extents_t extents;
gtk_widget_get_allocation(gtk_widget_get_toplevel(widget), &temp);
int font = temp.width / 90;
if(font < 12)
font = 12;
cairo_set_font_size(c,font);
sprintf(s, "%.1f ms", snst->calibrate ?
1000. / zoom_factor :
3600000. / (snst->guessed_bph * zoom_factor));
cairo_text_extents(c,s,&extents);
cairo_move_to(c, (width - extents.x_advance)/2, height - 30);
cairo_show_text(c,s);
return FALSE;
}
#ifdef DEBUG
static gboolean debug_draw_event(GtkWidget *widget, cairo_t *c, struct output_panel *op)
{
UNUSED(widget);
cairo_init(c);
struct snapshot *snst = op->snst;
struct processing_buffers *p;
if(snst->calibrate)
p = &op->computer->pdata->buffers[0];
else
p = snst->pb;
if(p) {
double a = snst->nominal_sr / 10;
double b = snst->nominal_sr * 2;
draw_debug_graph(a,b,c,p,op->debug_drawing_area);
cairo_set_source(c,snst->is_old?yellow:white);
cairo_stroke(c);
}
return FALSE;
}
#endif
static void handle_clear_trace(GtkButton *b, struct output_panel *op)
{
UNUSED(b);
if(op->computer) {
lock_computer(op->computer);
if(!op->snst->calibrate) {
memset(op->snst->events,0,op->snst->events_count*sizeof(uint64_t));
op->computer->clear_trace = 1;
}
unlock_computer(op->computer);
gtk_widget_queue_draw(op->paperstrip_drawing_area);
}
}
static void handle_center_trace(GtkButton *b, struct output_panel *op)
{
UNUSED(b);
struct snapshot *snst = op->snst;
uint64_t last_ev = snst->events[snst->events_wp];
double new_centering;
if(last_ev) {
double sweep;
if(snst->calibrate)
sweep = (double) snst->nominal_sr / PAPERSTRIP_ZOOM_CAL;
else
sweep = snst->sample_rate * 3600. / (PAPERSTRIP_ZOOM * snst->guessed_bph);
new_centering = fmod(last_ev + .5*sweep , sweep);
} else
new_centering = 0;
snst->trace_centering = new_centering;
gtk_widget_queue_draw(op->paperstrip_drawing_area);
}
static void shift_trace(struct output_panel *op, double direction)
{
struct snapshot *snst = op->snst;
double sweep;
if(snst->calibrate)
sweep = (double) snst->nominal_sr / PAPERSTRIP_ZOOM_CAL;
else
sweep = snst->sample_rate * 3600. / (PAPERSTRIP_ZOOM * snst->guessed_bph);
snst->trace_centering = fmod(snst->trace_centering + sweep * (1.+.1*direction), sweep);
gtk_widget_queue_draw(op->paperstrip_drawing_area);
}
static void handle_left(GtkButton *b, struct output_panel *op)
{
UNUSED(b);
shift_trace(op,-1);
}
static void handle_right(GtkButton *b, struct output_panel *op)
{
UNUSED(b);
shift_trace(op,1);
}
void op_set_snapshot(struct output_panel *op, struct snapshot *snst)
{
op->snst = snst;
gtk_widget_set_sensitive(op->clear_button, !snst->calibrate);
}
void op_set_border(struct output_panel *op, int i)
{
gtk_container_set_border_width(GTK_CONTAINER(op->panel), i);
}
void op_destroy(struct output_panel *op)
{
snapshot_destroy(op->snst);
free(op);
}
struct output_panel *init_output_panel(struct computer *comp, struct snapshot *snst, int border)
{
struct output_panel *op = malloc(sizeof(struct output_panel));
op->computer = comp;
op->snst = snst;
op->panel = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
gtk_container_set_border_width(GTK_CONTAINER(op->panel), border);
// Info area on top
op->output_drawing_area = gtk_drawing_area_new();
gtk_widget_set_size_request(op->output_drawing_area, 0, OUTPUT_WINDOW_HEIGHT);
gtk_box_pack_start(GTK_BOX(op->panel),op->output_drawing_area, FALSE, TRUE, 0);
g_signal_connect (op->output_drawing_area, "draw", G_CALLBACK(output_draw_event), op);
gtk_widget_set_events(op->output_drawing_area, GDK_EXPOSURE_MASK);
GtkWidget *hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
gtk_box_pack_start(GTK_BOX(op->panel), hbox2, TRUE, TRUE, 0);
GtkWidget *vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
gtk_box_pack_start(GTK_BOX(hbox2), vbox2, FALSE, TRUE, 0);
// Paperstrip
op->paperstrip_drawing_area = gtk_drawing_area_new();
gtk_widget_set_size_request(op->paperstrip_drawing_area, 300, 0);
gtk_box_pack_start(GTK_BOX(vbox2), op->paperstrip_drawing_area, TRUE, TRUE, 0);
g_signal_connect (op->paperstrip_drawing_area, "draw", G_CALLBACK(paperstrip_draw_event), op);
gtk_widget_set_events(op->paperstrip_drawing_area, GDK_EXPOSURE_MASK);
GtkWidget *hbox3 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
gtk_box_pack_start(GTK_BOX(vbox2), hbox3, FALSE, TRUE, 0);
// < button
GtkWidget *left_button = gtk_button_new_with_label("<");
gtk_box_pack_start(GTK_BOX(hbox3), left_button, TRUE, TRUE, 0);
g_signal_connect (left_button, "clicked", G_CALLBACK(handle_left), op);
// CLEAR button
if(comp) {
op->clear_button = gtk_button_new_with_label("Clear");
gtk_box_pack_start(GTK_BOX(hbox3), op->clear_button, TRUE, TRUE, 0);
g_signal_connect (op->clear_button, "clicked", G_CALLBACK(handle_clear_trace), op);
gtk_widget_set_sensitive(op->clear_button, !snst->calibrate);
}
// CENTER button
GtkWidget *center_button = gtk_button_new_with_label("Center");
gtk_box_pack_start(GTK_BOX(hbox3), center_button, TRUE, TRUE, 0);
g_signal_connect (center_button, "clicked", G_CALLBACK(handle_center_trace), op);
// > button
GtkWidget *right_button = gtk_button_new_with_label(">");
gtk_box_pack_start(GTK_BOX(hbox3), right_button, TRUE, TRUE, 0);
g_signal_connect (right_button, "clicked", G_CALLBACK(handle_right), op);
GtkWidget *vbox3 = gtk_box_new(GTK_ORIENTATION_VERTICAL,10);
gtk_box_pack_start(GTK_BOX(hbox2), vbox3, TRUE, TRUE, 0);
// Tic waveform area
op->tic_drawing_area = gtk_drawing_area_new();
gtk_box_pack_start(GTK_BOX(vbox3), op->tic_drawing_area, TRUE, TRUE, 0);
g_signal_connect (op->tic_drawing_area, "draw", G_CALLBACK(tic_draw_event), op);
gtk_widget_set_events(op->tic_drawing_area, GDK_EXPOSURE_MASK);
// Toc waveform area
op->toc_drawing_area = gtk_drawing_area_new();
gtk_box_pack_start(GTK_BOX(vbox3), op->toc_drawing_area, TRUE, TRUE, 0);
g_signal_connect (op->toc_drawing_area, "draw", G_CALLBACK(toc_draw_event), op);
gtk_widget_set_events(op->toc_drawing_area, GDK_EXPOSURE_MASK);
// Period waveform area
op->period_drawing_area = gtk_drawing_area_new();
gtk_box_pack_start(GTK_BOX(vbox3), op->period_drawing_area, TRUE, TRUE, 0);
g_signal_connect (op->period_drawing_area, "draw", G_CALLBACK(period_draw_event), op);
gtk_widget_set_events(op->period_drawing_area, GDK_EXPOSURE_MASK);
#ifdef DEBUG
op->debug_drawing_area = gtk_drawing_area_new();
gtk_box_pack_start(GTK_BOX(vbox3), op->debug_drawing_area, TRUE, TRUE, 0);
g_signal_connect (op->debug_drawing_area, "draw", G_CALLBACK(debug_draw_event), op);
gtk_widget_set_events(op->debug_drawing_area, GDK_EXPOSURE_MASK);
#endif
return op;
}

568
src/serializer.c Normal file
View file

@ -0,0 +1,568 @@
/*
tg
Copyright (C) 2015 Marcello Mamino
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "tg.h"
#include <inttypes.h>
#define LABEL_SIZE 64
#define XSTR(X) STR(X)
#define STR(X) #X
#define LABEL_SIZE_STR XSTR(LABEL_SIZE)
#ifdef _WIN32
// My own implementation of %a
// because I'm sick of bugs in __mingw_fprintf()
static char hex_digit(uint64_t n)
{
if(n < 10) return (char)n+'0';
else return (char)n-10+'a';
}
static int write_hex_double(FILE *f, double d)
{
uint64_t bd = *(uint64_t *)&d;
uint64_t mantissa = bd << 12;
uint64_t exponent = (bd >> 52) & 0x7ff;
uint64_t sign = bd >> 63;
// nan or inf = error
if(exponent == 0x7ff) return 1;
if(sign && fprintf(f, "-") < 0) return 1;
if(exponent == 0) {
if(mantissa) {
exponent = 1;
if(fprintf(f, "0x0") < 0) return 1;
} else {
return 0 > fprintf(f, "0x0p+0");
}
} else {
if(fprintf(f, "0x1") < 0) return 1;
}
if(mantissa && fprintf(f, ".") < 0) return 1;
for(; mantissa; mantissa <<= 4)
if(fprintf(f, "%c", hex_digit( mantissa >> 60 )) < 0) return 1;
int exp = (int)exponent - 1023;
if(fprintf(f, "p%c", exp < 0 ? '-' : '+') < 0) return 1;
return 0 > fprintf(f,"%d",abs(exp));
}
static int parse_hex_digit(char c)
{
if('0' <= c && c <= '9') return c - '0';
if('a' <= c && c <= 'f') return 10 + c - 'a';
if('A' <= c && c <= 'F') return 10 + c - 'A';
return -1;
}
static int parse_hex_double(FILE *f, double *d)
{
char s[64], *t = s;
if(1 != fscanf(f, "%63[0-9a-fA-F.xXpP+-]", s)) return 1;
double place = 1;
if(*t == '+') t++;
else if(*t == '-') {
place = -1;
t++;
}
if(*t != '0') return 1;
if(*++t != 'x' && *t != 'X') return 1;
int n = parse_hex_digit(*++t);
if(n < 0) return 1;
*d = place * n;
if(!*++t) return 0;
if(*t != '.') goto exponent;
while(*++t && *t != 'p' && *t != 'P') {
place /= 16;
n = parse_hex_digit(*t);
if(n < 0) return 1;
*d += place * n;
}
exponent:
if(*t) {
long int radix = strtol(t+1, &t, 10);
if(*t) return 1;
*d *= pow(2, radix);
}
return 0;
}
#endif
static int serialize_uint64_t(FILE *f, uint64_t x)
{
return 0 > fprintf(f, "I%"PRIu64";\n", x);
}
static int scan_uint64_t(FILE *f, uint64_t *x)
{
int n = 0;
return 1 != fscanf(f, " I%"SCNu64";%n", x, &n) || !n;
}
static int serialize_int64_t(FILE *f, int64_t x)
{
return 0 > fprintf(f, "I%"PRId64";\n", x);
}
static int scan_int64_t(FILE *f, int64_t *x)
{
int n = 0;
return 1 != fscanf(f, " I%"SCNd64";%n", x, &n) || !n;
}
static int serialize_int(FILE *f, int x)
{
return serialize_int64_t(f, x);
}
static int scan_int(FILE *f, int *x)
{
int64_t y;
if(scan_int64_t(f, &y) || y < INT_MIN || y > INT_MAX) return 1;
*x = y;
return 0;
}
static int serialize_double(FILE *f, double x)
{
#ifdef _WIN32
if(fprintf(f, "I") < 0) return 1;
if(write_hex_double(f, x)) return 1;
return 0 > fprintf(f, ";\n");
#else
return 0 > fprintf(f, "I%a;\n", x);
#endif
}
static int scan_double(FILE *f, double *x)
{
int n = 0;
#ifdef _WIN32
if( 0 != fscanf(f, " I%n", &n) || !n ) return 1;
if( parse_hex_double(f,x) ) return 1;
n = 0;
if( 0 != fscanf(f, ";%n", &n) || !n ) return 1;
return 0;
#else
return 1 != fscanf(f, " I%la;%n", x, &n) || !n;
#endif
}
static int serialize_float(FILE *f, float x)
{
return serialize_double(f, x);
}
static int scan_float(FILE *f, float *x)
{
double y;
if(scan_double(f, &y)) return 1;
*x = y;
return 0;
}
static int serialize_string(FILE *f, char *s)
{
if(0 > fprintf(f, "S%"PRIu64";", (uint64_t)strlen(s))) return 1;
return 0 > fprintf(f, "%s;\n", s);
}
static int scan_string(FILE *f, char **s, uint64_t max_l, uint64_t *len)
{
uint64_t l;
int n = 0;
if(1 != fscanf(f, " S%"SCNu64";%n", &l, &n) || !n) return 1;
if(max_l && l >= max_l) return 1;
if(!*s) *s = malloc(l+1);
if(l+1 != fread(*s, 1, l+1, f)) return 1;
if((*s)[l] != ';') return 1;
(*s)[l] = 0;
if(len) *len = l;
return 0;
}
static int serialize_uint64_t_array(FILE *f, uint64_t *a, uint64_t len)
{
uint64_t i;
if(0 > fprintf(f, "A%"PRIu64";\n", len)) return 1;
for(i = 0; i < len; i++)
if(serialize_uint64_t(f, a[i])) return 1;
return 0;
}
static int scan_uint64_t_array(FILE *f, uint64_t **a, uint64_t max_l, uint64_t *len)
{
uint64_t l,i;
int n = 0;
if(1 != fscanf(f, " A%"SCNu64";%n", &l, &n) || !n) return 1;
if(max_l && l > max_l) return 1;
if(!*a) *a = malloc(l*sizeof(uint64_t));
for(i = 0; i < l; i++)
if(scan_uint64_t(f, *a+i)) return 1;
if(len) *len = l;
return 0;
}
static int serialize_float_array(FILE *f, float *a, uint64_t len)
{
uint64_t i;
if(0 > fprintf(f, "A%"PRIu64";\n", len)) return 1;
for(i = 0; i < len; i++)
if(serialize_float(f, a[i])) return 1;
return 0;
}
static int scan_float_array(FILE *f, float **a, uint64_t max_l, uint64_t *len)
{
uint64_t l,i;
int n = 0;
if(1 != fscanf(f, " A%"SCNu64";%n", &l, &n) || !n) return 1;
if(max_l && l > max_l) return 1;
if(!*a) *a = malloc(l*sizeof(float));
for(i = 0; i < l; i++)
if(scan_float(f, *a+i)) return 1;
if(len) *len = l;
return 0;
}
static int make_label(FILE *f, char *l)
{
return 0 > fprintf(f, "L%s;\n", l);
}
static int scan_label(FILE *f, char *l)
{
int n = 0;
return 1 != fscanf(f, " L%" LABEL_SIZE_STR "[^;];%n", l, &n) || !n;
}
static int eat_object(FILE *f)
{
char c;
if(1 != fscanf(f, " %c", &c)) return 1;
uint64_t l;
int n = 0;
switch(c) {
case 'I':
return 0 != fscanf(f, "%*[^;];%n", &n) || !n;
case 'S':
if(1 != fscanf(f, "%"SCNu64";%n", &l, &n) || !n) return 1;
if(fseek(f, l, SEEK_CUR)) return 1;
return 1 != fscanf(f, "%c", &c) || c != ';';
case 'A':
if(1 != fscanf(f, "%"SCNu64";%n", &l, &n) || !n) return 1;
for(;l;l--) if(eat_object(f)) return 1;
return 0;
case 'T':
if(1 != fscanf(f, "%c", &c)) return 1;
if(c != ';') return 1;
for(;;) {
char b[LABEL_SIZE+1];
if(scan_label(f, b)) return 1;
if(!strcmp("__end__", b)) return 0;
if(eat_object(f)) return 1;
}
case 'U':
if(1 != fscanf(f, "%c", &c)) return 1;
if(c != ';') return 1;
char b[LABEL_SIZE+1];
if(scan_label(f, b)) return 1;
if(eat_object(f)) return 1;
return 0;
default:
return 1;
}
}
static int serialize_struct_begin(FILE *f)
{
return 0 > fprintf(f, "T;\n");
}
static int serialize_struct_end(FILE *f)
{
return make_label(f, "__end__");
}
static int serialize_union_begin(FILE *f, char *s)
{
if(fprintf(f, "U;\n") < 0) return 1;
return make_label(f, s);
}
#define SERIALIZE(T,A) { \
if(make_label(f, #A)) return 1; \
if(serialize_ ## T (f, s -> A)) return 1; \
}
static int serialize_snapshot(FILE *f, struct snapshot *s, char *name)
{
if(serialize_union_begin(f, "realtime-snapshot")) return 1;
if(serialize_struct_begin(f)) return 1;
if(name) {
if(make_label(f, "name")) return 1;
if(serialize_string(f, name)) return 1;
}
if(make_label(f, "pb->waveform")) return 1;
if(serialize_float_array(f, s->pb->waveform, s->pb->sample_count)) return 1;
if(make_label(f, "events")) return 1;
if(serialize_uint64_t_array(f, s->events, s->events_count)) return 1;
SERIALIZE(int,pb->sample_rate);
SERIALIZE(double,pb->period);
SERIALIZE(double,pb->waveform_max);
SERIALIZE(int,pb->tic);
SERIALIZE(int,pb->toc);
SERIALIZE(double,pb->tic_pulse);
SERIALIZE(double,pb->toc_pulse);
SERIALIZE(int,is_old);
SERIALIZE(uint64_t,timestamp);
SERIALIZE(int,nominal_sr);
SERIALIZE(int,bph);
SERIALIZE(double,la);
SERIALIZE(int,cal);
SERIALIZE(int,events_wp);
SERIALIZE(int,signal);
SERIALIZE(double,sample_rate);
SERIALIZE(int,guessed_bph);
SERIALIZE(double,rate);
SERIALIZE(double,be);
SERIALIZE(double,amp);
SERIALIZE(double,trace_centering);
SERIALIZE(int,is_light);
return serialize_struct_end(f);
}
#define SCAN(T,A) { \
if(!strcmp( #A , l )) { \
debug("serializer: scanning " #A "\n"); \
if(scan_ ## T (f, &((*s) -> A))) goto error; \
continue; \
}}
static int scan_snapshot(FILE *f, struct snapshot **s, char **name)
{
char l[LABEL_SIZE+1];
int n = 0;
*s = NULL;
*name = NULL;
if(0 != fscanf(f, " U;%n", &n) || !n) return 1;
if(scan_label(f,l)) return 1;
if(strcmp("realtime-snapshot", l))
return eat_object(f);
*s = malloc(sizeof(struct snapshot));
memset(*s, 0, sizeof(struct snapshot));
(*s)->pb = malloc(sizeof(struct processing_buffers));
memset((*s)->pb, 0, sizeof(struct processing_buffers));
*name = NULL;
n = 0;
if(0 != fscanf(f, " T;%n", &n) || !n) goto error;
for(;;) {
if(scan_label(f,l)) goto error;
if(!strcmp("__end__", l)) break;
if(!strcmp("name", l)) {
debug("serializer: scanning name\n");
if(scan_string(f, name, 0, NULL)) goto error;
continue;
}
if(!strcmp("pb->waveform", l)) {
debug("serializer: scanning pb->waveform\n");
uint64_t x;
if( (*s)->pb->waveform ||
scan_float_array(f, &((*s)->pb->waveform), INT_MAX, &x)) goto error;
(*s)->pb->sample_count = x;
continue;
}
if(!strcmp("events", l)) {
debug("serializer: scanning events\n");
uint64_t x;
if( (*s)->events ||
scan_uint64_t_array(f, &((*s)->events), INT_MAX, &x)) goto error;
(*s)->events_count = x;
continue;
}
SCAN(int,pb->sample_rate);
SCAN(double,pb->period);
SCAN(double,pb->waveform_max);
SCAN(int,pb->tic);
SCAN(int,pb->toc);
SCAN(double,pb->tic_pulse);
SCAN(double,pb->toc_pulse);
SCAN(int,is_old);
SCAN(uint64_t,timestamp);
SCAN(int,nominal_sr);
SCAN(int,bph);
SCAN(double,la);
SCAN(int,cal);
SCAN(int,events_wp);
SCAN(int,signal);
SCAN(double,sample_rate);
SCAN(int,guessed_bph);
SCAN(double,rate);
SCAN(double,be);
SCAN(double,amp);
SCAN(double,trace_centering);
SCAN(int,is_light);
if(eat_object(f)) goto error;
}
debug("serializer: checking period\n");
if((*s)->pb->period <= 0 || (*s)->pb->sample_count < ceil((*s)->pb->period)) goto error;
debug("serializer: checking timestamp\n");
if(!(*s)->timestamp) goto error;
debug("serializer: checking nominal_sr\n");
if(!(*s)->nominal_sr) goto error;
debug("serializer: checking bph\n");
if((*s)->bph && ( (*s)->bph < MIN_BPH || (*s)->bph > MAX_BPH )) goto error;
debug("serializer: checking la\n");
if((*s)->la < MIN_LA || (*s)->la > MAX_LA ) goto error;
debug("serializer: checking cal\n");
if((*s)->cal < MIN_CAL || (*s)->cal > MAX_CAL) goto error;
debug("serializer: checking events\n");
if((*s)->events_count && (*s)->events_wp >= (*s)->events_count) goto error;
if((*s)->signal > NSTEPS) (*s)->signal = NSTEPS;
debug("serializer: checking sample_rate\n");
if((*s)->sample_rate <= 0) goto error;
debug("serializer: checking guessed_bph\n");
if((*s)->guessed_bph < MIN_BPH || (*s)->guessed_bph > MAX_BPH) goto error;
debug("serializer: checking rate\n");
if((*s)->rate < -9999 || (*s)->rate > 9999) goto error;
debug("serializer: checking beat error\n");
if((*s)->be < 0 || (*s)->be > 99.9) goto error;
debug("serializer: checking amplitude\n");
if((*s)->amp < 0 || (*s)->amp > 360) goto error;
(*s)->pb->events = NULL;
#ifdef DEBUG
(*s)->pb->debug = NULL;
#endif
return 0;
error:
free(*name);
free((*s)->pb->waveform);
free((*s)->pb);
free((*s)->events);
free(*s);
*s = NULL;
*name = NULL;
return 1;
}
static int scan_snapshot_list(FILE *f, struct snapshot ***s, char ***names, uint64_t *cnt)
{
debug("serializer: scanning snapshot list\n");
uint64_t i;
int n = 0;
*s = NULL;
*names = NULL;
*cnt = 0;
if(1 != fscanf(f, " A%"SCNu64";%n", &i, &n) || !n) goto error;
*s = malloc(i*sizeof(struct snapshot *));
*names = malloc(i*sizeof(char *));
uint64_t j;
for(j = 0; j < i; j++) {
if(scan_snapshot(f, *s+*cnt, *names+*cnt)) goto error;
*cnt += !!(*s)[*cnt];
}
*s = realloc(*s, *cnt*sizeof(struct snapshot *));
*names = realloc(*names, *cnt*sizeof(char *));
return 0;
error:
debug("serializer: error in snapshot list\n");
for(; (*cnt)--;) {
snapshot_destroy((*s)[*cnt]);
free((*names)[*cnt]);
}
free(*s);
free(*names);
*s = NULL;
*names = NULL;
*cnt = 0;
return 1;
}
int write_file(FILE *f, struct snapshot **s, char **names, uint64_t cnt)
{
uint64_t i;
if(make_label(f, "tg-timer-version")) return 1;
if(serialize_string(f, VERSION)) return 1;
if(make_label(f, "data")) return 1;
if(serialize_struct_begin(f)) return 1;
if(make_label(f, "snapshot-list")) return 1;
if(0 > fprintf(f, "A%"PRIu64";\n", cnt)) return 1;
for(i = 0; i < cnt; i++)
if(serialize_snapshot(f, s[i], names[i])) return 1;
if(serialize_struct_end(f)) return 1;
return 0;
}
int read_file(FILE *f, struct snapshot ***s, char ***names, uint64_t *cnt)
{
debug("serializer: reading file\n");
char lbl[LABEL_SIZE+1];
char *l = lbl;
if(scan_label(f, l) || strcmp("tg-timer-version",l)) return 1;
if(scan_string(f, &l, LABEL_SIZE, NULL)) return 1;
debug("serializer: read version %s\n",l);
if(scan_label(f, l) || strcmp("data",l)) return 1;
debug("serializer: found data structure\n",l);
int n = 0;
if(0 != fscanf(f, " T;%n", &n) || !n) return 1;
*s = NULL;
*names = NULL;
for(;;) {
if(scan_label(f,l)) goto error;
if(!strcmp("__end__",l)) break;
if(!strcmp("snapshot-list",l)) {
if(*s || scan_snapshot_list(f, s, names, cnt)) goto error;
else continue;
}
if(eat_object(f)) goto error;
}
debug("serializer: end of data structure\n",l);
char c;
if(*s && 1 != fscanf(f, " %c", &c)) return 0;
#ifdef DEBUG
if(*s) {
debug("serializer: stray char %c (%d) after end\n", c, c);
} else {
debug("serializer: no snapshots\n");
}
#endif
error:
debug("serializer: read error\n");
if(*s) {
uint64_t i;
for(i = 0; i < *cnt; i++) {
snapshot_destroy((*s)[i]);
free((*names)[i]);
}
free(*s);
free(*names);
*s = NULL;
*names = NULL;
}
return 1;
}

165
src/tg.h
View file

@ -27,17 +27,18 @@
#include <gtk/gtk.h>
#include <pthread.h>
#ifdef __CYGWIN__
#define _WIN32
#endif
#define CONFIG_FILE_NAME "tg-timer.ini"
#define FILTER_CUTOFF 3000
#define CAL_DATA_SIZE 900
#ifdef LIGHT
#define FIRST_STEP 0
#else
#define FIRST_STEP 1
#endif
#define FIRST_STEP_LIGHT 0
#define NSTEPS 4
#define PA_SAMPLE_RATE 44100
@ -69,9 +70,11 @@
#ifdef DEBUG
#define debug(...) print_debug(__VA_ARGS__)
#else
#define debug(...)
#define debug(...) {}
#endif
#define UNUSED(X) (void)(X)
/* algo.c */
struct processing_buffers {
int sample_rate;
@ -88,6 +91,7 @@ struct processing_buffers {
uint64_t timestamp, last_tic, last_toc, events_from;
uint64_t *events;
#ifdef DEBUG
int debug_size;
float *debug;
#endif
};
@ -104,10 +108,12 @@ struct calibration_data {
};
void setup_buffers(struct processing_buffers *b);
void pb_destroy(struct processing_buffers *b);
struct processing_buffers *pb_clone(struct processing_buffers *p);
void pb_destroy_clone(struct processing_buffers *p);
void process(struct processing_buffers *p, int bph, double la);
void process(struct processing_buffers *p, int bph, double la, int light);
void setup_cal_data(struct calibration_data *cd);
void cal_data_destroy(struct calibration_data *cd);
int test_cal(struct processing_buffers *p);
int process_cal(struct processing_buffers *p, struct calibration_data *cd);
@ -115,62 +121,143 @@ int process_cal(struct processing_buffers *p, struct calibration_data *cd);
struct processing_data {
struct processing_buffers *buffers;
uint64_t last_tic;
int is_light;
};
int start_portaudio(int *nominal_sample_rate, double *real_sample_rate);
int terminate_portaudio();
uint64_t get_timestamp();
uint64_t get_timestamp(int light);
int analyze_pa_data(struct processing_data *pd, int bph, double la, uint64_t events_from);
int analyze_pa_data_cal(struct processing_data *pd, struct calibration_data *cd);
/* interface.c */
struct main_window {
GtkWidget *window;
/* computer.c */
struct snapshot {
struct processing_buffers *pb;
int is_old;
uint64_t timestamp;
int is_light;
int nominal_sr;
int calibrate;
int bph;
double la; // deg
int cal; // 0.1 s/d
int events_count;
uint64_t *events; // used in cal+timegrapher mode
int events_wp; // used in cal+timegrapher mode
uint64_t events_from; // used only in timegrapher mode
int signal;
int cal_state;
int cal_percent;
int cal_result; // 0.1 s/d
// data dependent on bph, la, cal
double sample_rate;
int guessed_bph;
double rate;
double be;
double amp;
double trace_centering;
};
struct computer {
pthread_t thread;
pthread_mutex_t mutex;
pthread_cond_t cond;
// controlled by interface
int recompute;
int calibrate;
int bph;
double la; // deg
int clear_trace;
void (*callback)(void *);
void *callback_data;
struct processing_data *pdata;
struct calibration_data *cdata;
struct snapshot *actv;
struct snapshot *curr;
};
struct snapshot *snapshot_clone(struct snapshot *s);
void snapshot_destroy(struct snapshot *s);
void computer_destroy(struct computer *c);
struct computer *start_computer(int nominal_sr, int bph, double la, int cal, int light);
void lock_computer(struct computer *c);
void unlock_computer(struct computer *c);
void compute_results(struct snapshot *s);
/* output_panel.c */
struct output_panel {
GtkWidget *panel;
GtkWidget *output_drawing_area;
GtkWidget *tic_drawing_area;
GtkWidget *toc_drawing_area;
GtkWidget *period_drawing_area;
GtkWidget *paperstrip_drawing_area;
GtkWidget *cal_spin_button;
GtkWidget *clear_button;
#ifdef DEBUG
GtkWidget *debug_drawing_area;
#endif
struct computer *computer;
struct snapshot *snst;
};
pthread_t computing_thread;
pthread_mutex_t recompute_mutex;
pthread_cond_t recompute_cond;
int recompute;
guint computer_kicker;
void initialize_palette();
struct output_panel *init_output_panel(struct computer *comp, struct snapshot *snst, int border);
void redraw_op(struct output_panel *op);
void op_set_snapshot(struct output_panel *op, struct snapshot *snst);
void op_set_border(struct output_panel *op, int i);
void op_destroy(struct output_panel *op);
/* interface.c */
struct main_window {
GtkApplication *app;
GtkWidget *window;
GtkWidget *bph_combo_box;
GtkWidget *la_spin_button;
GtkWidget *cal_spin_button;
GtkWidget *snapshot_button;
GtkWidget *snapshot_name;
GtkWidget *snapshot_name_entry;
GtkWidget *cal_button;
GtkWidget *notebook;
GtkWidget *save_item;
GtkWidget *save_all_item;
GtkWidget *close_all_item;
struct output_panel *active_panel;
struct computer *computer;
struct snapshot *active_snapshot;
int computer_timeout;
int is_light;
int zombie;
int controls_active;
int calibrate;
int calibrating;
int cal_updated;
struct processing_data *pdata;
struct processing_buffers *old;
struct calibration_data *cdata;
int is_old;
int bph;
int guessed_bph;
int last_bph;
double la; // deg
int cal; // 0.1 s/d
double sample_rate;
int nominal_sr;
uint64_t *events;
int events_wp;
uint64_t events_from;
double trace_centering;
int signal;
GKeyFile *config_file;
gchar *config_file_name;
struct conf_data *conf_data;
guint kick_timeout;
guint save_timeout;
};
extern int preset_bph[];
#ifdef DEBUG
extern int testing;
#endif
@ -178,11 +265,12 @@ extern int testing;
void print_debug(char *format,...);
void error(char *format,...);
// config.c
/* config.c */
#define CONFIG_FIELDS(OP) \
OP(bph, bph, int) \
OP(lift_angle, la, double) \
OP(calibration, cal, int)
OP(calibration, cal, int) \
OP(light_algorithm, is_light, int)
struct conf_data {
#define DEF(NAME,PLACE,TYPE) TYPE PLACE;
@ -192,3 +280,8 @@ struct conf_data {
void load_config(struct main_window *w);
void save_config(struct main_window *w);
void save_on_change(struct main_window *w);
void close_config(struct main_window *w);
/* serializer.c */
int write_file(FILE *f, struct snapshot **s, char **names, uint64_t cnt);
int read_file(FILE *f, struct snapshot ***s, char ***names, uint64_t *cnt);

View file

@ -1 +1 @@
0.4.0
0.5.0