Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
0ebea86
Add code for boolean config file options
xyzzy42 Feb 12, 2021
13269ee
Create utilities for menu items
xyzzy42 Feb 18, 2021
41ee57a
Break up the output panel GUI creation function
xyzzy42 Feb 18, 2021
4e68612
Use resizble paned widget for paperstrip and waveforms
xyzzy42 Feb 18, 2021
6db5467
Fix race with light mode switch and audio callback
xyzzy42 Jan 12, 2021
6664f84
Fix uninitialized variable warning in read_file()
xyzzy42 Mar 23, 2021
74f764f
Processes only need steps for large speedup
xyzzy42 Mar 31, 2021
87e0622
Draw partial signal dot for no amplitude
xyzzy42 Apr 1, 2021
2f1f7f9
Update m4/attributes.m4 to a newer upstream version
xyzzy42 Apr 8, 2021
53f6de7
Fix warning from m4/attributes.m4
xyzzy42 Apr 8, 2021
e1e4367
Fix issue preventing BPH below 14400 from working
xyzzy42 Feb 13, 2021
5fa7289
Lower min BPH to 8100
xyzzy42 Apr 13, 2021
22e2c68
Add an RPM spec file for building on Fedora, etc.
xyzzy42 Apr 3, 2021
05e85bc
Add vertical/horizontal paperstrip option
xyzzy42 Feb 18, 2021
6f088c0
Scale font using paperstrip/waveform window size
xyzzy42 Mar 8, 2021
9f1f263
Move snapshot display parameters to their own struct
xyzzy42 Mar 7, 2021
5e3212e
Size fonts based on drawing area
xyzzy42 Mar 17, 2021
5fc7d67
Use widget parameter instead of state in paperstrip expose
xyzzy42 Mar 17, 2021
7b81287
Add zoom slider for paperstrip
xyzzy42 Mar 18, 2021
0ab8b06
Redo paperstrip display, new dot algorithm
xyzzy42 Mar 18, 2021
cd45a9f
Fix missing initialization in snapshot
xyzzy42 Mar 22, 2021
a161e64
Don't draw slope when no rate is known
xyzzy42 Mar 23, 2021
a187e55
Hide zoom reset button when at original scale
xyzzy42 Mar 30, 2021
e6cc7a5
Round chart step up, rather than down, to beat length
xyzzy42 Mar 31, 2021
21965d1
Utility functions for gtk orientation
xyzzy42 Mar 31, 2021
fcf2399
Adjust zoom button for vertical/horizontal layout
xyzzy42 Mar 31, 2021
4282a1c
Read config file before starting audio
xyzzy42 Jan 12, 2021
cbd3a00
Change one sample sigma estimation to 0
xyzzy42 Apr 18, 2021
fbf47a8
Run high pass filter in audio thread
xyzzy42 Jan 9, 2021
5252757
Merge branch 'trentpi/autoconf-update'
xyzzy42 Apr 19, 2021
5e08001
Merge branch 'trentpi/spec-file'
xyzzy42 Apr 19, 2021
d3f1dd6
Merge branch 'trentpi/fix-low-bph'
xyzzy42 Apr 19, 2021
be536fa
Merge branch 'trentpi/hpf-audio-thread'
xyzzy42 Apr 19, 2021
e53d024
Merge branch 'trentpi/faster-processing'
xyzzy42 Apr 19, 2021
122c783
Merge branch 'trentpi/zoom'
xyzzy42 Apr 19, 2021
13e2743
Dynamically allocate sound buffer
xyzzy42 Mar 23, 2021
61e65b9
Add ARRAY_SIZE macro
xyzzy42 Mar 28, 2021
d2c24c0
Refactor audio opening code for runtime re-opening
xyzzy42 Feb 4, 2020
c664e90
Move HPF and callback info to audio context
xyzzy42 Apr 19, 2021
44ea8d4
Let audio module keep track of light mode and sample rate
xyzzy42 Apr 19, 2021
11f2fd9
Add interface for querying audio device list
xyzzy42 Mar 28, 2021
1b839ee
Add audio device and rate selection dialog
xyzzy42 Mar 28, 2021
0e74ea7
Save audio device and rate selection to config
xyzzy42 Mar 28, 2021
dc4679e
Add audio interface for setting/changing HPF cutoff frequency
xyzzy42 Apr 7, 2021
f2f39dd
Allow configuring HPF cutoff frequency from audio setup dialog
xyzzy42 Apr 6, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Makefile.in
*.log
*.trs
*.tar.gz
/packaging/tg-timer.spec

