Build AppImages as part of CI workflow. (#937)
* Initial check-in of files from Peter's repo.
* NO_STRIP isn't necessary on Ubuntu.
* Build AppImage as part of CI workflow.
* Try copying user's Python libraries.
* Force setting PYTHONHOME/PYTHONPATH inside FreeDV code.
* Fix PYTHONHOME paths.
* Clean up unneeded code.
* Have GitHub sanity check AppImage on clean build agents.
* Forgot to install pipewire and X inside test agent.
* Build AppImage on 22.04 instead due to glibc incompatibilities.
* apt update prior to building AppImage.
* Need GTK if we're going to build our own wxWidgets.
* Need to use Python 3.10 when building AppImages on 22.04.
* Restructure Linux tests to hopefully take less time to run.
* Forgot keyword.
* Fix typo.
* Need to run UT in debug mode.
* ASan needs to be run with gcc.
* Use PulseAudio instead of pipewire when testing AppImage on 22.04.
* Include version number in AppImage filename.
* Fix typo.
* Fix filename for AppImage testing.
* Build dependencies ourselves.
* Forgot that we duplicated the Sanity Check step.
* Ensure sanity check step fails if we lose sync.
* Run unit tests on 22.04 as well.
* Add missed GDK_BACKEND export.
* Transmit at least 2x during rade_reporting_* tests.
This is because the logic to calculate RTT between "rig" and FreeDV doesn't
fully complete until after the first TX->RX transition. By transmitting 2x,
we ensure that this logic is also exercised and thus reduce the number of
failures in the GitHub environment.
* latency_msec doesn't work with PulseAudio.
* On 22.04, limit tests to fullduplex_* to avoid GH environment problem.
* Add minimum requirements to user manual.
* Fix rendering issue while debugging test problem in local Ubuntu 22.04 environment.
* Forgot to add a tee command to the 24.04 test.
* Try 10ms latency for Pulse audio.
* Remove std::vector from RADE TX/RX steps just in case that's causing problems.
* CLOCK_MONOTONIC seems to have higher reliability for Linux tests.
* Fix compiler warning.
* WIP user manual update.
* Revert UI fix as it breaks things on macOS.
* Experiment: try to test AppImage in Fedora on GitHub.
* Try again without rtkit.
* Forgot to remove systemctl calls
* FUSE is required for AppImage.
* Use alternate method of running AppImage.
* Try third-party Fedora 41 systemd image to stop dropouts.
* Pass --init to Fedora container.
* Try another container for Fedora.
* No ps in container.
* Use podman to run container instead.
* Add missed DBus packages.
* Forgot to start pipewire.
* Start pipewire after dbus is set up.
* Use different way to start pipewire.
* podman might need to run as root.
* Forgot quote.
* Fix environment not getting passed to podman.
* Experiment: see if Fedora 41 can be tested too.
* Revert "Experiment: see if Fedora 41 can be tested too."
This reverts commit c751ab2c77.
* Bring back previous GUI bugfix but limit it to wxWidgets < 3.2.
* Fix timespec bug discovered during performance profiling.
* Forgot to capture rv.
* Revert back to CLOCK_REALTIME.
* Remove additional sleep on semaphore timeout.
* Need to use sem_clockwait if we're going to use monotonic time.
* Remove comment that no longer applies.
* Add PR #937 to changelog.
* Fix memory leak reported by ASan.
* Deploy GTK dependencies as part of AppImage.
* Add additional protection to avoid dropouts.
* Use deque instead of vector for function queues due to better worst-case complexity.
* Warning cleanup.
* Documentation update.
* Fix macOS compiler error.
* Eliminate the need to install Octave in GitHub environment.
* Have all macOS jobs run at once to save time.
pull/951/head
parent
e699e59e73
commit
1cb14f6d8a
|
|
@ -11,22 +11,223 @@ concurrency:
|
|||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Debug
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
|
||||
jobs:
|
||||
build:
|
||||
lint:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install required packages
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade -y
|
||||
sudo apt-get install codespell
|
||||
|
||||
- name: Spellcheck codebase
|
||||
shell: bash
|
||||
run: codespell --ignore-words-list=caf,radae,rade,inout,nin,ontop,parm,tthe,ue `find src -name '*.c*' -o -name '*.h' | grep -v 3rdparty`
|
||||
|
||||
dist:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: lint
|
||||
outputs:
|
||||
APPIMAGE_FILENAME: ${{ steps.appimage-filename.outputs.APPIMAGE_FILENAME }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install required packages
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade -y
|
||||
sudo apt-get install libpulse-dev sox git libasound2-dev libao-dev libgsm1-dev xvfb pipewire pulseaudio-utils pipewire-pulse wireplumber metacity at-spi2-core libdbus-1-dev libgtk-3-dev
|
||||
|
||||
- name: Install Python required modules
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
python3 -m venv rade-venv
|
||||
. ./rade-venv/bin/activate
|
||||
pip3 install torch torchaudio --index-url https://download.pytorch.org/whl/cpu
|
||||
pip3 install matplotlib
|
||||
|
||||
- name: Build freedv-gui using PulseAudio
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
. ./rade-venv/bin/activate
|
||||
./build_linux.sh
|
||||
|
||||
- name: Build AppImage
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}/appimage
|
||||
run: |
|
||||
./make-appimage.sh
|
||||
|
||||
- name: Calculate AppImage filename
|
||||
shell: bash
|
||||
id: appimage-filename
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
export FREEDV_VERSION=`cat build_linux/freedv-version.txt`
|
||||
echo "APPIMAGE_FILENAME=FreeDV-${FREEDV_VERSION}-x86_64.AppImage" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload AppImage
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.appimage-filename.outputs.APPIMAGE_FILENAME }}
|
||||
path: ${{github.workspace}}/appimage/${{ steps.appimage-filename.outputs.APPIMAGE_FILENAME }}
|
||||
compression-level: 0
|
||||
|
||||
test-appimage-fedora:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [build, dist]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{needs.dist.outputs.APPIMAGE_FILENAME}}
|
||||
path: ${{github.workspace}}
|
||||
|
||||
- name: Start Fedora container
|
||||
shell: bash
|
||||
run: |
|
||||
sudo podman run -d --name fedora-test --systemd=always --privileged -v ${{github.workspace}}:/workspace docker.io/jrei/systemd-fedora:42
|
||||
|
||||
- name: Install rtkit for RT threading
|
||||
shell: bash
|
||||
run: |
|
||||
sudo podman exec fedora-test sudo dnf -y update
|
||||
sudo podman exec fedora-test sudo dnf -y install dbus dbus-x11 dbus-tools rtkit dbus-devel polkit
|
||||
sudo podman exec fedora-test sudo sed -i 's/no/yes/g' /usr/share/polkit-1/actions/org.freedesktop.RealtimeKit1.policy
|
||||
|
||||
- name: Install required packages for audio/display
|
||||
shell: bash
|
||||
run: |
|
||||
sudo podman exec fedora-test sudo dnf -y install sox xorg-x11-server-Xvfb pipewire pipewire-pulseaudio pulseaudio-libs-devel pulseaudio-utils wireplumber metacity
|
||||
|
||||
- name: Sanity check RADE
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
chmod +x ${{needs.dist.outputs.APPIMAGE_FILENAME}}
|
||||
echo "FREEDV_BINARY=./${{needs.dist.outputs.APPIMAGE_FILENAME}} --appimage-extract-and-run" > tmp.env
|
||||
sudo podman exec --env-file ./tmp.env fedora-test /workspace/appimage/container-test.sh | tee tmp.log
|
||||
grep "Got 1 sync changes" tmp.log
|
||||
|
||||
test-appimage-ubuntu:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-24.04, ubuntu-22.04]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [build, dist]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{needs.dist.outputs.APPIMAGE_FILENAME}}
|
||||
path: ${{github.workspace}}
|
||||
|
||||
- name: Install rtkit for RT threading
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade -y
|
||||
sudo apt-get install dbus-x11 rtkit libdbus-1-dev polkitd
|
||||
sudo sed -i 's/no/yes/g' /usr/share/polkit-1/actions/org.freedesktop.RealtimeKit1.policy
|
||||
sudo systemctl restart polkit
|
||||
|
||||
- name: Install required packages for audio/display
|
||||
shell: bash
|
||||
if: ${{ matrix.os == 'ubuntu-24.04' }}
|
||||
run: |
|
||||
sudo apt-get install sox xvfb pipewire pulseaudio-utils pipewire-pulse wireplumber metacity
|
||||
|
||||
- name: Install required packages for audio/display
|
||||
shell: bash
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
||||
run: |
|
||||
sudo apt-get install sox xvfb pulseaudio pulseaudio-utils metacity
|
||||
systemctl --user --now enable pulseaudio.service pulseaudio.socket
|
||||
|
||||
- name: Sanity check RADE
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
||||
run: |
|
||||
sudo systemctl enable rtkit-daemon
|
||||
sudo systemctl start rtkit-daemon
|
||||
Xvfb :99 -screen 0 1024x768x16 &
|
||||
sleep 5
|
||||
export DISPLAY=:99.0
|
||||
export XDG_RUNTIME_DIR=/run/user/$(id -u)
|
||||
mkdir -p $XDG_RUNTIME_DIR
|
||||
chmod 700 $XDG_RUNTIME_DIR
|
||||
eval "$(dbus-launch --sh-syntax --exit-with-x11)"
|
||||
metacity --sm-disable --replace &
|
||||
sleep 5
|
||||
chmod +x ${{needs.dist.outputs.APPIMAGE_FILENAME}}
|
||||
FREEDV_BINARY=./${{needs.dist.outputs.APPIMAGE_FILENAME}} ./test/test_zeros.sh txrx RADEV1 | tee tmp.log
|
||||
grep "Got 1 sync changes" tmp.log
|
||||
|
||||
- name: Sanity check RADE
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
if: ${{ matrix.os == 'ubuntu-24.04' }}
|
||||
run: |
|
||||
sudo systemctl enable rtkit-daemon
|
||||
sudo systemctl start rtkit-daemon
|
||||
Xvfb :99 -screen 0 1024x768x16 &
|
||||
sleep 5
|
||||
export DISPLAY=:99.0
|
||||
export XDG_RUNTIME_DIR=/run/user/$(id -u)
|
||||
mkdir -p $XDG_RUNTIME_DIR
|
||||
chmod 700 $XDG_RUNTIME_DIR
|
||||
eval "$(dbus-launch --sh-syntax --exit-with-x11)"
|
||||
pipewire &
|
||||
pipewire-pulse &
|
||||
wireplumber &
|
||||
metacity --sm-disable --replace &
|
||||
sleep 5
|
||||
chmod +x ${{needs.dist.outputs.APPIMAGE_FILENAME}}
|
||||
FREEDV_BINARY=./${{needs.dist.outputs.APPIMAGE_FILENAME}} ./test/test_zeros.sh txrx RADEV1 | tee tmp.log
|
||||
grep "Got 1 sync changes" tmp.log
|
||||
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-24.04, ubuntu-22.04]
|
||||
sanitizer: [WITH_ASAN=0, WITH_RTSAN=1, WITH_ASAN=1]
|
||||
native-audio: [USE_NATIVE_AUDIO=0, USE_NATIVE_AUDIO=1]
|
||||
exclude:
|
||||
- os: ubuntu-22.04
|
||||
sanitizer: WITH_RTSAN=1
|
||||
- os: ubuntu-22.04
|
||||
sanitizer: WITH_ASAN=1
|
||||
- os: ubuntu-24.04
|
||||
sanitizer: WITH_RTSAN=1
|
||||
native-audio: USE_NATIVE_AUDIO=0
|
||||
- os: ubuntu-24.04
|
||||
sanitizer: WITH_ASAN=1
|
||||
native-audio: USE_NATIVE_AUDIO=0
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: lint
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install LLVM/Clang >=20.0 (Required for RTSan)
|
||||
if: ${{ matrix.os == 'ubuntu-24.04' }}
|
||||
if: ${{ matrix.sanitizer == 'WITH_RTSAN=1' }}
|
||||
uses: MorganCaron/latest-clang-action@master
|
||||
|
||||
- name: Install rtkit for RT threading
|
||||
|
|
@ -41,25 +242,21 @@ jobs:
|
|||
- name: Install common packages
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get install libpulse-dev libspeexdsp-dev libsamplerate0-dev sox git portaudio19-dev libhamlib-dev libasound2-dev libao-dev libgsm1-dev libsndfile-dev xvfb pipewire pulseaudio-utils pipewire-pulse wireplumber metacity at-spi2-core octave octave-signal
|
||||
sudo apt-get install libpulse-dev libspeexdsp-dev libsamplerate0-dev sox git portaudio19-dev libhamlib-dev libasound2-dev libao-dev libgsm1-dev libsndfile-dev xvfb pipewire pulseaudio-utils pipewire-pulse wireplumber metacity at-spi2-core
|
||||
|
||||
- name: Install version-specific packages
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get install libwxgtk3.0-gtk3-dev
|
||||
sudo apt-get install libwxgtk3.0-gtk3-dev pulseaudio
|
||||
systemctl --user --now enable pulseaudio.service pulseaudio.socket
|
||||
|
||||
- name: Install version-specific packages
|
||||
if: ${{ matrix.os == 'ubuntu-24.04' }}
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get install codespell libwxgtk3.2-dev
|
||||
sudo apt-get install libwxgtk3.2-dev pipewire pipewire-pulse wireplumber
|
||||
|
||||
- name: Spellcheck codebase
|
||||
if: ${{ matrix.os == 'ubuntu-24.04' }}
|
||||
shell: bash
|
||||
run: codespell --ignore-words-list=caf,radae,rade,inout,nin,ontop,parm,tthe,ue `find src -name '*.c*' -o -name '*.h' | grep -v 3rdparty`
|
||||
|
||||
- name: Install Python required modules
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
|
|
@ -69,23 +266,45 @@ jobs:
|
|||
pip3 install torch torchaudio --index-url https://download.pytorch.org/whl/cpu
|
||||
pip3 install matplotlib
|
||||
|
||||
- name: Build freedv-gui using PortAudio
|
||||
- name: Build freedv-gui
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
if: ${{ matrix.sanitizer == 'WITH_RTSAN=1' }}
|
||||
run: |
|
||||
. ./rade-venv/bin/activate
|
||||
UT_ENABLE=1 USE_NATIVE_AUDIO=0 ./build_linux.sh
|
||||
UT_ENABLE=1 CC=clang CXX=clang++ ${{matrix.native-audio}} ${{matrix.sanitizer}} ./build_linux.sh
|
||||
|
||||
- name: Build freedv-gui using PulseAudio
|
||||
- name: Build freedv-gui
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
if: ${{ matrix.sanitizer != 'WITH_RTSAN=1' }}
|
||||
run: |
|
||||
. ./rade-venv/bin/activate
|
||||
BUILD_TYPE=RelWithDebInfo UT_ENABLE=1 ./build_linux.sh
|
||||
UT_ENABLE=1 ${{matrix.native-audio}} ${{matrix.sanitizer}} ./build_linux.sh
|
||||
|
||||
- name: Execute unit tests
|
||||
shell: bash
|
||||
if: ${{ matrix.os == 'ubuntu-24.04' }}
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' && matrix.native-audio == 'USE_NATIVE_AUDIO=1' }}
|
||||
working-directory: ${{github.workspace}}/build_linux
|
||||
run: |
|
||||
sudo systemctl enable rtkit-daemon
|
||||
sudo systemctl start rtkit-daemon
|
||||
Xvfb :99 -screen 0 1024x768x16 &
|
||||
sleep 5
|
||||
export DISPLAY=:99.0
|
||||
export XDG_RUNTIME_DIR=/run/user/$(id -u)
|
||||
mkdir -p $XDG_RUNTIME_DIR
|
||||
chmod 700 $XDG_RUNTIME_DIR
|
||||
eval "$(dbus-launch --sh-syntax --exit-with-x11)"
|
||||
metacity --sm-disable --replace &
|
||||
sleep 5
|
||||
ln -s ${{github.workspace}}/build_linux/rade_src/model19_check3 model19_check3
|
||||
. ../rade-venv/bin/activate
|
||||
ASAN_OPTIONS=suppressions=${{github.workspace}}/test/asan_suppressions.txt LSAN_OPTIONS=suppressions=${{github.workspace}}/test/lsan_suppressions.txt PYTHONPATH=${{github.workspace}}/build_linux/rade_src:$PYTHONPATH ctest -V -R fullduplex_
|
||||
|
||||
- name: Execute unit tests
|
||||
shell: bash
|
||||
if: ${{ matrix.os == 'ubuntu-24.04' && matrix.native-audio == 'USE_NATIVE_AUDIO=1' }}
|
||||
working-directory: ${{github.workspace}}/build_linux
|
||||
run: |
|
||||
sudo systemctl enable rtkit-daemon
|
||||
|
|
@ -104,41 +323,6 @@ jobs:
|
|||
sleep 5
|
||||
ln -s ${{github.workspace}}/build_linux/rade_src/model19_check3 model19_check3
|
||||
. ../rade-venv/bin/activate
|
||||
PYTHONPATH=${{github.workspace}}/build_linux/rade_src:$PYTHONPATH ctest -V
|
||||
|
||||
- name: Instrument AddressSanitizer
|
||||
shell: bash
|
||||
if: ${{ matrix.os == 'ubuntu-24.04' }}
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
. ./rade-venv/bin/activate
|
||||
BUILD_TYPE=RelWithDebInfo WITH_ASAN=1 UT_ENABLE=1 ./build_linux.sh
|
||||
ASAN_OPTIONS=suppressions=${{github.workspace}}/test/asan_suppressions.txt LSAN_OPTIONS=suppressions=${{github.workspace}}/test/lsan_suppressions.txt PYTHONPATH=${{github.workspace}}/build_linux/rade_src:$PYTHONPATH ctest -V
|
||||
|
||||
- name: Check for memory leaks
|
||||
shell: bash
|
||||
if: ${{ matrix.os == 'ubuntu-24.04' }}
|
||||
working-directory: ${{github.workspace}}/build_linux
|
||||
run: |
|
||||
export DISPLAY=:99.0
|
||||
export XDG_RUNTIME_DIR=/run/user/$(id -u)
|
||||
. ../rade-venv/bin/activate
|
||||
ASAN_OPTIONS=suppressions=${{github.workspace}}/test/asan_suppressions.txt LSAN_OPTIONS=suppressions=${{github.workspace}}/test/lsan_suppressions.txt PYTHONPATH=${{github.workspace}}/build_linux/rade_src:$PYTHONPATH ctest -V -R "fullduplex_(RADE|700D)|rade_reporting_clean"
|
||||
|
||||
- name: Instrument RealtimeSanitizer
|
||||
shell: bash
|
||||
if: ${{ matrix.os == 'ubuntu-24.04' }}
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
. ./rade-venv/bin/activate
|
||||
BUILD_TYPE=RelWithDebInfo WITH_RTSAN=1 UT_ENABLE=1 CC=clang CXX=clang++ ./build_linux.sh
|
||||
|
||||
- name: Check for RT-unsafe function calls
|
||||
shell: bash
|
||||
if: ${{ matrix.os == 'ubuntu-24.04' }}
|
||||
working-directory: ${{github.workspace}}/build_linux
|
||||
run: |
|
||||
export DISPLAY=:99.0
|
||||
export XDG_RUNTIME_DIR=/run/user/$(id -u)
|
||||
. ../rade-venv/bin/activate
|
||||
PYTHONPATH=${{github.workspace}}/build_linux/rade_src:$PYTHONPATH ctest -V -R "fullduplex_(RADE|700D)|rade_reporting_clean"
|
||||
|
||||
|
|
|
|||
|
|
@ -21,33 +21,13 @@ jobs:
|
|||
os: [macos-13, macos-latest] # x86_64, ARM64
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: dist
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: gerlero/brew-install@v1
|
||||
with:
|
||||
packages: automake libtool numpy sox octave wxwidgets speexdsp portaudio libsndfile libsamplerate hamlib
|
||||
|
||||
- name: Install octave-signal
|
||||
if: ${{ matrix.os == 'macos-13' }}
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
# make sure gfortran is available
|
||||
gfortran --version
|
||||
octave-cli --eval "pkg install -forge control; pkg install -forge signal"
|
||||
|
||||
- name: Install octave-signal
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
shell: bash
|
||||
working-directory: ${{github.workspace}}
|
||||
run: |
|
||||
# make sure gfortran is available
|
||||
sudo ln -s /opt/homebrew/bin/gfortran-14 /usr/local/bin/gfortran
|
||||
gfortran --version
|
||||
octave-cli --eval "pkg install -forge control; pkg install -forge signal"
|
||||
packages: automake libtool numpy sox wxwidgets speexdsp portaudio libsndfile libsamplerate hamlib
|
||||
|
||||
- name: Install VB-Cable
|
||||
shell: bash
|
||||
|
|
|
|||
|
|
@ -11,3 +11,5 @@ a.out
|
|||
*.make
|
||||
*.log
|
||||
*.marks
|
||||
*.AppDir
|
||||
*.AppImage
|
||||
|
|
|
|||
|
|
@ -9,6 +9,20 @@ This is a live document. Notes on new FreeDV features are being added as they a
|
|||
|
||||
This section contains instructions to help you get started.
|
||||
|
||||
## System Requirements
|
||||
|
||||
FreeDV is officially supported on the following operating systems and versions:
|
||||
|
||||
* Windows: Windows 10 and later
|
||||
* macOS: macOS Big Sur (11.0) and later (ARM and Intel)
|
||||
* Linux: Ubuntu 22.04 LTS and later, Fedora 42 and later
|
||||
|
||||
While it may be possible to execute FreeDV on additional platforms and distributions
|
||||
(especially ones similar to ones currently supported), this is not guaranteed. Any
|
||||
bugfixes required for an unsupported platform may be deferred or rejected, especially
|
||||
if there is impact to supported platforms. Requests for official support of new platforms
|
||||
should go through our existing [Feature Request process](https://github.com/drowe67/freedv-gui/issues/new?template=feature_request.yml).
|
||||
|
||||
## Easy Setup
|
||||
|
||||
Upon starting FreeDV for the first time, the Easy Setup dialog will appear. This
|
||||
|
|
@ -814,6 +828,7 @@ LDPC | Low Density Parity Check Codes - a family of powerful FEC codes
|
|||
3. Build system:
|
||||
* Update Hamlib to 4.6.3 (macOS/Windows). (PR #930)
|
||||
* Reload current Git hash every time it changes. (PR #935)
|
||||
* Add infrastructure for generating AppImage builds. (PR #937)
|
||||
|
||||
## V2.0.0 June 2025
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/bash -e
|
||||
echo "In AppImage AppRun"
|
||||
export LD_LIBRARY_PATH="${APPIMAGE_LIBRARY_PATH}:${APPDIR}/usr/lib:${LD_LIBRARY_PATH}"
|
||||
export PATH="$APPDIR/usr/bin:$APPDIR/rade-venv/bin"
|
||||
export GDK_BACKEND=x11
|
||||
echo "PATH=$PATH"
|
||||
export PYTHONHOME="$APPDIR/usr"
|
||||
export PYTHONPATH="$APPDIR/rade_src:$APPDIR/rade-venv/lib/python3.10/site-packages"
|
||||
echo "PYTHONPATH=$PYTHONPATH"
|
||||
echo "PYTHONHOME=$PYTHONHOME"
|
||||
cd "$APPDIR"
|
||||
echo "#### after import"
|
||||
"$APPDIR/usr/bin/freedv" $@
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
[Desktop Entry]
|
||||
Name=FreeDV
|
||||
Exec=freedv
|
||||
Icon=freedv256x256
|
||||
Type=Application
|
||||
Categories=Utility;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd /workspace
|
||||
Xvfb :99 -screen 0 1024x768x16 &
|
||||
sleep 5
|
||||
export DISPLAY=:99.0
|
||||
export XDG_RUNTIME_DIR=/run/user/$(id -u)
|
||||
mkdir -p $XDG_RUNTIME_DIR
|
||||
chmod 700 $XDG_RUNTIME_DIR
|
||||
eval "$(dbus-launch --sh-syntax --exit-with-x11)"
|
||||
sudo systemctl restart polkit
|
||||
sudo systemctl enable rtkit-daemon
|
||||
sudo systemctl start rtkit-daemon
|
||||
pipewire &
|
||||
pipewire-pulse &
|
||||
wireplumber &
|
||||
metacity --sm-disable --replace &
|
||||
sleep 5
|
||||
./test/test_zeros.sh txrx RADEV1
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
APPNAME="FreeDV"
|
||||
APPDIR="$APPNAME.AppDir"
|
||||
BUILDDIR="../"
|
||||
|
||||
# Change to the directory where this script is located
|
||||
cd "$(dirname "$(realpath "$0")")"
|
||||
|
||||
if [ -d "$APPDIR" ]; then
|
||||
echo "Deleting $APPDIR..."
|
||||
rm -rf "$APPDIR"
|
||||
else
|
||||
echo "$APPDIR does not exist."
|
||||
fi
|
||||
|
||||
echo "Bundle dependencies..."
|
||||
if test -f linuxdeploy-x86_64.AppImage; then
|
||||
echo "linuxdeploy exists"
|
||||
else
|
||||
wget -c "https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh"
|
||||
wget https://github.com/linuxdeploy/linuxdeploy/releases/latest/download/linuxdeploy-x86_64.AppImage
|
||||
chmod +x linuxdeploy-x86_64.AppImage linuxdeploy-plugin-gtk.sh
|
||||
fi
|
||||
|
||||
./linuxdeploy-x86_64.AppImage \
|
||||
--executable /usr/bin/python3 \
|
||||
--executable ../build_linux/src/freedv \
|
||||
--appdir "$APPDIR" \
|
||||
--icon-file ../contrib/freedv256x256.png \
|
||||
--custom-apprun=AppRun.sh \
|
||||
--desktop-file FreeDV.desktop
|
||||
|
||||
# create the virtual environment (copied from Brian's build script)
|
||||
cd FreeDV.AppDir
|
||||
python3 -m venv rade-venv # || { echo "ERROR: create venv failed"; exit 1; }
|
||||
# Activate it
|
||||
source rade-venv/bin/activate # || { echo "ERROR: activate venv failed"; exit 1; }
|
||||
|
||||
# Clear cache in venv
|
||||
pip3 cache purge
|
||||
pip3 install --upgrade pip || echo "WARNING: pip upgrade failed"
|
||||
pip3 install numpy
|
||||
pip3 install torch torchaudio --index-url https://download.pytorch.org/whl/cpu
|
||||
pip3 install matplotlib
|
||||
cd -
|
||||
|
||||
echo "Fix venv python links..."
|
||||
echo "Now in $(pwd)"
|
||||
cd "$APPDIR/rade-venv/bin"
|
||||
echo "Now in $(pwd)"
|
||||
ln -s -f ../../usr/bin/python3 python
|
||||
ln -s -f ../../usr/bin/python3 python3
|
||||
ln -s -f ../../usr/bin/python3 python3.10
|
||||
cd - # back to the previous directory
|
||||
echo "### Now in $(pwd)"
|
||||
|
||||
# Copy /usr/lib/python3.10 to image
|
||||
cd $APPDIR/usr
|
||||
cp -a /usr/lib/python3.10 lib/
|
||||
cd -
|
||||
|
||||
# Copy the models and symlink
|
||||
echo "Copying rade_src..."
|
||||
# ls freedv-rade/freedv-gui/build_linux/rade_src/model
|
||||
# model05/ model17/ model18/ model19/ model19_check3/ model_bbfm_01/
|
||||
cp -r "$BUILDDIR/build_linux/rade_src" "$APPDIR/."
|
||||
cd "$APPDIR/usr/bin"
|
||||
ln -s "../../rade_src/model19_check3" "model19_check3"
|
||||
cd -
|
||||
|
||||
# Create the output
|
||||
./linuxdeploy-x86_64.AppImage \
|
||||
--appdir "$APPDIR" \
|
||||
--plugin gtk \
|
||||
--output appimage
|
||||
|
||||
# Include version number in AppImage filename
|
||||
FREEDV_VERSION=`cat ../build_linux/freedv-version.txt`
|
||||
mv FreeDV-x86_64.AppImage FreeDV-$FREEDV_VERSION-x86_64.AppImage
|
||||
|
||||
echo "Done"
|
||||
|
|
@ -50,6 +50,7 @@ function(CheckGitVersion)
|
|||
else()
|
||||
set(FREEDV_VERSION "${FreeDV_VERSION}")
|
||||
endif()
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/freedv-version.txt ${FREEDV_VERSION})
|
||||
|
||||
# Only update the git_version.cpp if the hash has changed. This will
|
||||
# prevent us from rebuilding the project more than we need to.
|
||||
|
|
@ -57,7 +58,6 @@ function(CheckGitVersion)
|
|||
# Set che GIT_HASH_CACHE variable the next build won't have
|
||||
# to regenerate the source file.
|
||||
CheckGitWrite(${GIT_HASH})
|
||||
|
||||
configure_file(${pre_configure_file} ${post_configure_file} @ONLY)
|
||||
endif ()
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public:
|
|||
|
||||
// Lets audio system know that we're done with the work on the received
|
||||
// audio.
|
||||
virtual void stopRealTimeWork() override { std::this_thread::sleep_for(10ms); }
|
||||
virtual void stopRealTimeWork(bool fastMode = false) override { std::this_thread::sleep_for(10ms); }
|
||||
|
||||
// Reverts real-time priority for current thread.
|
||||
virtual void clearHelperRealTime() override { /* empty */ }
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public:
|
|||
|
||||
// Lets audio system know that we're done with the work on the received
|
||||
// audio.
|
||||
virtual void stopRealTimeWork() override;
|
||||
virtual void stopRealTimeWork(bool fastMode = false) override;
|
||||
|
||||
// Reverts real-time priority for current thread.
|
||||
virtual void clearHelperRealTime() override;
|
||||
|
|
|
|||
|
|
@ -706,9 +706,9 @@ void MacAudioDevice::startRealTimeWork()
|
|||
}
|
||||
}
|
||||
|
||||
void MacAudioDevice::stopRealTimeWork()
|
||||
void MacAudioDevice::stopRealTimeWork(bool fastMode)
|
||||
{
|
||||
dispatch_semaphore_wait(sem_, dispatch_time(DISPATCH_TIME_NOW, AUDIO_SAMPLE_BLOCK_SEC * kOneNanosecond));
|
||||
dispatch_semaphore_wait(sem_, dispatch_time(DISPATCH_TIME_NOW, (int)(AUDIO_SAMPLE_BLOCK_SEC * kOneNanosecond) >> (fastMode ? 1 : 0)));
|
||||
}
|
||||
|
||||
void MacAudioDevice::clearHelperRealTime()
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <signal.h>
|
||||
|
||||
#include "PulseAudioDevice.h"
|
||||
#include "../util/timespec.h"
|
||||
|
||||
#if defined(USE_RTKIT)
|
||||
#include "rtkit.h"
|
||||
|
|
@ -41,7 +42,6 @@ using namespace std::chrono_literals;
|
|||
// TX audio to reach the radio.
|
||||
#define PULSE_TARGET_LATENCY_US 20000
|
||||
|
||||
thread_local std::chrono::high_resolution_clock::time_point PulseAudioDevice::StartTime_;
|
||||
thread_local bool PulseAudioDevice::MustStopWork_ = false;
|
||||
|
||||
PulseAudioDevice::PulseAudioDevice(pa_threaded_mainloop *mainloop, pa_context* context, wxString devName, IAudioEngine::AudioDirection direction, int sampleRate, int numChannels)
|
||||
|
|
@ -278,20 +278,19 @@ void PulseAudioDevice::setHelperRealTime()
|
|||
|
||||
void PulseAudioDevice::startRealTimeWork()
|
||||
{
|
||||
StartTime_ = std::chrono::high_resolution_clock::now();
|
||||
|
||||
sleepFallback_ = false;
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts_) == -1)
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts_) == -1)
|
||||
{
|
||||
sleepFallback_ = true;
|
||||
}
|
||||
}
|
||||
void PulseAudioDevice::stopRealTimeWork()
|
||||
void PulseAudioDevice::stopRealTimeWork(bool fastMode)
|
||||
{
|
||||
if (sleepFallback_)
|
||||
{
|
||||
// Fallback to simple sleep.
|
||||
IAudioDevice::stopRealTimeWork();
|
||||
IAudioDevice::stopRealTimeWork(fastMode);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -300,28 +299,22 @@ void PulseAudioDevice::stopRealTimeWork()
|
|||
{
|
||||
latency = PULSE_TARGET_LATENCY_US;
|
||||
}
|
||||
latency >>= fastMode ? 1 : 0;
|
||||
|
||||
ts_.tv_nsec += latency * 1000;
|
||||
if (ts_.tv_nsec >= 1000000000)
|
||||
{
|
||||
ts_.tv_sec++;
|
||||
ts_.tv_nsec -= 1000000000;
|
||||
}
|
||||
ts_ = timespec_normalise(ts_);
|
||||
|
||||
if (sem_timedwait(&sem_, &ts_) < 0 && errno != ETIMEDOUT)
|
||||
int rv = 0;
|
||||
while ((rv = sem_clockwait(&sem_, CLOCK_MONOTONIC, &ts_)) == -1 && errno == EINTR)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
if (rv == -1 && errno != ETIMEDOUT)
|
||||
{
|
||||
// Fallback to simple sleep.
|
||||
sleepFallback_ = true;
|
||||
IAudioDevice::stopRealTimeWork();
|
||||
}
|
||||
else if (errno == ETIMEDOUT)
|
||||
{
|
||||
auto endTime = std::chrono::high_resolution_clock::now();
|
||||
if ((endTime - StartTime_) >= std::chrono::microseconds(PULSE_TARGET_LATENCY_US * 10))
|
||||
{
|
||||
// Took a lot longer than expected. Force a sleep so we don't get killed by rtkit.
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(PULSE_TARGET_LATENCY_US));
|
||||
}
|
||||
}
|
||||
|
||||
MustStopWork_ = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ public:
|
|||
|
||||
// Lets audio system know that we're done with the work on the received
|
||||
// audio.
|
||||
virtual void stopRealTimeWork() override;
|
||||
virtual void stopRealTimeWork(bool fastMode = false) override;
|
||||
|
||||
// Reverts real-time priority for current thread.
|
||||
virtual void clearHelperRealTime() override;
|
||||
|
|
@ -82,7 +82,6 @@ private:
|
|||
int sampleRate_;
|
||||
int numChannels_;
|
||||
|
||||
thread_local static std::chrono::high_resolution_clock::time_point StartTime_;
|
||||
thread_local static bool MustStopWork_;
|
||||
|
||||
sem_t sem_;
|
||||
|
|
|
|||
|
|
@ -560,7 +560,7 @@ void WASAPIAudioDevice::startRealTimeWork()
|
|||
// empty
|
||||
}
|
||||
|
||||
void WASAPIAudioDevice::stopRealTimeWork()
|
||||
void WASAPIAudioDevice::stopRealTimeWork(bool fastMode)
|
||||
{
|
||||
if (semaphore_ == nullptr)
|
||||
{
|
||||
|
|
@ -570,12 +570,12 @@ void WASAPIAudioDevice::stopRealTimeWork()
|
|||
}
|
||||
|
||||
// Wait a maximum of (bufferSize / sampleRate) seconds for the semaphore to return
|
||||
DWORD result = WaitForSingleObject(semaphore_, (1000 * bufferFrameCount_) / sampleRate_);
|
||||
DWORD result = WaitForSingleObject(semaphore_, ((1000 * bufferFrameCount_) / sampleRate_) >> (fastMode ? 1 : 0));
|
||||
|
||||
if (result != WAIT_TIMEOUT && result != WAIT_OBJECT_0)
|
||||
{
|
||||
// Fallback to a simple sleep.
|
||||
IAudioDevice::stopRealTimeWork();
|
||||
IAudioDevice::stopRealTimeWork(fastMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public:
|
|||
|
||||
// Lets audio system know that we're done with the work on the received
|
||||
// audio.
|
||||
virtual void stopRealTimeWork() override;
|
||||
virtual void stopRealTimeWork(bool fastMode = false) override;
|
||||
|
||||
// Reverts real-time priority for current thread.
|
||||
virtual void clearHelperRealTime() override;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ PlotScalar::PlotScalar(wxWindow* parent,
|
|||
m_graticule_a_step = graticule_a_step;
|
||||
assert(strlen(a_fmt) < 15);
|
||||
memset(m_a_fmt, 0, sizeof(m_a_fmt));
|
||||
strncpy(m_a_fmt, a_fmt, strlen(a_fmt));
|
||||
strncpy(m_a_fmt, a_fmt, sizeof(m_a_fmt) - 1);
|
||||
m_mini = mini;
|
||||
m_bar_graph = 0;
|
||||
m_logy = 0;
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ void PlotSpectrum::draw(wxGraphicsContext* ctx)
|
|||
break;
|
||||
default:
|
||||
assert(0);
|
||||
mag = m_magdB[index]; // assume m_numSampleAveraging = 1
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -396,4 +397,4 @@ void PlotSpectrum::OnKeyDown(wxKeyEvent& event)
|
|||
void PlotSpectrum::OnMouseMiddleDown(wxMouseEvent& event)
|
||||
{
|
||||
clickTune(FDMDV_FCENTRE);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1018,7 +1018,7 @@ void FreeDVReporterDialog::OnStatusTextChange(wxCommandEvent& event)
|
|||
// reduces the number of times one must backspace in order to completely
|
||||
// remove the flag.
|
||||
int index = 0;
|
||||
while (index < statusMsg.Length())
|
||||
while (index < (int)statusMsg.Length())
|
||||
{
|
||||
auto chr = statusMsg.GetChar(index);
|
||||
if (chr.GetValue() == 0x1F3F4)
|
||||
|
|
@ -1026,7 +1026,7 @@ void FreeDVReporterDialog::OnStatusTextChange(wxCommandEvent& event)
|
|||
log_debug("Found start char at index %d", index);
|
||||
auto endIndex = index + 1;
|
||||
bool foundEnd = false;
|
||||
while (endIndex < statusMsg.Length())
|
||||
while (endIndex < (int)statusMsg.Length())
|
||||
{
|
||||
auto endChar = statusMsg.GetChar(endIndex).GetValue();
|
||||
if (endChar == 0xE007F)
|
||||
|
|
@ -1432,7 +1432,7 @@ void FreeDVReporterDialog::FreeDVReporterDataModel::execQueuedAction_()
|
|||
fn();
|
||||
|
||||
lk.lock();
|
||||
fnQueue_.erase(fnQueue_.begin());
|
||||
fnQueue_.pop_front();
|
||||
size = fnQueue_.size();
|
||||
lk.unlock();
|
||||
}
|
||||
|
|
@ -1529,6 +1529,12 @@ FreeDVReporterDialog::FreeDVReporterDataModel::FreeDVReporterDataModel(FreeDVRep
|
|||
FreeDVReporterDialog::FreeDVReporterDataModel::~FreeDVReporterDataModel()
|
||||
{
|
||||
setReporter(nullptr);
|
||||
|
||||
for (auto& kvp : allReporterData_)
|
||||
{
|
||||
delete kvp.second;
|
||||
}
|
||||
allReporterData_.clear();
|
||||
}
|
||||
|
||||
void FreeDVReporterDialog::FreeDVReporterDataModel::setReporter(std::shared_ptr<FreeDVReporter> reporter)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
|
||||
#include <wx/tipwin.h>
|
||||
|
|
@ -259,7 +259,7 @@ class FreeDVReporterDialog : public wxFrame
|
|||
|
||||
std::shared_ptr<FreeDVReporter> reporter_;
|
||||
std::map<std::string, ReporterData*> allReporterData_;
|
||||
std::vector<std::function<void()> > fnQueue_;
|
||||
std::deque<std::function<void()> > fnQueue_;
|
||||
std::mutex fnQueueMtx_;
|
||||
std::recursive_mutex dataMtx_;
|
||||
bool isConnected_;
|
||||
|
|
|
|||
|
|
@ -558,7 +558,7 @@ bool MainApp::OnInit()
|
|||
log_info("PYTHONPATH is %s", (const char*)ppath.ToUTF8());
|
||||
#endif // __APPLE__
|
||||
|
||||
#endif // _WIN32 || __APPLE__
|
||||
#endif // _WIN32 || __APPLE__
|
||||
|
||||
#if defined(UNOFFICIAL_RELEASE)
|
||||
// Terminate the application if the current date > expiration date
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ void PlaybackStep::nonRtThreadEntry_()
|
|||
}
|
||||
}
|
||||
|
||||
if (numRead < samplesAtSourceRate && codec2_fifo_used(outputFifo_) == 0)
|
||||
if ((int)numRead < samplesAtSourceRate && codec2_fifo_used(outputFifo_) == 0)
|
||||
{
|
||||
//log_info("file read complete");
|
||||
fileCompleteFn_();
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ RADEReceiveStep::RADEReceiveStep(struct rade* dv, FARGANState* fargan, rade_text
|
|||
, fargan_(fargan)
|
||||
, inputSampleFifo_(nullptr)
|
||||
, outputSampleFifo_(nullptr)
|
||||
, pendingFeatures_(nullptr)
|
||||
, pendingFeaturesIdx_(0)
|
||||
, featuresFile_(nullptr)
|
||||
, textPtr_(textPtr)
|
||||
{
|
||||
|
|
@ -76,7 +78,8 @@ RADEReceiveStep::RADEReceiveStep(struct rade* dv, FARGANState* fargan, rade_text
|
|||
eooOut_ = new float[rade_n_eoo_bits(dv_)];
|
||||
assert(eooOut_ != nullptr);
|
||||
|
||||
pendingFeatures_.reserve(rade_n_features_in_out(dv_));
|
||||
pendingFeatures_ = new float[NB_TOTAL_FEATURES];
|
||||
assert(pendingFeatures_ != nullptr);
|
||||
}
|
||||
|
||||
RADEReceiveStep::~RADEReceiveStep()
|
||||
|
|
@ -85,6 +88,7 @@ RADEReceiveStep::~RADEReceiveStep()
|
|||
delete[] inputBufCplx_;
|
||||
delete[] featuresOut_;
|
||||
delete[] eooOut_;
|
||||
delete[] pendingFeatures_;
|
||||
outputSamples_ = nullptr;
|
||||
|
||||
if (featuresFile_ != nullptr)
|
||||
|
|
@ -181,35 +185,23 @@ std::shared_ptr<short> RADEReceiveStep::execute(std::shared_ptr<short> inputSamp
|
|||
|
||||
for (int i = 0; i < nout; i++)
|
||||
{
|
||||
pendingFeatures_.push_back(featuresOut_[i]);
|
||||
}
|
||||
|
||||
// FARGAN processing (features->analog audio)
|
||||
while (pendingFeatures_.size() >= NB_TOTAL_FEATURES)
|
||||
{
|
||||
// XXX - lpcnet_demo reads NB_TOTAL_FEATURES from RADE
|
||||
// but only processes NB_FEATURES of those for some reason.
|
||||
float featuresIn[NB_FEATURES];
|
||||
for (int i = 0; i < NB_FEATURES; i++)
|
||||
pendingFeatures_[pendingFeaturesIdx_++] = featuresOut_[i];
|
||||
if (pendingFeaturesIdx_ == NB_TOTAL_FEATURES)
|
||||
{
|
||||
featuresIn[i] = pendingFeatures_[0];
|
||||
pendingFeatures_.erase(pendingFeatures_.begin());
|
||||
}
|
||||
for (int i = 0; i < (NB_TOTAL_FEATURES - NB_FEATURES); i++)
|
||||
{
|
||||
pendingFeatures_.erase(pendingFeatures_.begin());
|
||||
}
|
||||
pendingFeaturesIdx_ = 0;
|
||||
|
||||
float fpcm[LPCNET_FRAME_SIZE];
|
||||
short pcm[LPCNET_FRAME_SIZE];
|
||||
fargan_synthesize(fargan_, fpcm, featuresIn);
|
||||
for (int i = 0; i < LPCNET_FRAME_SIZE; i++)
|
||||
{
|
||||
pcm[i] = (int)floor(.5 + MIN32(32767, MAX32(-32767, 32768.f*fpcm[i])));
|
||||
}
|
||||
// FARGAN processing (features->analog audio)
|
||||
float fpcm[LPCNET_FRAME_SIZE];
|
||||
short pcm[LPCNET_FRAME_SIZE];
|
||||
fargan_synthesize(fargan_, fpcm, pendingFeatures_);
|
||||
for (int i = 0; i < LPCNET_FRAME_SIZE; i++)
|
||||
{
|
||||
pcm[i] = (int)floor(.5 + MIN32(32767, MAX32(-32767, 32768.f*fpcm[i])));
|
||||
}
|
||||
|
||||
*numOutputSamples += LPCNET_FRAME_SIZE;
|
||||
codec2_fifo_write(outputSampleFifo_, pcm, LPCNET_FRAME_SIZE);
|
||||
*numOutputSamples += LPCNET_FRAME_SIZE;
|
||||
codec2_fifo_write(outputSampleFifo_, pcm, LPCNET_FRAME_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,5 +242,5 @@ void RADEReceiveStep::reset()
|
|||
{
|
||||
codec2_fifo_read(outputSampleFifo_, &buf, 1);
|
||||
}
|
||||
pendingFeatures_.clear();
|
||||
pendingFeaturesIdx_ = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
#include <atomic>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include "IPipelineStep.h"
|
||||
#include "../freedv_interface.h"
|
||||
#include "rade_api.h"
|
||||
|
|
@ -57,7 +56,8 @@ private:
|
|||
FARGANState* fargan_;
|
||||
struct FIFO* inputSampleFifo_;
|
||||
struct FIFO* outputSampleFifo_;
|
||||
std::vector<float> pendingFeatures_;
|
||||
float* pendingFeatures_;
|
||||
int pendingFeaturesIdx_;
|
||||
FILE* featuresFile_;
|
||||
rade_text_t textPtr_;
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ RADETransmitStep::RADETransmitStep(struct rade* dv, LPCNetEncState* encState)
|
|||
, encState_(encState)
|
||||
, inputSampleFifo_(nullptr)
|
||||
, outputSampleFifo_(nullptr)
|
||||
, featureList_(nullptr)
|
||||
, featureListIdx_(0)
|
||||
, featuresFile_(nullptr)
|
||||
{
|
||||
inputSampleFifo_ = codec2_fifo_create(RADE_SPEECH_SAMPLE_RATE);
|
||||
|
|
@ -79,7 +81,8 @@ RADETransmitStep::RADETransmitStep(struct rade* dv, LPCNetEncState* encState)
|
|||
eooOutShort_ = new short[numEOOSamples + NUM_SAMPLES_SILENCE];
|
||||
assert(eooOutShort_ != nullptr);
|
||||
|
||||
featureList_.reserve(rade_n_features_in_out(dv_));
|
||||
featureList_ = new float[rade_n_features_in_out(dv_)];
|
||||
assert(featureList_ != nullptr);
|
||||
}
|
||||
|
||||
RADETransmitStep::~RADETransmitStep()
|
||||
|
|
@ -88,6 +91,7 @@ RADETransmitStep::~RADETransmitStep()
|
|||
delete[] radeOutShort_;
|
||||
delete[] eooOut_;
|
||||
delete[] eooOutShort_;
|
||||
delete[] featureList_;
|
||||
outputSamples_ = nullptr;
|
||||
|
||||
if (featuresFile_ != nullptr)
|
||||
|
|
@ -143,7 +147,7 @@ std::shared_ptr<short> RADETransmitStep::execute(std::shared_ptr<short> inputSam
|
|||
|
||||
if ((*numOutputSamples + numSamplesPerTx) < maxSamples && codec2_fifo_used(inputSampleFifo_) >= LPCNET_FRAME_SIZE)
|
||||
{
|
||||
unsigned int numRequiredFeaturesForRADE = rade_n_features_in_out(dv_);
|
||||
int numRequiredFeaturesForRADE = rade_n_features_in_out(dv_);
|
||||
short pcm[LPCNET_FRAME_SIZE];
|
||||
float features[NB_TOTAL_FEATURES];
|
||||
|
||||
|
|
@ -160,36 +164,33 @@ std::shared_ptr<short> RADETransmitStep::execute(std::shared_ptr<short> inputSam
|
|||
|
||||
for (int index = 0; index < NB_TOTAL_FEATURES; index++)
|
||||
{
|
||||
featureList_.push_back(features[index]);
|
||||
}
|
||||
featureList_[featureListIdx_++] = features[index];
|
||||
if (featureListIdx_ == numRequiredFeaturesForRADE)
|
||||
{
|
||||
featureListIdx_ = 0;
|
||||
|
||||
// RADE TX handling
|
||||
while (featureList_.size() >= numRequiredFeaturesForRADE)
|
||||
{
|
||||
// RADE TX handling
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
__rtsan_disable();
|
||||
__rtsan_disable();
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
|
||||
rade_tx(dv_, radeOut_, &featureList_[0]);
|
||||
rade_tx(dv_, radeOut_, &featureList_[0]);
|
||||
|
||||
#if defined(__clang__)
|
||||
#if defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
__rtsan_enable();
|
||||
__rtsan_enable();
|
||||
#endif // defined(__has_feature) && __has_feature(realtime_sanitizer)
|
||||
#endif // defined(__clang__)
|
||||
|
||||
for (unsigned int index = 0; index < numRequiredFeaturesForRADE; index++)
|
||||
{
|
||||
featureList_.erase(featureList_.begin());
|
||||
for (int index = 0; index < numSamplesPerTx; index++)
|
||||
{
|
||||
// We only need the real component for TX.
|
||||
radeOutShort_[index] = radeOut_[index].real * RADE_SCALING_FACTOR;
|
||||
}
|
||||
codec2_fifo_write(outputSampleFifo_, radeOutShort_, numSamplesPerTx);
|
||||
}
|
||||
for (int index = 0; index < numSamplesPerTx; index++)
|
||||
{
|
||||
// We only need the real component for TX.
|
||||
radeOutShort_[index] = radeOut_[index].real * RADE_SCALING_FACTOR;
|
||||
}
|
||||
codec2_fifo_write(outputSampleFifo_, radeOutShort_, numSamplesPerTx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -246,5 +247,5 @@ void RADETransmitStep::reset()
|
|||
{
|
||||
codec2_fifo_read(outputSampleFifo_, &buf, 1);
|
||||
}
|
||||
featureList_.clear();
|
||||
featureListIdx_ = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
#define AUDIO_PIPELINE__RADE_TRANSMIT_STEP_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include "IPipelineStep.h"
|
||||
#include "../freedv_interface.h"
|
||||
#include "rade_api.h"
|
||||
|
|
@ -49,7 +48,8 @@ private:
|
|||
LPCNetEncState* encState_;
|
||||
struct FIFO* inputSampleFifo_;
|
||||
struct FIFO* outputSampleFifo_;
|
||||
std::vector<float> featureList_;
|
||||
float* featureList_;
|
||||
int featureListIdx_;
|
||||
|
||||
FILE* featuresFile_;
|
||||
|
||||
|
|
|
|||
|
|
@ -476,8 +476,18 @@ void* TxRxThread::Entry()
|
|||
|
||||
if (m_tx) txProcessing_();
|
||||
else rxProcessing_();
|
||||
|
||||
helper_->stopRealTimeWork();
|
||||
|
||||
// Determine whether we need to pause for a shorter amount
|
||||
// of time to avoid dropouts.
|
||||
paCallBackData *cbData = g_rxUserdata;
|
||||
struct FIFO* outFifo = cbData->outfifo1;
|
||||
if (!m_tx)
|
||||
{
|
||||
outFifo = (g_nSoundCards == 1) ? cbData->outfifo1 : cbData->outfifo2;
|
||||
}
|
||||
auto totalFifoCapacity = codec2_fifo_used(outFifo) + codec2_fifo_free(outFifo);
|
||||
auto fifoUsed = codec2_fifo_used(outFifo);
|
||||
helper_->stopRealTimeWork(fifoUsed < totalFifoCapacity / 2);
|
||||
}
|
||||
|
||||
// Force pipeline to delete itself when we're done with the thread.
|
||||
|
|
|
|||
|
|
@ -537,7 +537,12 @@ TopFrame::TopFrame(wxWindow* parent, wxWindowID id, const wxString& title, const
|
|||
m_btnHelp->SetToolTip(_("Get help with FreeDV."));
|
||||
helpSizer->Add(m_btnHelp, 0, wxALIGN_CENTER|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5);
|
||||
leftSizer->SetMinSize(wxSize(-1, 375));
|
||||
|
||||
#if !wxCHECK_VERSION(3,2,0)
|
||||
leftOuterSizer->Add(leftSizer, 0, wxALL | wxEXPAND | wxFIXED_MINSIZE, 1);
|
||||
#else
|
||||
leftOuterSizer->Add(leftSizer, 2, wxALL | wxEXPAND | wxFIXED_MINSIZE, 1);
|
||||
#endif // !wxCHECK_VERSION(3,2,0)
|
||||
leftOuterSizer->Add(helpSizer, 0, wxFIXED_MINSIZE | wxALL | wxEXPAND, 1);
|
||||
|
||||
bSizer1->Add(leftOuterSizer, 0, wxALL|wxEXPAND, 5);
|
||||
|
|
|
|||
|
|
@ -2,11 +2,16 @@ if(WIN32)
|
|||
set(UTIL_WIN32_FILES logging/libfmemopen.c Win32COMObject.cpp)
|
||||
endif(WIN32)
|
||||
|
||||
if(LINUX)
|
||||
set(UTIL_LINUX_FILES timespec.c)
|
||||
endif(LINUX)
|
||||
|
||||
add_library(fdv_util STATIC
|
||||
SocketIoClient.cpp
|
||||
ThreadedObject.cpp
|
||||
ThreadedTimer.cpp
|
||||
TcpConnectionHandler.cpp
|
||||
logging/ulog.c
|
||||
${UTIL_LINUX_FILES}
|
||||
${UTIL_WIN32_FILES}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ public:
|
|||
virtual void startRealTimeWork() = 0;
|
||||
|
||||
// Lets audio system know that we're done with the work on the received
|
||||
// audio.
|
||||
virtual void stopRealTimeWork() = 0;
|
||||
// audio. fastMode is an optional argument that indicates whether we
|
||||
// need to reduce timeouts due to buffers running empty.
|
||||
virtual void stopRealTimeWork(bool fastMode = false) = 0;
|
||||
|
||||
// Reverts real-time priority for current thread.
|
||||
virtual void clearHelperRealTime() = 0;
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ void ThreadedObject::eventLoop_()
|
|||
}
|
||||
|
||||
fn = eventQueue_[0];
|
||||
eventQueue_.erase(eventQueue_.begin());
|
||||
eventQueue_.pop_front();
|
||||
}
|
||||
|
||||
if (fn)
|
||||
|
|
@ -117,4 +117,4 @@ void ThreadedObject::eventLoop_()
|
|||
count--;
|
||||
} while (count > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
|
||||
class ThreadedObject
|
||||
|
|
@ -44,11 +44,11 @@ protected:
|
|||
private:
|
||||
bool isDestroying_;
|
||||
std::thread objectThread_;
|
||||
std::vector<std::function<void()> > eventQueue_;
|
||||
std::deque<std::function<void()> > eventQueue_;
|
||||
std::recursive_mutex eventQueueMutex_;
|
||||
std::condition_variable_any eventQueueCV_;
|
||||
|
||||
void eventLoop_();
|
||||
};
|
||||
|
||||
#endif // THREADED_OBJECT_H
|
||||
#endif // THREADED_OBJECT_H
|
||||
|
|
|
|||
|
|
@ -0,0 +1,979 @@
|
|||
/* Functions for working with timespec structures
|
||||
* Written by Daniel Collins (2017-2021)
|
||||
* timespec_mod by Alex Forencich (2019)
|
||||
* Various contributions by Ingo Albrecht (2021)
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
*
|
||||
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
* distribute this software, either in source code form or as a compiled
|
||||
* binary, for any purpose, commercial or non-commercial, and by any
|
||||
* means.
|
||||
*
|
||||
* In jurisdictions that recognize copyright laws, the author or authors
|
||||
* of this software dedicate any and all copyright interest in the
|
||||
* software to the public domain. We make this dedication for the benefit
|
||||
* of the public at large and to the detriment of our heirs and
|
||||
* successors. We intend this dedication to be an overt act of
|
||||
* relinquishment in perpetuity of all present and future rights to this
|
||||
* software under copyright law.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* For more information, please refer to <http://unlicense.org/>
|
||||
*/
|
||||
|
||||
/** \file timespec.c
|
||||
* \brief Functions for working with timespec structures.
|
||||
*
|
||||
* This library aims to provide a comprehensive set of functions with
|
||||
* well-defined behaviour that handle all edge cases (e.g. negative values) in
|
||||
* a sensible manner.
|
||||
*
|
||||
* Negative values are allowed in the tv_sec and/or tv_usec field of timespec
|
||||
* structures, tv_usec is always relative to tv_sec, so mixing positive and
|
||||
* negative values will produce consistent results:
|
||||
*
|
||||
* <PRE>
|
||||
* { tv_sec = 1, tv_nsec = 500000000 } == 1.5 seconds
|
||||
* { tv_sec = 1, tv_nsec = 0 } == 1.0 seconds
|
||||
* { tv_sec = 1, tv_nsec = -500000000 } == 0.5 seconds
|
||||
* { tv_sec = 0, tv_nsec = 500000000 } == 0.5 seconds
|
||||
* { tv_sec = 0, tv_nsec = 0 } == 0.0 seconds
|
||||
* { tv_sec = 0, tv_nsec = -500000000 } == -0.5 seconds
|
||||
* { tv_sec = -1, tv_nsec = 500000000 } == -0.5 seconds
|
||||
* { tv_sec = -1, tv_nsec = 0 } == -1.0 seconds
|
||||
* { tv_sec = -1, tv_nsec = -500000000 } == -1.5 seconds
|
||||
* </PRE>
|
||||
*
|
||||
* Furthermore, any timespec structure processed or returned by library functions
|
||||
* is normalised according to the rules in timespec_normalise().
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "timespec.h"
|
||||
|
||||
#define NSEC_PER_SEC 1000000000
|
||||
|
||||
/** \fn struct timespec timespec_add(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns the result of adding two timespec structures.
|
||||
*/
|
||||
struct timespec timespec_add(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
/* Normalise inputs to prevent tv_nsec rollover if whole-second values
|
||||
* are packed in it.
|
||||
*/
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
ts1.tv_sec += ts2.tv_sec;
|
||||
ts1.tv_nsec += ts2.tv_nsec;
|
||||
|
||||
return timespec_normalise(ts1);
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_sub(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns the result of subtracting ts2 from ts1.
|
||||
*/
|
||||
struct timespec timespec_sub(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
/* Normalise inputs to prevent tv_nsec rollover if whole-second values
|
||||
* are packed in it.
|
||||
*/
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
ts1.tv_sec -= ts2.tv_sec;
|
||||
ts1.tv_nsec -= ts2.tv_nsec;
|
||||
|
||||
return timespec_normalise(ts1);
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_mod(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns the remainder left over after dividing ts1 by ts2 (ts1%ts2).
|
||||
*/
|
||||
struct timespec timespec_mod(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
int i = 0;
|
||||
bool neg1 = false;
|
||||
bool neg2 = false;
|
||||
|
||||
/* Normalise inputs to prevent tv_nsec rollover if whole-second values
|
||||
* are packed in it.
|
||||
*/
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
/* If ts2 is zero, just return ts1
|
||||
*/
|
||||
if (ts2.tv_sec == 0 && ts2.tv_nsec == 0)
|
||||
{
|
||||
return ts1;
|
||||
}
|
||||
|
||||
/* If inputs are negative, flip and record sign
|
||||
*/
|
||||
if (ts1.tv_sec < 0 || ts1.tv_nsec < 0)
|
||||
{
|
||||
neg1 = true;
|
||||
ts1.tv_sec = -ts1.tv_sec;
|
||||
ts1.tv_nsec = -ts1.tv_nsec;
|
||||
}
|
||||
|
||||
if (ts2.tv_sec < 0 || ts2.tv_nsec < 0)
|
||||
{
|
||||
neg2 = true;
|
||||
ts2.tv_sec = -ts2.tv_sec;
|
||||
ts2.tv_nsec = -ts2.tv_nsec;
|
||||
}
|
||||
|
||||
/* Shift ts2 until it is larger than ts1 or is about to overflow
|
||||
*/
|
||||
while ((ts2.tv_sec < (LONG_MAX >> 1)) && timespec_ge(ts1, ts2))
|
||||
{
|
||||
i++;
|
||||
ts2.tv_nsec <<= 1;
|
||||
ts2.tv_sec <<= 1;
|
||||
if (ts2.tv_nsec > NSEC_PER_SEC)
|
||||
{
|
||||
ts2.tv_nsec -= NSEC_PER_SEC;
|
||||
ts2.tv_sec++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Division by repeated subtraction
|
||||
*/
|
||||
while (i >= 0)
|
||||
{
|
||||
if (timespec_ge(ts1, ts2))
|
||||
{
|
||||
ts1 = timespec_sub(ts1, ts2);
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
i--;
|
||||
if (ts2.tv_sec & 1)
|
||||
{
|
||||
ts2.tv_nsec += NSEC_PER_SEC;
|
||||
}
|
||||
ts2.tv_nsec >>= 1;
|
||||
ts2.tv_sec >>= 1;
|
||||
}
|
||||
|
||||
/* If signs differ and result is nonzero, subtract once more to cross zero
|
||||
*/
|
||||
if (neg1 ^ neg2 && (ts1.tv_sec != 0 || ts1.tv_nsec != 0))
|
||||
{
|
||||
ts1 = timespec_sub(ts1, ts2);
|
||||
}
|
||||
|
||||
/* Restore sign
|
||||
*/
|
||||
if (neg1)
|
||||
{
|
||||
ts1.tv_sec = -ts1.tv_sec;
|
||||
ts1.tv_nsec = -ts1.tv_nsec;
|
||||
}
|
||||
|
||||
return ts1;
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_min(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Return the lesser one of the two given timespec values.
|
||||
*/
|
||||
struct timespec timespec_min(struct timespec ts1, struct timespec ts2) {
|
||||
if(timespec_le(ts1, ts2)) {
|
||||
return ts1;
|
||||
} else {
|
||||
return ts2;
|
||||
}
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_max(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Return the greater one of the two given timespec values.
|
||||
*/
|
||||
struct timespec timespec_max(struct timespec ts1, struct timespec ts2) {
|
||||
if(timespec_ge(ts1, ts2)) {
|
||||
return ts1;
|
||||
} else {
|
||||
return ts2;
|
||||
}
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_clamp(struct timespec ts, struct timespec min, struct timespec max)
|
||||
* \brief Clamp the value of TS between MIN and MAX.
|
||||
*/
|
||||
struct timespec timespec_clamp(struct timespec ts, struct timespec min, struct timespec max) {
|
||||
if(timespec_gt(ts, max)) {
|
||||
return max;
|
||||
}
|
||||
if(timespec_lt(ts, min)) {
|
||||
return min;
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
|
||||
/** \fn int timespec_cmp(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns (1, 0, -1) if ts1 is (greater than, equal to, less than) to ts2.
|
||||
*/
|
||||
int timespec_cmp(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
if(ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec == ts2.tv_nsec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if((ts1.tv_sec > ts2.tv_sec)
|
||||
|| (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec > ts2.tv_nsec))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** \fn bool timespec_eq(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns true if the two timespec structures are equal.
|
||||
*/
|
||||
bool timespec_eq(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
return (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec == ts2.tv_nsec);
|
||||
}
|
||||
|
||||
/** \fn bool timespec_gt(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns true if ts1 is greater than ts2.
|
||||
*/
|
||||
bool timespec_gt(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
return (ts1.tv_sec > ts2.tv_sec || (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec > ts2.tv_nsec));
|
||||
}
|
||||
|
||||
/** \fn bool timespec_ge(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns true if ts1 is greater than or equal to ts2.
|
||||
*/
|
||||
bool timespec_ge(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
return (ts1.tv_sec > ts2.tv_sec || (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec >= ts2.tv_nsec));
|
||||
}
|
||||
|
||||
/** \fn bool timespec_lt(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns true if ts1 is less than ts2.
|
||||
*/
|
||||
bool timespec_lt(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
return (ts1.tv_sec < ts2.tv_sec || (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec < ts2.tv_nsec));
|
||||
}
|
||||
|
||||
/** \fn bool timespec_le(struct timespec ts1, struct timespec ts2)
|
||||
* \brief Returns true if ts1 is less than or equal to ts2.
|
||||
*/
|
||||
bool timespec_le(struct timespec ts1, struct timespec ts2)
|
||||
{
|
||||
ts1 = timespec_normalise(ts1);
|
||||
ts2 = timespec_normalise(ts2);
|
||||
|
||||
return (ts1.tv_sec < ts2.tv_sec || (ts1.tv_sec == ts2.tv_sec && ts1.tv_nsec <= ts2.tv_nsec));
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_from_double(double s)
|
||||
* \brief Converts a fractional number of seconds to a timespec.
|
||||
*/
|
||||
struct timespec timespec_from_double(double s)
|
||||
{
|
||||
struct timespec ts = {
|
||||
.tv_sec = s,
|
||||
.tv_nsec = (s - (long)(s)) * NSEC_PER_SEC,
|
||||
};
|
||||
|
||||
return timespec_normalise(ts);
|
||||
}
|
||||
|
||||
/** \fn double timespec_to_double(struct timespec ts)
|
||||
* \brief Converts a timespec to a fractional number of seconds.
|
||||
*/
|
||||
double timespec_to_double(struct timespec ts)
|
||||
{
|
||||
return ((double)(ts.tv_sec) + ((double)(ts.tv_nsec) / NSEC_PER_SEC));
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_from_timeval(struct timeval tv)
|
||||
* \brief Converts a timeval to a timespec.
|
||||
*/
|
||||
struct timespec timespec_from_timeval(struct timeval tv)
|
||||
{
|
||||
struct timespec ts = {
|
||||
.tv_sec = tv.tv_sec,
|
||||
.tv_nsec = tv.tv_usec * 1000
|
||||
};
|
||||
|
||||
return timespec_normalise(ts);
|
||||
}
|
||||
|
||||
/** \fn struct timeval timespec_to_timeval(struct timespec ts)
|
||||
* \brief Converts a timespec to a timeval.
|
||||
*/
|
||||
struct timeval timespec_to_timeval(struct timespec ts)
|
||||
{
|
||||
ts = timespec_normalise(ts);
|
||||
|
||||
struct timeval tv = {
|
||||
.tv_sec = ts.tv_sec,
|
||||
.tv_usec = ts.tv_nsec / 1000,
|
||||
};
|
||||
|
||||
return tv;
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_from_ms(long milliseconds)
|
||||
* \brief Converts an integer number of milliseconds to a timespec.
|
||||
*/
|
||||
struct timespec timespec_from_ms(long milliseconds)
|
||||
{
|
||||
struct timespec ts = {
|
||||
.tv_sec = (milliseconds / 1000),
|
||||
.tv_nsec = (milliseconds % 1000) * 1000000,
|
||||
};
|
||||
|
||||
return timespec_normalise(ts);
|
||||
}
|
||||
|
||||
/** \fn long timespec_to_ms(struct timespec ts)
|
||||
* \brief Converts a timespec to an integer number of milliseconds.
|
||||
*/
|
||||
long timespec_to_ms(struct timespec ts)
|
||||
{
|
||||
return (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000);
|
||||
}
|
||||
|
||||
/** \fn struct timespec timespec_normalise(struct timespec ts)
|
||||
* \brief Normalises a timespec structure.
|
||||
*
|
||||
* Returns a normalised version of a timespec structure, according to the
|
||||
* following rules:
|
||||
*
|
||||
* 1) If tv_nsec is >=1,000,000,00 or <=-1,000,000,000, flatten the surplus
|
||||
* nanoseconds into the tv_sec field.
|
||||
*
|
||||
* 2) If tv_nsec is negative, decrement tv_sec and roll tv_nsec up to represent
|
||||
* the same value attainable by ADDING nanoseconds to tv_sec.
|
||||
*/
|
||||
struct timespec timespec_normalise(struct timespec ts)
|
||||
{
|
||||
while(ts.tv_nsec >= NSEC_PER_SEC)
|
||||
{
|
||||
++(ts.tv_sec);
|
||||
ts.tv_nsec -= NSEC_PER_SEC;
|
||||
}
|
||||
|
||||
while(ts.tv_nsec <= -NSEC_PER_SEC)
|
||||
{
|
||||
--(ts.tv_sec);
|
||||
ts.tv_nsec += NSEC_PER_SEC;
|
||||
}
|
||||
|
||||
if(ts.tv_nsec < 0)
|
||||
{
|
||||
/* Negative nanoseconds isn't valid according to POSIX.
|
||||
* Decrement tv_sec and roll tv_nsec over.
|
||||
*/
|
||||
|
||||
--(ts.tv_sec);
|
||||
ts.tv_nsec = (NSEC_PER_SEC + ts.tv_nsec);
|
||||
}
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <stdio.h>
|
||||
|
||||
#define TEST_NORMALISE(ts_sec, ts_nsec, expect_sec, expect_nsec) { \
|
||||
struct timespec in = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
|
||||
struct timespec got = timespec_normalise(in); \
|
||||
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
|
||||
{ \
|
||||
printf("%s:%d: timespec_normalise({%ld, %ld}) returned wrong values\n", __FILE__, __LINE__, \
|
||||
(long)(ts_sec), (long)(ts_nsec)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_BINOP(func, ts1_sec, ts1_nsec, ts2_sec, ts2_nsec, expect_sec, expect_nsec) { \
|
||||
struct timespec ts1 = { .tv_sec = ts1_sec, .tv_nsec = ts1_nsec }; \
|
||||
struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \
|
||||
struct timespec got = func(ts1, ts2); \
|
||||
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
|
||||
{ \
|
||||
printf(#func "({%ld, %ld}, {%ld, %ld}) returned wrong values\n", \
|
||||
(long)(ts1_sec), (long)(ts1_nsec), (long)(ts2_sec), (long)(ts2_nsec)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_TRINOP(func, ts1_sec, ts1_nsec, ts2_sec, ts2_nsec, ts3_sec, ts3_nsec, expect_sec, expect_nsec) { \
|
||||
struct timespec ts1 = { .tv_sec = ts1_sec, .tv_nsec = ts1_nsec }; \
|
||||
struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \
|
||||
struct timespec ts3 = { .tv_sec = ts3_sec, .tv_nsec = ts3_nsec }; \
|
||||
struct timespec got = func(ts1, ts2, ts3); \
|
||||
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
|
||||
{ \
|
||||
printf(#func "({%ld, %ld}, {%ld, %ld}, {%ld, %ld}) returned wrong values\n", \
|
||||
(long)(ts1_sec), (long)(ts1_nsec), \
|
||||
(long)(ts2_sec), (long)(ts2_nsec), \
|
||||
(long)(ts3_sec), (long)(ts3_nsec)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_TEST_FUNC(func, ts1_sec, ts1_nsec, ts2_sec, ts2_nsec, expect) { \
|
||||
struct timespec ts1 = { .tv_sec = ts1_sec, .tv_nsec = ts1_nsec }; \
|
||||
struct timespec ts2 = { .tv_sec = ts2_sec, .tv_nsec = ts2_nsec }; \
|
||||
int got = func(ts1, ts2); \
|
||||
if(got != expect) \
|
||||
{ \
|
||||
printf("%s:%d: " #func "({%ld, %ld}, {%ld, %ld}) returned %d, expected %s\n", __FILE__, __LINE__, \
|
||||
(long)(ts1_sec), (long)(ts1_nsec), (long)(ts2_sec), (long)(ts2_nsec), \
|
||||
got, #expect); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_FROM_DOUBLE(d_secs, expect_sec, expect_nsec) { \
|
||||
struct timespec got = timespec_from_double(d_secs); \
|
||||
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
|
||||
{ \
|
||||
printf("%s:%d: timespec_from_double(%f) returned wrong values\n", __FILE__, __LINE__, (double)(d_secs)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_TO_DOUBLE(ts_sec, ts_nsec, expect) { \
|
||||
struct timespec ts = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
|
||||
double got = timespec_to_double(ts); \
|
||||
if(got != expect) { \
|
||||
printf("%s:%d: timespec_to_double({%ld, %ld}) returned wrong value\n", __FILE__, __LINE__, \
|
||||
(long)(ts_sec), (long)(ts_nsec)); \
|
||||
printf(" Expected: %f\n", (double)(expect)); \
|
||||
printf(" Got: %f\n", got); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_FROM_TIMEVAL(in_sec, in_usec, expect_sec, expect_nsec) { \
|
||||
struct timeval tv = { .tv_sec = in_sec, .tv_usec = in_usec }; \
|
||||
struct timespec got = timespec_from_timeval(tv); \
|
||||
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
|
||||
{ \
|
||||
printf("%s:%d: timespec_from_timeval({%ld, %ld}) returned wrong values\n", __FILE__, __LINE__, \
|
||||
(long)(in_sec), (long)(in_usec)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_TO_TIMEVAL(ts_sec, ts_nsec, expect_sec, expect_usec) { \
|
||||
struct timespec ts = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
|
||||
struct timeval got = timespec_to_timeval(ts); \
|
||||
if(got.tv_sec != expect_sec || got.tv_usec != expect_usec) \
|
||||
{ \
|
||||
printf("%s:%d: timespec_to_timeval({%ld, %ld}) returned wrong values\n", __FILE__, __LINE__, \
|
||||
(long)(ts_sec), (long)(ts_nsec)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_usec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_usec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_FROM_MS(msecs, expect_sec, expect_nsec) { \
|
||||
struct timespec got = timespec_from_ms(msecs); \
|
||||
if(got.tv_sec != expect_sec || got.tv_nsec != expect_nsec) \
|
||||
{ \
|
||||
printf("%s:%d: timespec_from_ms(%ld) returned wrong values\n", __FILE__, __LINE__, (long)(msecs)); \
|
||||
printf(" Expected: {%ld, %ld}\n", (long)(expect_sec), (long)(expect_nsec)); \
|
||||
printf(" Got: {%ld, %ld}\n", (long)(got.tv_sec), (long)(got.tv_nsec)); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define TEST_TO_MS(ts_sec, ts_nsec, expect) { \
|
||||
struct timespec ts = { .tv_sec = ts_sec, .tv_nsec = ts_nsec }; \
|
||||
long got = timespec_to_ms(ts); \
|
||||
if(got != expect) { \
|
||||
printf("%s:%d: timespec_to_ms({%ld, %ld}) returned wrong value\n", __FILE__, __LINE__, \
|
||||
(long)(ts_sec), (long)(ts_nsec)); \
|
||||
printf(" Expected: %ld\n", (long)(expect)); \
|
||||
printf(" Got: %ld\n", got); \
|
||||
++result; \
|
||||
} \
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
// timespec_add
|
||||
|
||||
TEST_BINOP(timespec_add, 0,0, 0,0, 0,0);
|
||||
TEST_BINOP(timespec_add, 0,0, 1,0, 1,0);
|
||||
TEST_BINOP(timespec_add, 1,0, 0,0, 1,0);
|
||||
TEST_BINOP(timespec_add, 1,0, 1,0, 2,0);
|
||||
TEST_BINOP(timespec_add, 1,500000000, 1,0, 2,500000000);
|
||||
TEST_BINOP(timespec_add, 1,0, 1,500000000, 2,500000000);
|
||||
TEST_BINOP(timespec_add, 1,500000000, 1,500000000, 3,0);
|
||||
TEST_BINOP(timespec_add, 1,500000000, 1,499999999, 2,999999999);
|
||||
TEST_BINOP(timespec_add, 1,500000000, 1,500000000, 3,0);
|
||||
TEST_BINOP(timespec_add, 1,999999999, 1,999999999, 3,999999998);
|
||||
TEST_BINOP(timespec_add, 0,500000000, 1,500000000, 2,0);
|
||||
TEST_BINOP(timespec_add, 1,500000000, 0,500000000, 2,0);
|
||||
|
||||
// timespec_sub
|
||||
|
||||
TEST_BINOP(timespec_sub, 0,0, 0,0, 0,0);
|
||||
TEST_BINOP(timespec_sub, 1,0, 0,0, 1,0);
|
||||
TEST_BINOP(timespec_sub, 1,0, 1,0, 0,0);
|
||||
TEST_BINOP(timespec_sub, 1,500000000, 0,500000000, 1,0);
|
||||
TEST_BINOP(timespec_sub, 5,500000000, 2,999999999, 2,500000001);
|
||||
TEST_BINOP(timespec_sub, 0,0, 1,0, -1,0);
|
||||
TEST_BINOP(timespec_sub, 0,500000000, 1,500000000, -1,0);
|
||||
TEST_BINOP(timespec_sub, 0,0, 1,500000000, -2,500000000);
|
||||
TEST_BINOP(timespec_sub, 1,0, 1,500000000, -1,500000000);
|
||||
TEST_BINOP(timespec_sub, 1,0, 1,499999999, -1,500000001);
|
||||
|
||||
// timespec_mod
|
||||
|
||||
TEST_BINOP(timespec_mod, 0,0, 0,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, 0,0, 1,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, 1,0, 0,0, 1,0);
|
||||
TEST_BINOP(timespec_mod, 1,0, 1,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, 10,0, 1,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, 10,0, 3,0, 1,0);
|
||||
TEST_BINOP(timespec_mod, 10,0, -3,0, -2,0);
|
||||
TEST_BINOP(timespec_mod, -10,0, 3,0, 2,0);
|
||||
TEST_BINOP(timespec_mod, -10,0, -3,0, -1,0);
|
||||
TEST_BINOP(timespec_mod, 10,0, 5,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, 10,0, -5,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, -10,0, 5,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, -10,0, -5,0, 0,0);
|
||||
TEST_BINOP(timespec_mod, 1,500000000, 0,500000000, 0,0);
|
||||
TEST_BINOP(timespec_mod, 5,500000000, 2,999999999, 2,500000001);
|
||||
TEST_BINOP(timespec_mod, 0,500000000, 1,500000000, 0,500000000);
|
||||
TEST_BINOP(timespec_mod, 0,0, 1,500000000, 0,0);
|
||||
TEST_BINOP(timespec_mod, 1,0, 1,500000000, 1,0);
|
||||
TEST_BINOP(timespec_mod, 1,0, 0,1, 0,0);
|
||||
TEST_BINOP(timespec_mod, 1,123456789, 0,1000, 0,789);
|
||||
TEST_BINOP(timespec_mod, 1,0, 0,9999999, 0,100);
|
||||
TEST_BINOP(timespec_mod, 12345,54321, 0,100001, 0,5555);
|
||||
TEST_BINOP(timespec_mod, LONG_MAX,0, 0,1, 0,0);
|
||||
TEST_BINOP(timespec_mod, LONG_MAX,0, LONG_MAX,1, LONG_MAX,0);
|
||||
|
||||
// timespec_clamp
|
||||
|
||||
TEST_TRINOP(timespec_clamp, 0,0, 0,0, 0,0, 0,0);
|
||||
|
||||
TEST_TRINOP(timespec_clamp, 1000,0, 2000,0, 3000,0, 2000,0);
|
||||
TEST_TRINOP(timespec_clamp, 1500,0, 2000,0, 3000,0, 2000,0);
|
||||
TEST_TRINOP(timespec_clamp, 1999,0, 2000,0, 3000,0, 2000,0);
|
||||
TEST_TRINOP(timespec_clamp, 2000,0, 2000,0, 3000,0, 2000,0);
|
||||
TEST_TRINOP(timespec_clamp, 2001,0, 2000,0, 3000,0, 2001,0);
|
||||
TEST_TRINOP(timespec_clamp, 2250,0, 2000,0, 3000,0, 2250,0);
|
||||
TEST_TRINOP(timespec_clamp, 2500,0, 2000,0, 3000,0, 2500,0);
|
||||
TEST_TRINOP(timespec_clamp, 2750,0, 2000,0, 3000,0, 2750,0);
|
||||
TEST_TRINOP(timespec_clamp, 2999,0, 2000,0, 3000,0, 2999,0);
|
||||
TEST_TRINOP(timespec_clamp, 3000,0, 2000,0, 3000,0, 3000,0);
|
||||
TEST_TRINOP(timespec_clamp, 3001,0, 2000,0, 3000,0, 3000,0);
|
||||
TEST_TRINOP(timespec_clamp, 3500,0, 2000,0, 3000,0, 3000,0);
|
||||
TEST_TRINOP(timespec_clamp, 4000,0, 2000,0, 3000,0, 3000,0);
|
||||
|
||||
TEST_TRINOP(timespec_clamp, 0,1000, 0,2000, 0,3000, 0,2000);
|
||||
TEST_TRINOP(timespec_clamp, 0,1500, 0,2000, 0,3000, 0,2000);
|
||||
TEST_TRINOP(timespec_clamp, 0,1999, 0,2000, 0,3000, 0,2000);
|
||||
TEST_TRINOP(timespec_clamp, 0,2000, 0,2000, 0,3000, 0,2000);
|
||||
TEST_TRINOP(timespec_clamp, 0,2001, 0,2000, 0,3000, 0,2001);
|
||||
TEST_TRINOP(timespec_clamp, 0,2250, 0,2000, 0,3000, 0,2250);
|
||||
TEST_TRINOP(timespec_clamp, 0,2500, 0,2000, 0,3000, 0,2500);
|
||||
TEST_TRINOP(timespec_clamp, 0,2750, 0,2000, 0,3000, 0,2750);
|
||||
TEST_TRINOP(timespec_clamp, 0,2999, 0,2000, 0,3000, 0,2999);
|
||||
TEST_TRINOP(timespec_clamp, 0,3000, 0,2000, 0,3000, 0,3000);
|
||||
TEST_TRINOP(timespec_clamp, 0,3001, 0,2000, 0,3000, 0,3000);
|
||||
TEST_TRINOP(timespec_clamp, 0,3500, 0,2000, 0,3000, 0,3000);
|
||||
TEST_TRINOP(timespec_clamp, 0,4000, 0,2000, 0,3000, 0,3000);
|
||||
|
||||
TEST_TRINOP(timespec_clamp,0,-1000, 0,-3000, 0,-2000, 0,-2000);
|
||||
TEST_TRINOP(timespec_clamp,0,-1500, 0,-3000, 0,-2000, 0,-2000);
|
||||
TEST_TRINOP(timespec_clamp,0,-1999, 0,-3000, 0,-2000, 0,-2000);
|
||||
TEST_TRINOP(timespec_clamp,0,-3000, 0,-3000, 0,-2000, 0,-3000);
|
||||
TEST_TRINOP(timespec_clamp,0,-2001, 0,-3000, 0,-2000, 0,-2001);
|
||||
TEST_TRINOP(timespec_clamp,0,-2250, 0,-3000, 0,-2000, 0,-2250);
|
||||
TEST_TRINOP(timespec_clamp,0,-2500, 0,-3000, 0,-2000, 0,-2500);
|
||||
TEST_TRINOP(timespec_clamp,0,-2750, 0,-3000, 0,-2000, 0,-2750);
|
||||
TEST_TRINOP(timespec_clamp,0,-2999, 0,-3000, 0,-2000, 0,-2999);
|
||||
TEST_TRINOP(timespec_clamp,0,-2000, 0,-3000, 0,-2000, 0,-2000);
|
||||
TEST_TRINOP(timespec_clamp,0,-3001, 0,-3000, 0,-2000, 0,-3000);
|
||||
TEST_TRINOP(timespec_clamp,0,-3500, 0,-3000, 0,-2000, 0,-3000);
|
||||
TEST_TRINOP(timespec_clamp,0,-2000, 0,-3000, 0,-2000, 0,-2000);
|
||||
|
||||
TEST_TRINOP(timespec_clamp,0,-4000, 0,-3000, 0,3000, 0,-3000);
|
||||
TEST_TRINOP(timespec_clamp,0,-3001, 0,-3000, 0,3000, 0,-3000);
|
||||
TEST_TRINOP(timespec_clamp,0,-3000, 0,-3000, 0,3000, 0,-3000);
|
||||
TEST_TRINOP(timespec_clamp,0,-2999, 0,-3000, 0,3000, 0,-2999);
|
||||
TEST_TRINOP(timespec_clamp,0,-1500, 0,-3000, 0,3000, 0,-1500);
|
||||
TEST_TRINOP(timespec_clamp,0, -1, 0,-3000, 0,3000, 0, -1);
|
||||
TEST_TRINOP(timespec_clamp,0, 0, 0,-3000, 0,3000, 0, 0);
|
||||
TEST_TRINOP(timespec_clamp,0, 1, 0,-3000, 0,3000, 0, 1);
|
||||
TEST_TRINOP(timespec_clamp,0, 1500, 0,-3000, 0,3000, 0, 1500);
|
||||
TEST_TRINOP(timespec_clamp,0, 2999, 0,-3000, 0,3000, 0, 2999);
|
||||
TEST_TRINOP(timespec_clamp,0, 3000, 0,-3000, 0,3000, 0, 3000);
|
||||
TEST_TRINOP(timespec_clamp,0, 3001, 0,-3000, 0,3000, 0, 3000);
|
||||
TEST_TRINOP(timespec_clamp,0, 4000, 0,-3000, 0,3000, 0, 3000);
|
||||
|
||||
// timespec_min
|
||||
|
||||
TEST_BINOP(timespec_min, 0,0, 0,0, 0,0);
|
||||
TEST_BINOP(timespec_min, 0,0, 1,0, 0,0);
|
||||
TEST_BINOP(timespec_min, 1,0, 0,0, 0,0);
|
||||
TEST_BINOP(timespec_min, 1,0, 1,0, 1,0);
|
||||
TEST_BINOP(timespec_min, 10,0, 1,0, 1,0);
|
||||
TEST_BINOP(timespec_min, 10,0, 3,0, 3,0);
|
||||
TEST_BINOP(timespec_min, 10,0, -3,0, -3,0);
|
||||
TEST_BINOP(timespec_min, -10,0, 3,0, -10,0);
|
||||
TEST_BINOP(timespec_min, -10,0, -3,0, -10,0);
|
||||
TEST_BINOP(timespec_min, 10,0, 5,0, 5,0);
|
||||
TEST_BINOP(timespec_min, 10,0, -5,0, -5,0);
|
||||
TEST_BINOP(timespec_min, -10,0, 5,0, -10,0);
|
||||
TEST_BINOP(timespec_min, -10,0, -5,0, -10,0);
|
||||
TEST_BINOP(timespec_min, 1,500000000, 0,500000000, 0,500000000);
|
||||
TEST_BINOP(timespec_min, 5,500000000, 2,999999999, 2,999999999);
|
||||
TEST_BINOP(timespec_min, 0,500000000, 1,500000000, 0,500000000);
|
||||
TEST_BINOP(timespec_min, 0,0, 1,500000000, 0,0);
|
||||
TEST_BINOP(timespec_min, 1,0, 1,500000000, 1,0);
|
||||
TEST_BINOP(timespec_min, 1,0, 0,1, 0,1);
|
||||
TEST_BINOP(timespec_min, 1,123456789, 0,1000, 0,1000);
|
||||
TEST_BINOP(timespec_min, 1,0, 0,9999999, 0,9999999);
|
||||
TEST_BINOP(timespec_min, 12345,54321, 0,100001, 0,100001);
|
||||
TEST_BINOP(timespec_min, LONG_MIN,0, 0,1, LONG_MIN,0);
|
||||
TEST_BINOP(timespec_min, LONG_MIN,0, 0,-1, LONG_MIN,0);
|
||||
TEST_BINOP(timespec_min, LONG_MIN,0, LONG_MAX,0, LONG_MIN,0);
|
||||
TEST_BINOP(timespec_min, LONG_MIN,0, LONG_MIN,0, LONG_MIN,0);
|
||||
TEST_BINOP(timespec_min, LONG_MAX,0, 0,1, 0,1);
|
||||
TEST_BINOP(timespec_min, LONG_MAX,0, 0,-1, 0,-1);
|
||||
TEST_BINOP(timespec_min, LONG_MAX,0, LONG_MAX,0, LONG_MAX,0);
|
||||
TEST_BINOP(timespec_min, LONG_MAX,0, LONG_MIN,0, LONG_MIN,0);
|
||||
|
||||
// timespec_max
|
||||
|
||||
TEST_BINOP(timespec_max, 0,0, 0,0, 0,0);
|
||||
TEST_BINOP(timespec_max, 0,0, 1,0, 1,0);
|
||||
TEST_BINOP(timespec_max, 1,0, 0,0, 1,0);
|
||||
TEST_BINOP(timespec_max, 1,0, 1,0, 1,0);
|
||||
TEST_BINOP(timespec_max, 10,0, 1,0, 10,0);
|
||||
TEST_BINOP(timespec_max, 10,0, 3,0, 10,0);
|
||||
TEST_BINOP(timespec_max, 10,0, -3,0, 10,0);
|
||||
TEST_BINOP(timespec_max, -10,0, 3,0, 3,0);
|
||||
TEST_BINOP(timespec_max, -10,0, -3,0, -3,0);
|
||||
TEST_BINOP(timespec_max, 10,0, 5,0, 10,0);
|
||||
TEST_BINOP(timespec_max, 10,0, -5,0, 10,0);
|
||||
TEST_BINOP(timespec_max, -10,0, 5,0, 5,0);
|
||||
TEST_BINOP(timespec_max, -10,0, -5,0, -5,0);
|
||||
TEST_BINOP(timespec_max, 1,500000000, 0,500000000, 1,500000000);
|
||||
TEST_BINOP(timespec_max, 5,500000000, 2,999999999, 5,500000000);
|
||||
TEST_BINOP(timespec_max, 0,500000000, 1,500000000, 1,500000000);
|
||||
TEST_BINOP(timespec_max, 0,0, 1,500000000, 1,500000000);
|
||||
TEST_BINOP(timespec_max, 1,0, 1,500000000, 1,500000000);
|
||||
TEST_BINOP(timespec_max, 1,0, 0,1, 1,0);
|
||||
TEST_BINOP(timespec_max, 1,123456789, 0,1000, 1,123456789);
|
||||
TEST_BINOP(timespec_max, 1,0, 0,9999999, 1,0);
|
||||
TEST_BINOP(timespec_max, 12345,54321, 0,100001, 12345,54321);
|
||||
TEST_BINOP(timespec_max, LONG_MIN,0, 0,1, 0,1);
|
||||
TEST_BINOP(timespec_max, LONG_MIN,0, 0,-1, 0,-1);
|
||||
TEST_BINOP(timespec_max, LONG_MIN,0, LONG_MAX,0, LONG_MAX,0);
|
||||
TEST_BINOP(timespec_max, LONG_MIN,0, LONG_MIN,0, LONG_MIN,0);
|
||||
TEST_BINOP(timespec_max, LONG_MAX,0, 0,1, LONG_MAX,0);
|
||||
TEST_BINOP(timespec_max, LONG_MAX,0, 0,-1, LONG_MAX,0);
|
||||
TEST_BINOP(timespec_max, LONG_MAX,0, LONG_MAX,0, LONG_MAX,0);
|
||||
TEST_BINOP(timespec_max, LONG_MAX,0, LONG_MIN,0, LONG_MAX,0);
|
||||
|
||||
// timespec_cmp
|
||||
|
||||
TEST_TEST_FUNC(timespec_cmp, 0,0, 0,0, 0);
|
||||
TEST_TEST_FUNC(timespec_cmp, 100,0, 100,0, 0);
|
||||
TEST_TEST_FUNC(timespec_cmp, -100,0, -100,0, 0);
|
||||
|
||||
TEST_TEST_FUNC(timespec_cmp, 1,0, 0,0, 1);
|
||||
TEST_TEST_FUNC(timespec_cmp, 0,0, 1,0, -1);
|
||||
TEST_TEST_FUNC(timespec_cmp, 0,1, 0,0, 1);
|
||||
TEST_TEST_FUNC(timespec_cmp, 0,0, 0,1, -1);
|
||||
TEST_TEST_FUNC(timespec_cmp, 1,0, 0,100, 1);
|
||||
TEST_TEST_FUNC(timespec_cmp, 0,100 , 1,0, -1);
|
||||
|
||||
TEST_TEST_FUNC(timespec_cmp, -0,-0, 0,0, 0);
|
||||
TEST_TEST_FUNC(timespec_cmp, -10,-500000000, -11,500000000, 0);
|
||||
TEST_TEST_FUNC(timespec_cmp, -10,-500000001, -11,499999999, 0);
|
||||
TEST_TEST_FUNC(timespec_cmp, -10,-500000001, -11,500000001, -1);
|
||||
TEST_TEST_FUNC(timespec_cmp, -11,500000001, -10,-500000001, 1);
|
||||
|
||||
// timespec_eq
|
||||
|
||||
TEST_TEST_FUNC(timespec_eq, 0,0, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_eq, 100,0, 100,0, true);
|
||||
TEST_TEST_FUNC(timespec_eq, -200,0, -200,0, true);
|
||||
TEST_TEST_FUNC(timespec_eq, 0,300, 0,300, true);
|
||||
TEST_TEST_FUNC(timespec_eq, 0,-400, 0,-400, true);
|
||||
|
||||
TEST_TEST_FUNC(timespec_eq, 100,1, 100,0, false);
|
||||
TEST_TEST_FUNC(timespec_eq, 101,0, 100,0, false);
|
||||
TEST_TEST_FUNC(timespec_eq, -100,0, 100,0, false);
|
||||
TEST_TEST_FUNC(timespec_eq, 0,10, 0,-10, false);
|
||||
|
||||
TEST_TEST_FUNC(timespec_eq, -0,-0, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_eq, -10,-500000000, -11,500000000, true);
|
||||
TEST_TEST_FUNC(timespec_eq, -10,-500000001, -11,499999999, true);
|
||||
TEST_TEST_FUNC(timespec_eq, -10,-500000001, -11,500000001, false);
|
||||
|
||||
// timespec_gt
|
||||
|
||||
TEST_TEST_FUNC(timespec_gt, 1,0, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_gt, 0,0, -1,0, true);
|
||||
TEST_TEST_FUNC(timespec_gt, 0,1, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_gt, 0,0, 0,-1, true);
|
||||
|
||||
TEST_TEST_FUNC(timespec_gt, 1,0, 1,0, false);
|
||||
TEST_TEST_FUNC(timespec_gt, 1,1, 1,1, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -1,0, 0,0, false);
|
||||
TEST_TEST_FUNC(timespec_gt, 0,-1, 0,0, false);
|
||||
|
||||
TEST_TEST_FUNC(timespec_gt, 0,0, -0,-0, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -10,-500000000, -11,500000000, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -11,500000000, -10,-500000000, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -10,-500000001, -11,499999999, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -11,499999999, -11,499999999, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -10,-500000001, -11,500000001, false);
|
||||
TEST_TEST_FUNC(timespec_gt, -11,500000001, -10,-500000001, true);
|
||||
|
||||
// timespec_ge
|
||||
|
||||
TEST_TEST_FUNC(timespec_ge, 1,0, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_ge, 0,0, -1,0, true);
|
||||
TEST_TEST_FUNC(timespec_ge, 0,1, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_ge, 0,0, 0,-1, true);
|
||||
TEST_TEST_FUNC(timespec_ge, 1,0, 1,0, true);
|
||||
TEST_TEST_FUNC(timespec_ge, 1,1, 1,1, true);
|
||||
|
||||
TEST_TEST_FUNC(timespec_ge, -1,0, 0,0, false);
|
||||
TEST_TEST_FUNC(timespec_ge, 0,-1, 0,0, false);
|
||||
|
||||
TEST_TEST_FUNC(timespec_ge, 0,0, -0,-0, true);
|
||||
TEST_TEST_FUNC(timespec_ge, -10,-500000000, -11,500000000, true);
|
||||
TEST_TEST_FUNC(timespec_ge, -11,500000000, -10,-500000000, true);
|
||||
TEST_TEST_FUNC(timespec_ge, -10,-500000001, -11,499999999, true);
|
||||
TEST_TEST_FUNC(timespec_ge, -11,499999999, -11,499999999, true);
|
||||
TEST_TEST_FUNC(timespec_ge, -10,-500000001, -11,500000001, false);
|
||||
TEST_TEST_FUNC(timespec_ge, -11,500000001, -10,-500000001, true);
|
||||
|
||||
// timespec_lt
|
||||
|
||||
TEST_TEST_FUNC(timespec_lt, 0,0, 1,0, true);
|
||||
TEST_TEST_FUNC(timespec_lt, -1,0, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_lt, 0,0, 0,1, true);
|
||||
TEST_TEST_FUNC(timespec_lt, 0,-1, 0,0, true);
|
||||
|
||||
TEST_TEST_FUNC(timespec_lt, 1,0, 1,0, false);
|
||||
TEST_TEST_FUNC(timespec_lt, 1,1, 1,1, false);
|
||||
TEST_TEST_FUNC(timespec_lt, 0,0, -1,0, false);
|
||||
TEST_TEST_FUNC(timespec_lt, 0,0, 0,-1, false);
|
||||
|
||||
TEST_TEST_FUNC(timespec_lt, 0,0, -0,-0, false);
|
||||
TEST_TEST_FUNC(timespec_lt, -10,-500000000, -11,500000000, false);
|
||||
TEST_TEST_FUNC(timespec_lt, -11,500000000, -10,-500000000, false);
|
||||
TEST_TEST_FUNC(timespec_lt, -10,-500000001, -11,499999999, false);
|
||||
TEST_TEST_FUNC(timespec_lt, -11,499999999, -11,499999999, false);
|
||||
TEST_TEST_FUNC(timespec_lt, -10,-500000001, -11,500000001, true);
|
||||
TEST_TEST_FUNC(timespec_lt, -11,500000001, -10,-500000001, false);
|
||||
|
||||
// timespec_le
|
||||
|
||||
TEST_TEST_FUNC(timespec_le, 0,0, 1,0, true);
|
||||
TEST_TEST_FUNC(timespec_le, -1,0, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_le, 0,0, 0,1, true);
|
||||
TEST_TEST_FUNC(timespec_le, 0,-1, 0,0, true);
|
||||
TEST_TEST_FUNC(timespec_le, 1,0, 1,0, true);
|
||||
TEST_TEST_FUNC(timespec_le, 1,1, 1,1, true);
|
||||
|
||||
TEST_TEST_FUNC(timespec_le, 0,0, -1,0, false);
|
||||
TEST_TEST_FUNC(timespec_le, 0,0, 0,-1, false);
|
||||
|
||||
TEST_TEST_FUNC(timespec_le, 0,0, -0,-0, true);
|
||||
TEST_TEST_FUNC(timespec_le, -10,-500000000, -11,500000000, true);
|
||||
TEST_TEST_FUNC(timespec_le, -11,500000000, -10,-500000000, true);
|
||||
TEST_TEST_FUNC(timespec_le, -10,-500000001, -11,499999999, true);
|
||||
TEST_TEST_FUNC(timespec_le, -11,499999999, -11,499999999, true);
|
||||
TEST_TEST_FUNC(timespec_le, -10,-500000001, -11,500000001, true);
|
||||
TEST_TEST_FUNC(timespec_le, -11,500000001, -10,-500000001, false);
|
||||
|
||||
// timespec_from_double
|
||||
|
||||
TEST_FROM_DOUBLE(0.0, 0,0);
|
||||
TEST_FROM_DOUBLE(10.0, 10,0);
|
||||
TEST_FROM_DOUBLE(-10.0, -10,0);
|
||||
TEST_FROM_DOUBLE(0.5, 0,500000000);
|
||||
TEST_FROM_DOUBLE(-0.5, -1,500000000);
|
||||
TEST_FROM_DOUBLE(10.5, 10,500000000);
|
||||
TEST_FROM_DOUBLE(-10.5, -11,500000000);
|
||||
|
||||
// timespec_to_double
|
||||
|
||||
TEST_TO_DOUBLE(0,0, 0.0);
|
||||
TEST_TO_DOUBLE(10,0, 10.0);
|
||||
TEST_TO_DOUBLE(-10,0, -10.0);
|
||||
TEST_TO_DOUBLE(0,500000000, 0.5);
|
||||
TEST_TO_DOUBLE(0,-500000000, -0.5);
|
||||
TEST_TO_DOUBLE(10,500000000, 10.5);
|
||||
TEST_TO_DOUBLE(10,-500000000, 9.5);
|
||||
TEST_TO_DOUBLE(-10,500000000, -9.5);
|
||||
TEST_TO_DOUBLE(-10,-500000000, -10.5);
|
||||
|
||||
// timespec_from_timeval
|
||||
|
||||
TEST_FROM_TIMEVAL(0,0, 0,0);
|
||||
TEST_FROM_TIMEVAL(1,0, 1,0);
|
||||
TEST_FROM_TIMEVAL(1000,0, 1000,0);
|
||||
TEST_FROM_TIMEVAL(0,0, 0,0);
|
||||
TEST_FROM_TIMEVAL(-1,0, -1,0);
|
||||
TEST_FROM_TIMEVAL(-1000,0, -1000,0);
|
||||
|
||||
TEST_FROM_TIMEVAL(1,1, 1,1000);
|
||||
TEST_FROM_TIMEVAL(1,1000, 1,1000000);
|
||||
TEST_FROM_TIMEVAL(1,-1, 0,999999000);
|
||||
TEST_FROM_TIMEVAL(1,-1000, 0,999000000);
|
||||
TEST_FROM_TIMEVAL(-1,-1, -2,999999000);
|
||||
TEST_FROM_TIMEVAL(-1,-1000, -2,999000000);
|
||||
|
||||
// timespec_to_timeval
|
||||
|
||||
TEST_TO_TIMEVAL(0,0, 0,0);
|
||||
TEST_TO_TIMEVAL(1,0, 1,0);
|
||||
TEST_TO_TIMEVAL(10,0, 10,0);
|
||||
TEST_TO_TIMEVAL(-1,0, -1,0);
|
||||
TEST_TO_TIMEVAL(-10,0, -10,0);
|
||||
|
||||
TEST_TO_TIMEVAL(1,1, 1,0);
|
||||
TEST_TO_TIMEVAL(1,999, 1,0);
|
||||
TEST_TO_TIMEVAL(1,1000, 1,1);
|
||||
TEST_TO_TIMEVAL(1,1001, 1,1);
|
||||
TEST_TO_TIMEVAL(1,2000, 1,2);
|
||||
TEST_TO_TIMEVAL(1,2000000, 1,2000);
|
||||
|
||||
TEST_TO_TIMEVAL(1,-1, 0,999999);
|
||||
TEST_TO_TIMEVAL(1,-999, 0,999999);
|
||||
TEST_TO_TIMEVAL(1,-1000, 0,999999);
|
||||
TEST_TO_TIMEVAL(1,-1001, 0,999998);
|
||||
TEST_TO_TIMEVAL(1,-2000, 0,999998);
|
||||
TEST_TO_TIMEVAL(1,-2000000, 0,998000);
|
||||
|
||||
TEST_TO_TIMEVAL(-1,-1, -2,999999);
|
||||
TEST_TO_TIMEVAL(-1,-999, -2,999999);
|
||||
TEST_TO_TIMEVAL(-1,-1000, -2,999999);
|
||||
TEST_TO_TIMEVAL(-1,-1001, -2,999998);
|
||||
TEST_TO_TIMEVAL(-1,-2000, -2,999998);
|
||||
TEST_TO_TIMEVAL(-1,-2000000, -2,998000);
|
||||
|
||||
TEST_TO_TIMEVAL(1,1500000000, 2,500000);
|
||||
TEST_TO_TIMEVAL(1,-1500000000, -1,500000);
|
||||
TEST_TO_TIMEVAL(-1,-1500000000, -3,500000);
|
||||
|
||||
// timespec_from_ms
|
||||
|
||||
TEST_FROM_MS(0, 0,0);
|
||||
TEST_FROM_MS(1, 0,1000000);
|
||||
TEST_FROM_MS(-1, -1,999000000);
|
||||
TEST_FROM_MS(1500, 1,500000000);
|
||||
TEST_FROM_MS(-1000, -1,0);
|
||||
TEST_FROM_MS(-1500, -2,500000000);
|
||||
|
||||
// timespec_to_ms
|
||||
|
||||
TEST_TO_MS(0,0, 0);
|
||||
TEST_TO_MS(10,0, 10000);
|
||||
TEST_TO_MS(-10,0, -10000);
|
||||
TEST_TO_MS(0,500000000, 500);
|
||||
TEST_TO_MS(0,-500000000, -500);
|
||||
TEST_TO_MS(10,500000000, 10500);
|
||||
TEST_TO_MS(10,-500000000, 9500);
|
||||
TEST_TO_MS(-10,500000000, -9500);
|
||||
TEST_TO_MS(-10,-500000000, -10500);
|
||||
|
||||
// timespec_normalise
|
||||
|
||||
TEST_NORMALISE(0,0, 0,0);
|
||||
|
||||
TEST_NORMALISE(0,1000000000, 1,0);
|
||||
TEST_NORMALISE(0,1500000000, 1,500000000);
|
||||
TEST_NORMALISE(0,-1000000000, -1,0);
|
||||
TEST_NORMALISE(0,-1500000000, -2,500000000);
|
||||
|
||||
TEST_NORMALISE(5,1000000000, 6,0);
|
||||
TEST_NORMALISE(5,1500000000, 6,500000000);
|
||||
TEST_NORMALISE(-5,-1000000000, -6,0);
|
||||
TEST_NORMALISE(-5,-1500000000, -7,500000000);
|
||||
|
||||
TEST_NORMALISE(0,2000000000, 2,0);
|
||||
TEST_NORMALISE(0,2100000000, 2,100000000);
|
||||
TEST_NORMALISE(0,-2000000000, -2,0);
|
||||
TEST_NORMALISE(0,-2100000000, -3,900000000);
|
||||
|
||||
TEST_NORMALISE(1,-500000001, 0,499999999);
|
||||
TEST_NORMALISE(1,-500000000, 0,500000000);
|
||||
TEST_NORMALISE(1,-499999999, 0,500000001);
|
||||
TEST_NORMALISE(0,-499999999, -1,500000001);
|
||||
|
||||
TEST_NORMALISE(-1,500000000, -1,500000000);
|
||||
TEST_NORMALISE(-1,499999999, -1,499999999);
|
||||
|
||||
if(result > 0)
|
||||
{
|
||||
printf("%d tests failed\n", result);
|
||||
}
|
||||
else{
|
||||
printf("All tests passed\n");
|
||||
}
|
||||
|
||||
return !!result; /* Don't overflow the exit status */
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/* Functions for working with timespec structures
|
||||
* Written by Daniel Collins (2017-2021)
|
||||
* timespec_mod by Alex Forencich (2019)
|
||||
* Various contributions by Ingo Albrecht (2021)
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
*
|
||||
* Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
* distribute this software, either in source code form or as a compiled
|
||||
* binary, for any purpose, commercial or non-commercial, and by any
|
||||
* means.
|
||||
*
|
||||
* In jurisdictions that recognize copyright laws, the author or authors
|
||||
* of this software dedicate any and all copyright interest in the
|
||||
* software to the public domain. We make this dedication for the benefit
|
||||
* of the public at large and to the detriment of our heirs and
|
||||
* successors. We intend this dedication to be an overt act of
|
||||
* relinquishment in perpetuity of all present and future rights to this
|
||||
* software under copyright law.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* For more information, please refer to <http://unlicense.org/>
|
||||
*/
|
||||
|
||||
#ifndef DAN_TIMESPEC_H
|
||||
#define DAN_TIMESPEC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct timespec timespec_add(struct timespec ts1, struct timespec ts2);
|
||||
struct timespec timespec_sub(struct timespec ts1, struct timespec ts2);
|
||||
struct timespec timespec_mod(struct timespec ts1, struct timespec ts2);
|
||||
|
||||
struct timespec timespec_min(struct timespec ts1, struct timespec ts2);
|
||||
struct timespec timespec_max(struct timespec ts1, struct timespec ts2);
|
||||
struct timespec timespec_clamp(struct timespec ts1, struct timespec min, struct timespec max);
|
||||
|
||||
int timespec_cmp(struct timespec ts1, struct timespec ts2);
|
||||
bool timespec_eq(struct timespec ts1, struct timespec ts2);
|
||||
bool timespec_gt(struct timespec ts1, struct timespec ts2);
|
||||
bool timespec_ge(struct timespec ts1, struct timespec ts2);
|
||||
bool timespec_lt(struct timespec ts1, struct timespec ts2);
|
||||
bool timespec_le(struct timespec ts1, struct timespec ts2);
|
||||
|
||||
struct timespec timespec_from_double(double s);
|
||||
double timespec_to_double(struct timespec ts);
|
||||
struct timespec timespec_from_timeval(struct timeval tv);
|
||||
struct timeval timespec_to_timeval(struct timespec ts);
|
||||
struct timespec timespec_from_ms(long milliseconds);
|
||||
long timespec_to_ms(struct timespec ts);
|
||||
|
||||
struct timespec timespec_normalise(struct timespec ts);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !DAN_TIMESPEC_H */
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -321,7 +321,7 @@ class HamlibHandler:
|
|||
except:
|
||||
self.ErrParam()
|
||||
else:
|
||||
if (not x) and self.app.ptt:
|
||||
if (not x) and self.app.ptt and self.app.timesBeforeKill == 0:
|
||||
# Sleep for 20ms to match typical SDR behavior + 60ms to account for varying system load/virtual audio latency.
|
||||
# References:
|
||||
# Virtual audio latency: https://vb-audio.com/Cable/VBCABLE_ReferenceManual.pdf (assuming 20ms/1024 sample buffer size @ 48 kHz)
|
||||
|
|
@ -332,6 +332,7 @@ class HamlibHandler:
|
|||
self.app.ptt = 1
|
||||
else:
|
||||
self.app.ptt = 0
|
||||
self.app.timesBeforeKill = self.app.timesBeforeKill - 1
|
||||
def GetSplitVfo(self):
|
||||
self.Reply('SPLIT', self.app.splitenable, 'TXVFO', self.app.txvfo, 0)
|
||||
def SetSplitVfo(self):
|
||||
|
|
@ -369,8 +370,9 @@ class HamlibHandler:
|
|||
|
||||
class App:
|
||||
"""This is the main application class. It listens for connectons from clients and creates a server for each one."""
|
||||
def __init__(self, pid):
|
||||
def __init__(self, pid, timesBeforeKill):
|
||||
self.pid = pid
|
||||
self.timesBeforeKill = timesBeforeKill
|
||||
self.hamlib_clients = []
|
||||
self.hamlib_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
|
|
@ -412,8 +414,12 @@ class App:
|
|||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
if len(sys.argv) != 2:
|
||||
if len(sys.argv) < 2:
|
||||
raise RuntimeError("A PID for the process to kill on TX->RX is required")
|
||||
App(int(sys.argv[1])).Run()
|
||||
if len(sys.argv) != 3:
|
||||
timesBeforeKill = 100
|
||||
else:
|
||||
timesBeforeKill = int(sys.argv[2])
|
||||
App(int(sys.argv[1]), timesBeforeKill).Run()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(0)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ fi
|
|||
|
||||
createVirtualAudioCable () {
|
||||
CABLE_NAME=$1
|
||||
pactl load-module module-null-sink sink_name=$CABLE_NAME sink_properties=device.description=$CABLE_NAME latency_msec=1
|
||||
pactl load-module module-null-sink sink_name=$CABLE_NAME sink_properties=device.description=$CABLE_NAME
|
||||
}
|
||||
|
||||
FREEDV_RADIO_TO_COMPUTER_DEVICE="${FREEDV_RADIO_TO_COMPUTER_DEVICE:-FreeDV_Radio_To_Computer}"
|
||||
|
|
@ -26,7 +26,7 @@ if [ "$OPERATING_SYSTEM" == "Linux" ]; then
|
|||
DRIVER_INDEX_FREEDV_COMPUTER_TO_SPEAKER=$(createVirtualAudioCable FreeDV_Computer_To_Speaker)
|
||||
DRIVER_INDEX_FREEDV_MICROPHONE_TO_COMPUTER=$(createVirtualAudioCable FreeDV_Microphone_To_Computer)
|
||||
DRIVER_INDEX_FREEDV_COMPUTER_TO_RADIO=$(createVirtualAudioCable FreeDV_Computer_To_Radio)
|
||||
DRIVER_INDEX_LOOPBACK=`pactl load-module module-loopback source="FreeDV_Computer_To_Radio.monitor" sink="FreeDV_Radio_To_Computer" latency_msec=1`
|
||||
DRIVER_INDEX_LOOPBACK=`pactl load-module module-loopback source="FreeDV_Computer_To_Radio.monitor" sink="FreeDV_Radio_To_Computer"`
|
||||
fi
|
||||
|
||||
# Determine correct record device to retrieve TX data
|
||||
|
|
@ -71,14 +71,19 @@ fi
|
|||
RECORD_PID=$!
|
||||
|
||||
# Start "radio"
|
||||
python3 $SCRIPTPATH/hamlibserver.py $RECORD_PID &
|
||||
if [ "$2" == "mpp" ]; then
|
||||
TIMES_BEFORE_KILL=5
|
||||
else
|
||||
TIMES_BEFORE_KILL=1
|
||||
fi
|
||||
python3 $SCRIPTPATH/hamlibserver.py $RECORD_PID $TIMES_BEFORE_KILL &
|
||||
RADIO_PID=$!
|
||||
|
||||
# Start FreeDV in test mode to record TX
|
||||
if [ "$2" == "mpp" ]; then
|
||||
TX_ARGS="-txtime 1 -txattempts 6 "
|
||||
else
|
||||
TX_ARGS="-txtime 5 "
|
||||
TX_ARGS="-txtime 1 -txattempts 2 "
|
||||
fi
|
||||
$FREEDV_BINARY -f $(pwd)/$FREEDV_CONF_FILE -ut tx -utmode RADE $TX_ARGS >tmp.log 2>&1 &
|
||||
|
||||
|
|
@ -99,11 +104,8 @@ cat tmp.log
|
|||
kill $RECORD_PID
|
||||
|
||||
if [ "$1" != "" ]; then
|
||||
FADING_DIR="$(pwd)/fading"
|
||||
if [ ! -d "$FADING_DIR" ]; then
|
||||
mkdir $FADING_DIR
|
||||
(cd $1/../codec2_src/unittest && ./fading_files.sh $FADING_DIR)
|
||||
fi
|
||||
FADING_DIR="$SCRIPTPATH/fading"
|
||||
|
||||
# Add noise to recording to test performance
|
||||
if [ "$2" == "mpp" ]; then
|
||||
sox $(pwd)/test.wav -t raw -r 8000 -c 1 -e signed-integer -b 16 - | $1/src/ch - - --No -24 --mpp --fading_dir $FADING_DIR | sox -t raw -r 8000 -c 1 -e signed-integer -b 16 - -t wav $(pwd)/testwithnoise.wav
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ FREEDV_RX_FILE=$3
|
|||
# Determine sox driver to use for recording/playback
|
||||
OPERATING_SYSTEM=`uname`
|
||||
SOX_DRIVER=alsa
|
||||
FREEDV_BINARY=src/freedv
|
||||
FREEDV_BINARY=${FREEDV_BINARY:-src/freedv}
|
||||
if [ "$OPERATING_SYSTEM" == "Darwin" ]; then
|
||||
SOX_DRIVER=coreaudio
|
||||
FREEDV_BINARY=src/FreeDV.app/Contents/MacOS/freedv
|
||||
|
|
|
|||
Loading…
Reference in New Issue