# build directories
/deb
Expand Down
3 changes: 2 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ LIBS = $(GTK_LIBS) \
AM_CPPFLAGS = -DPROGRAM_NAME=\"Tg\" -DVERSION=\"$(PACKAGE_VERSION)\"
tg_timer_dbg_CPPFLAGS = $(AM_CPPFLAGS) -DDEBUG

AM_CFLAGS = $(GTK_CFLAGS) \
AM_CFLAGS = $(WARNINGFLAGS) \
$(GTK_CFLAGS) \
$(GTHREAD_CFLAGS) \
$(PORTAUDIO_CFLAGS) \
$(FFTW_CFLAGS)
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ You can now launch tg by typing

Binary .deb packages can be downloaded from https://tg.ciovil.li

### Fedora, CentOS or other Redhat-based

Binary RPM packages are available from https://copr.fedorainfracloud.org/coprs/tpiepho/tg-timer/

This COPR repository can be added to dnf's list with:
```sh
dnf copr enable tpiepho/tg-timer
```
Then tg-timer can be installed with `dnf install tg-timer`, or with any dnf
based GUI package installer.

## Compiling from sources

The source code of tg can probably be built by any C99 compiler, however
Expand Down Expand Up @@ -107,3 +118,11 @@ cd tg
./configure
make
```

To build an RPM on Fedora or another RPM based distro, install the build
prerequisites and checkout the source as for compiling (above), then run
`rpmbuild` to create the RPM:

```sh
rpmbuild --build-in-place -bb packaging/tg-timer.spec
```
8 changes: 6 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ AC_INIT([Tg], [tg_version], [vacaboja@gmail.com], [tg-timer], [https://github.co
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])

AC_PROG_CC
AC_LANG(C)
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])
Expand All @@ -18,9 +19,12 @@ 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], [], [])
CC_CHECK_FLAGS_APPEND([with_cflags], [CFLAGS], [-Wall -Wextra])
AC_SUBST([WARNINGFLAGS], $with_cflags)

AC_OUTPUT([Makefile icons/Makefile])
AC_CONFIG_FILES([Makefile icons/Makefile])
AC_CONFIG_FILES([packaging/tg-timer.spec:packaging/tg-timer.inc.in:packaging/tg-timer.in.spec])
AC_OUTPUT

AC_MSG_RESULT([
$PACKAGE_NAME $VERSION
Expand Down
95 changes: 38 additions & 57 deletions m4/attributes.m4
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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 Copyright (c) 2012 Lucas De Marchi <lucas.de.marchi@gmail.com>
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
Expand Down Expand Up @@ -32,52 +33,32 @@ 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 Check if FLAG in ENV-VAR is supported by compiler and append it
dnl to WHERE-TO-APPEND variable. Note that we invert -Wno-* checks to
dnl -W* as gcc cannot test for negated warnings. If a C snippet is passed,
dnl use it, otherwise use a simple main() definition that just returns 0.
dnl CC_CHECK_FLAG_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG], [C-SNIPPET])

AC_DEFUN([CC_CHECK_FLAG_APPEND], [
AC_CACHE_CHECK([if $CC supports flag $3 in envvar $2],
AS_TR_SH([cc_cv_$2_$3]),
[eval "AS_TR_SH([cc_save_$2])='${$2}'"
eval "AS_TR_SH([$2])='${cc_save_$2} -Werror `echo "$3" | sed 's/^-Wno-/-W/'`'"
AC_LINK_IFELSE([AC_LANG_SOURCE(ifelse([$4], [],
[int main(void) { return 0; } ],
[$4]))],
[eval "AS_TR_SH([cc_cv_$2_$3])='yes'"],
[eval "AS_TR_SH([cc_cv_$2_$3])='no'"])
eval "AS_TR_SH([$2])='$cc_save_$2'"])

AS_IF([eval test x$]AS_TR_SH([cc_cv_$2_$3])[ = xyes],
[eval "$1='${$1} $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])
dnl CC_CHECK_FLAGS_APPEND([WHERE-TO-APPEND], [ENV-VAR], [FLAG1 FLAG2], [C-SNIPPET])
AC_DEFUN([CC_CHECK_FLAGS_APPEND], [
for flag in [$3]; do
CC_CHECK_FLAG_APPEND([$1], [$2], $flag, [$4])
done
])

Expand All @@ -89,7 +70,7 @@ AC_DEFUN([CC_CHECK_LDFLAGS], [
AS_TR_SH([cc_cv_ldflags_$1]),
[ac_save_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS $1"
AC_LINK_IFELSE([int main() { return 1; }],
AC_LINK_IFELSE([AC_LANG_SOURCE([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"
Expand All @@ -112,13 +93,13 @@ AC_DEFUN([CC_NOUNDEFINED], [
*-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 for a much more readable command line, 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
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])
Expand Down Expand Up @@ -147,7 +128,7 @@ AC_DEFUN([CC_CHECK_ATTRIBUTE], [
AS_TR_SH([cc_cv_attribute_$1]),
[ac_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $cc_cv_werror"
AC_COMPILE_IFELSE([$3],
AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])],
[eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"],
[eval "AS_TR_SH([cc_cv_attribute_$1])='no'"])
CFLAGS="$ac_save_CFLAGS"
Expand Down Expand Up @@ -254,8 +235,8 @@ AC_DEFUN([CC_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')
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"],
Expand All @@ -271,11 +252,11 @@ AC_DEFUN([CC_FUNC_EXPECT], [
[cc_cv_func_expect],
[ac_save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $cc_cv_werror"
AC_COMPILE_IFELSE(
AC_COMPILE_IFELSE([AC_LANG_SOURCE(
[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"
Expand All @@ -295,11 +276,11 @@ AC_DEFUN([CC_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([
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
int main() {
static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0;
return c;
}], [cc_cv_attribute_aligned=$cc_attribute_align_try; break])
}])], [cc_cv_attribute_aligned=$cc_attribute_align_try; break])
done
CFLAGS="$ac_save_CFLAGS"
])
Expand Down
80 changes: 80 additions & 0 deletions packaging/tg-timer.in.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# If the configure script is run, it will define the package version by
# prepending a definition of AC_VERSION to this spec.
# If rpkg preprocesses this file, as in a COPR build, then it will generate the
# package version from the "version" file and saved in the macro RPKG_VERSION.
# This does not require running the configure script, which does not happen on
# COPR.

# It's also possible to define the macro 'version' on the command line. The
# version used will be the first defined from 'version' macro, AC_VERSION, and
# then RPKG_VERSION.

# rpkg replaces with command output, otherwise rpmbuild replaces 3 {'s with 2 {'s.
%define RPKG_VERSION {{{ sed s/-/_/ version }}}
%define RPKG_SNAPINFO {{{ echo .$(git log -1 --date=format:"%Y%m%d" --format="%ad")git$(git rev-parse --short HEAD) }}}
%define have_rpkg %([ "%{RPKG_VERSION}" = "{{ sed s/-/_/ version }}" ]; echo $?)
%if 0%{!?version:1}
%if %{have_rpkg}
%define version %{RPKG_VERSION}
%define snapinfo %{RPKG_SNAPINFO}
%endif
%{?AC_VERSION: %define version %{AC_VERSION}}
%{!?version: %{error:Need to define version, e.g. --define "version x.y.z", or preprocess this file with configure or rpkg}}
%endif

# Define pkgrel when building to set release. Otherwise git rev is used for snapshot tag.
%if 0%{!?pkgrel:1} && 0%{!?snapinfo:1}
%{warn: pkgrel not defined, attempting to build snapshot from current git checkout}
%{warn: Define pkgrel, e.g. --define "pkgrel 1", if not building git snapshot}
%define snapinfo .%(git log -1 --date=format:"%Y%m%d" --format="%ad")git%(git rev-parse --short HEAD)
%define needgit 1
%endif

Name: tg-timer
Version: %{version}
Release: %{?pkgrel}%{!?pkgrel:1}%{?snapinfo}%{?dist}
Summary: Mechanical watch movement timegrapher
License: GPL2
Group: Misc
URL: https://github.com/vacaboja/tg
Source: %name-%version.tar.gz
Packager: Trent Piepho <tpiepho@gmail.com>
BuildRequires: gcc, gtk3-devel, portaudio-devel, fftw-devel
BuildRequires: desktop-file-utils, autoconf, automake
%{?needgit:BuildRequires: git}

%description
Tg (tg-timer) is a program to evaluate the performance of mechanical watch
movements. Tg works with the noise produced by a watch mechanism, and it
produces real-time readings of the rate (or accuracy) and various other
operational parameters.

%prep
%setup -q

%build
autoreconf -fi
%configure
%make_build

%install
%make_install

%check
desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop

%files
%license LICENSE
%doc README.md
%_bindir/%{name}
%_mandir/man1/%{name}.1*
%{_datadir}/applications/%{name}.desktop
%{_datadir}/icons/hicolor/*/apps/%{name}.png
%{_datadir}/icons/hicolor/scalable/apps/%{name}.svg
%{_datadir}/icons/hicolor/*/mimetypes/application-x-%{name}-data.png
%{_datadir}/icons/hicolor/scalable/mimetypes/application-x-%{name}-data.svg
%{_datadir}/mime/packages/%{name}.xml

%changelog
* Sat Apr 03 2021 Trent Piepho <tpiepho at gmail.com> 1:0.5.2
- Initial version
2 changes: 2 additions & 0 deletions packaging/tg-timer.inc.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Added by configure script to define package version
%define AC_VERSION @PACKAGE_VERSION@
19 changes: 8 additions & 11 deletions src/algo.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,14 @@

#include "tg.h"

struct filter {
double a0,a1,a2,b1,b2;
};

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;
}

static void make_hp(struct filter *f, double freq)
void make_hp(struct filter *f, double freq)
{
double K = tan(M_PI * freq);
double norm = 1 / (1 + K * sqrt(2) + K * K);
Expand Down Expand Up @@ -84,8 +80,6 @@ void setup_buffers(struct processing_buffers *b)
b->plan_e = fftwf_plan_dft_r2c_1d(b->sample_rate, b->tic_wf, b->tic_fft, FFTW_ESTIMATE);
b->plan_f = fftwf_plan_dft_r2c_1d(b->sample_rate, b->slice_wf, b->slice_fft, FFTW_ESTIMATE);
b->plan_g = fftwf_plan_dft_c2r_1d(b->sample_rate, b->slice_fft, b->slice_wf, FFTW_ESTIMATE);
b->hpf = malloc(sizeof(struct filter));
make_hp(b->hpf,(double)FILTER_CUTOFF/b->sample_rate);
b->lpf = malloc(sizeof(struct filter));
make_lp(b->lpf,(double)FILTER_CUTOFF/b->sample_rate);
b->events = malloc(EVENTS_MAX * sizeof(uint64_t));
Expand Down Expand Up @@ -116,7 +110,6 @@ void pb_destroy(struct processing_buffers *b)
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
Expand Down Expand Up @@ -301,7 +294,6 @@ 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);
if(run_noise_suppressor) noise_suppressor(b);

for(i=0; i < b->sample_count; i++)
Expand Down Expand Up @@ -452,7 +444,7 @@ static int compute_period(struct processing_buffers *b, int bph)
if(count > 1)
b->sigma = sqrt((sq_sum - count * estimate * estimate)/ (count-1));
else
b->sigma = b->period;
b->sigma = 0; // No std. dev. estimate possible with just 1 sample
return 0;
}

Expand Down Expand Up @@ -898,15 +890,20 @@ static void compute_cal(struct calibration_data *cd)
void process(struct processing_buffers *p, int bph, double la, int light)
{
prepare_data(p, !light);

p->ready = !compute_period(p,bph);
if(p->ready && p->period >= p->sample_rate / 2) {
/* Limit to 20% greater when period is known, or 500 ms when guessing period */
const int min_bph = bph ? bph : TYP_BPH;
const int max_period = (int)(1.2 * 3600 * 2) * p->sample_rate / min_bph;
if(p->ready && p->period >= max_period) {
debug("Detected period too long\n");
p->ready = 0;
}
if(!p->ready) {
debug("abort after compute_period()\n");
return;
}

prepare_waveform(p);
p->ready = !compute_parameters(p);
if(!p->ready) {
Expand Down
Loading