Add tests to verify lack of sync loss in RX (#761)

* Add logic for automatically running RX/TX tests.

* Add GH action to load loopback audio driver.

* Add ctest configuration file.

* Try loading modules after installing packages.

* Add additional required packages.

* Add automated test script.

* Temporarily disable GH action changes.

* Have RX take 60 seconds as well.

* Ubuntu upgrade broke /bin/sh.

* Fix issue preventing no zero message from printing.

* Print sync status once per second and validate no resyncs.

* Fix spelling error.

* Switch to RADE main.

* Another attempt at enabling aloop in the GH action.

* Use _ instead of - for module name.

* Use pacmd to create virtual sound devices instead.

* Check sync state every 100ms instead of every 1s.

* Fix issue preventing sync changes from making it to the log.

* Add full duplex test.

* Add full duplex tests to CMakeLists.

* Generate sine wave for TX.

* Update GH action to allow new ctests to run.

* Add debugging for UT.

* pipewire-pulse is needed to actually use PipeWire.

* Install suggested packages to get additional required commands.

* Can't use --install-suggests, so explicitly mention packages.

* Enable pipewire service.

* Fix call to helper Python script.

* Show status of pipewire prior to running tests.

* Remove status display from GH action.

* Use module-loopback to simulate TX plugged into RX.

* Try explicitly starting pipewire.

* Use strict 20ms cadence for TX/RX thread instead of relying on the audio subsystem.

* Need to install wireplumber as well.

* Update regular expression in ctest as sync change message isn't printed unless there's a problem.

* Add RADE to ctests.

* RADE ctest pass wasn't actually a pass.

* Need to explicitly check for LINUX for PulseAudio based ctests.

* Reenable PortAudio build.

* Invert test criteria.

* Show pipewire status just before thye test run.

* List sink info prior to each test.

* Make sure virtual devices aren't suspended.

* Need to have the suspension disable code in another file.

* Actually, installing pulseaudio isn't a good idea.

* Need a window manager for things to refresh properly.

* Display wireplumber info after FDV start.

* Add additional sleep to guarantee that FreeDV is listening when wireplumber status is printed.

* Add pw-top for additional info.

* dbus needs to be running for pipewire to work.

* Need to wait a bit for Xvfb to become available.

* Get additional pipewire logs prior to UT run.

* Install/run rtkit too.

* Attempt to get pipewire logs after test execution.

* Looks like we still need to start pipewire manually.

* Further increase pipewire debugging.

* Too much debugging output before.

* Upgrade packages first before installing new ones.

* We weren't actually running 24.04 on GH.

* Adjust packages due to using 24.04.

* Suppress spellcheck issue.

* Disable pipewire debugging.

* Test changes to PulseAudioDevice to improve dropouts.

* Update tests to reflect 1 resync per 120s proposed threshold.

* Disable additional debugging output.

* Allow override of test audio devices from the command line.

* Enable ctests on macOS.

* getSync() isn't thread safe, causing false UT failures.

* Install virtual audio devices in CI environment for macOS.

* Use script to build drivers instead.

* Oops, forgot to remove some code first.

* Adjust permissions of script.

* Make tests work on Linux again with loopback devices.

* Attempt change to make virtual audio devices actually build.

* Try building on x86 macOS.

* Fix quote escape issues.

* More quote escape issues.

* Forgot missed folder change.

* Enable mirrored virtual audio devices.

* Fix incorrect device names in ctest execution.

* Need to append '2ch' to the other device names too.

* Clear sync when transmitting.

* Grant microphone access to FreeDV.

* Need to back out one of the changes due to voice keyer problems.

* Attempt to fix CI failures.

* Take screenshot of macOS CI environment for debugging.

* Let's try running the script separately to get a screenshot.

* Screenshot was in the wrong place.

* Need to grant access to provisioner for microphone.

* Alternate way of granting permissions.

* Oops, remove call to tccutil.

* Per PLT meeting, we shouldn't drop sync at all.

* Shorten test time to 60s.

* More tweaks to make sure that the devices are correct on Linux.

* Disable screen capture.

* Additional fixes to resolve script problems on macOS.

* I don't think previous PulseAudio changes made anything better.

* Improve dropouts when using PulseAudio/pipewire.

* Revert "Improve dropouts when using PulseAudio/pipewire."

This reverts commit f16c27f4d3.

* Have stream write callback get audio data from higher level code.

* Add PowerShell script for Windows testing.

* Move ctest files into a separate location for organization purposes.

* Add parameters for PowerShell test script.

* Add help text for the script.

* Update conf file path due to previous commits.

* Fix syntax errors in script.

* Experiment: Test Windows installer on GH action.

* Try actually running full duplex test inside Windows.

* Add 5 seconds to allow Xvfb to fully come up.

* Update other actions to use latest checkout.

* Need VS command prompt.

* Add additional debugging.

* Maybe we need to throw an exception to make the GH action fail.

* List devices another way.

* Add yet more debugging.

* Fix audio devices.

* Try with 700D to at least make sure we're on the right track.

* Fix GH action.

* Fix syntax errors.

* Try installing VAC instead as FreeDV is crashing with Scream.

* Fix syntax error.

* Try different hardware ID.

* Ignore error code that actually means success.

* Oops, fix syntax error.

* Try earlier version of VAC.

* Fix archive commands.

* Fix path.

* Probably need to start AudioEndpointBuilder too.

* Stop services first.

* Print out any errors that might be getting thrown.

* Revert "Print out any errors that might be getting thrown."

This reverts commit 15e3908c80.

* Disable test GH action for Windows and investigate failure later.

* Reenable Windows testing with new way of debugging.

* Add logging based on clues from Windows event log.

* Revert "Add logging based on clues from Windows event log."

This reverts commit de4f11362e.

* Use procdump to get a crash dump.

* Full dump seems to take a while.

* Run procdump in the background.

* Log audio errors.

* Print device name in error message.

* Make sure message box doesn't display.

* Enable additional PortAudio debugging.

* Add yet more logging.

* Add additional Registry key to ensure FreeDV has microphone permissions.

* Need to add Registry key.

* Add more keys to try to force microphone permission.

* Clean up debugging, add tests for RADE/700E/1600.

* More cleanup.
ms-log-library
Mooneer Salem 2024-11-23 08:08:40 -08:00 committed by GitHub
parent 36566a7f22
commit 0d40130f2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1151 additions and 33 deletions

View File

@ -15,33 +15,64 @@ jobs:
# well on Windows or Mac. You can convert this to a matrix build if you need
# cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install packages
shell: bash
run: |
sudo apt-get update
sudo apt-get install codespell libpulse-dev libspeexdsp-dev libsamplerate0-dev sox git libwxgtk3.0-gtk3-dev portaudio19-dev libhamlib-dev libasound2-dev libao-dev libgsm1-dev libsndfile-dev python3-numpy
sudo apt-get upgrade -y
sudo apt-get install codespell libpulse-dev libspeexdsp-dev libsamplerate0-dev sox git libwxgtk3.2-dev portaudio19-dev libhamlib-dev libasound2-dev libao-dev libgsm1-dev libsndfile-dev xvfb pipewire pulseaudio-utils pipewire-pulse wireplumber metacity dbus-x11 at-spi2-core rtkit
- name: Spellcheck codebase
shell: bash
run: codespell --ignore-words-list=radae,rade,inout,nin,ontop,parm,tthe,ue `find src -name '*.c*' -o -name '*.h' -o -name '*.mm'`
run: codespell --ignore-words-list=caf,radae,rade,inout,nin,ontop,parm,tthe,ue `find src -name '*.c*' -o -name '*.h' -o -name '*.mm'`
- 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 PortAudio
shell: bash
working-directory: ${{github.workspace}}
run: UT_ENABLE=1 ./build_linux.sh portaudio
run: |
. ./rade-venv/bin/activate
UT_ENABLE=1 ./build_linux.sh portaudio
- name: Build freedv-gui using PulseAudio
shell: bash
working-directory: ${{github.workspace}}
run: UT_ENABLE=1 ./build_linux.sh pulseaudio
run: |
. ./rade-venv/bin/activate
UT_ENABLE=1 ./build_linux.sh pulseaudio
- name: Execute unit tests
shell: bash
working-directory: ${{github.workspace}}/build_linux
run: make test
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
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

View File

@ -18,20 +18,31 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install packages
shell: bash
working-directory: ${{github.workspace}}
run: brew install automake libtool numpy
run: brew install automake libtool numpy sox
- name: Install virtual audio devices
shell: bash
working-directory: ${{github.workspace}}
run: ./build_macos_sound_drivers.sh
- name: Build freedv-gui
shell: bash
working-directory: ${{github.workspace}}
run: UT_ENABLE=1 ./build_osx.sh
- name: Workaround macOS permission issues
run: |
sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/usr/local/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159,NULL,NULL,'UNUSED',1687786159);"
sqlite3 $HOME/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT OR IGNORE INTO access VALUES ('kTCCServiceMicrophone','/opt/off/opt/runner/provisioner/provisioner',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159,NULL,NULL,'UNUSED',1687786159);"
- name: Execute unit tests
shell: bash
working-directory: ${{github.workspace}}/build_osx
run: make test
run: |
FREEDV_COMPUTER_TO_RADIO_DEVICE="BlackHoleRadio 2ch" FREEDV_RADIO_TO_COMPUTER_DEVICE="BlackHoleRadio 2ch 2" FREEDV_COMPUTER_TO_SPEAKER_DEVICE="BlackHole1 2ch" FREEDV_MICROPHONE_TO_COMPUTER_DEVICE="BlackHole2 2ch" ctest -V

View File

@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install WINE
run: |
@ -48,6 +48,7 @@ jobs:
export WINEPREFIX=`pwd`/wine-env
wget https://www.python.org/ftp/python/3.12.7/python-3.12.7-amd64.exe
Xvfb :99 -screen 0 1024x768x16 &
sleep 10
export DISPLAY=:99.0
wine ./python-3.12.7-amd64.exe /quiet /log c:\\python.log InstallAllUsers=1 Include_doc=0 Include_tcltk=0 || :
cat $WINEPREFIX/drive_c/python.log
@ -76,3 +77,105 @@ jobs:
run: |
export PATH=${{github.workspace}}/llvm-mingw-20230320-ucrt-ubuntu-18.04-x86_64/bin:$PATH
make -j6 package
- name: Rename installer
shell: bash
working-directory: ${{github.workspace}}/build_windows
run: |
mv FreeDV*.exe FreeDV.exe
- name: Stash for next step
uses: actions/upload-artifact@v4
with:
name: FreeDVSetupProgram
path: ${{github.workspace}}/build_windows/FreeDV.exe
test:
runs-on: windows-latest
needs: build
env:
RADIO_TO_COMPUTER_DEVICE: "CABLE Output (VB-Audio Virtual Cable)"
COMPUTER_TO_RADIO_DEVICE: "Speakers (VB-Audio Virtual Cable)"
MICROPHONE_TO_COMPUTER_DEVICE: "Line 1 (Virtual Audio Cable)"
COMPUTER_TO_SPEAKER_DEVICE: "Line 1 (Virtual Audio Cable)"
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: FreeDVSetupProgram
path: ${{github.workspace}}
- uses: ilammy/msvc-dev-cmd@v1
- name: Install FreeDV on hard drive
shell: pwsh
run: |
.\FreeDV.exe /S /D=${{github.workspace}}\FreeDV-Install-Location | Out-Null
- name: Copy test script to install folder
shell: pwsh
run: |
Copy-Item -Path ${{github.workspace}}/test/TestFreeDVFullDuplex.ps1 -Destination ${{github.workspace}}\FreeDV-Install-Location\bin
Copy-Item -Path ${{github.workspace}}/test/freedv-ctest-fullduplex.conf.tmpl -Destination ${{github.workspace}}\FreeDV-Install-Location\bin
- name: Install VB-Cable ("Radio" sound device)
uses: LABSN/sound-ci-helpers@v1
- run: 'Invoke-WebRequest https://software.muzychenko.net/trials/vac464.zip -OutFile vac464.zip'
- run: 'Expand-Archive -Path vac464.zip -DestinationPath vac464'
- run: 'Import-Certificate -FilePath ${{github.workspace}}\test\vac464.cer -CertStoreLocation Cert:\LocalMachine\root'
- run: 'Import-Certificate -FilePath ${{github.workspace}}\test\vac464.cer -CertStoreLocation Cert:\LocalMachine\TrustedPublisher'
- name: Install driver
shell: pwsh
run: |
.\vac464\setup64.exe -s -k 30570681-0a8b-46e5-8cb2-d835f43af0c5 | Out-Null
Start-Sleep -Seconds 10
# For convenience, make sure we fail fast if for whatever reason the install gets blocked on some GUI prompt.
timeout-minutes: 5
- name: Grant FreeDV access to the microphone
run: |
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone" -Name Value -Value Allow
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone" -Name Value -Value Allow
New-Item -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone\" -Name "NonPackaged" -Force
Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone\NonPackaged" -Name Value -Value Allow
New-Item -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\" -Name "AppPrivacy" -Force
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppPrivacy" -Name LetAppsAccessMicrophone -Value 0
- name: Start Windows Audio Service
run: |
net start audiosrv
- name: List audio devices
shell: pwsh
run: |
Get-CimInstance win32_sounddevice
- name: Test RADE
shell: pwsh
working-directory: ${{github.workspace}}\FreeDV-Install-Location\bin
run: |
.\TestFreeDVFullDuplex.ps1 -RadioToComputerDevice "${{env.RADIO_TO_COMPUTER_DEVICE}}" -ComputerToRadioDevice "${{env.COMPUTER_TO_RADIO_DEVICE}}" -MicrophoneToComputerDevice "${{env.MICROPHONE_TO_COMPUTER_DEVICE}}" -ComputerToSpeakerDevice "${{env.COMPUTER_TO_SPEAKER_DEVICE}}" -ModeToTest RADE -NumberOfRuns 1
timeout-minutes: 5
- name: Test 700D
shell: pwsh
working-directory: ${{github.workspace}}\FreeDV-Install-Location\bin
run: |
.\TestFreeDVFullDuplex.ps1 -RadioToComputerDevice "${{env.RADIO_TO_COMPUTER_DEVICE}}" -ComputerToRadioDevice "${{env.COMPUTER_TO_RADIO_DEVICE}}" -MicrophoneToComputerDevice "${{env.MICROPHONE_TO_COMPUTER_DEVICE}}" -ComputerToSpeakerDevice "${{env.COMPUTER_TO_SPEAKER_DEVICE}}" -ModeToTest 700D -NumberOfRuns 1
timeout-minutes: 5
- name: Test 700E
shell: pwsh
working-directory: ${{github.workspace}}\FreeDV-Install-Location\bin
run: |
.\TestFreeDVFullDuplex.ps1 -RadioToComputerDevice "${{env.RADIO_TO_COMPUTER_DEVICE}}" -ComputerToRadioDevice "${{env.COMPUTER_TO_RADIO_DEVICE}}" -MicrophoneToComputerDevice "${{env.MICROPHONE_TO_COMPUTER_DEVICE}}" -ComputerToSpeakerDevice "${{env.COMPUTER_TO_SPEAKER_DEVICE}}" -ModeToTest 700E -NumberOfRuns 1
timeout-minutes: 5
- name: Test 1600
shell: pwsh
working-directory: ${{github.workspace}}\FreeDV-Install-Location\bin
run: |
.\TestFreeDVFullDuplex.ps1 -RadioToComputerDevice "${{env.RADIO_TO_COMPUTER_DEVICE}}" -ComputerToRadioDevice "${{env.COMPUTER_TO_RADIO_DEVICE}}" -MicrophoneToComputerDevice "${{env.MICROPHONE_TO_COMPUTER_DEVICE}}" -ComputerToSpeakerDevice "${{env.COMPUTER_TO_SPEAKER_DEVICE}}" -ModeToTest 1600 -NumberOfRuns 1
timeout-minutes: 5

View File

@ -719,3 +719,17 @@ elseif(UNIX AND NOT APPLE)
include(CPack)
endif(WIN32)
if(UNITTEST)
# The below tests are currently Linux-only due to a dependency on
# PulseAudio/pipewire.
macro(DefineAudioTest utName)
add_test(NAME fullduplex_${utName} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test/test_zeros.sh txrx ${utName})
set_tests_properties(fullduplex_${utName} PROPERTIES PASS_REGULAR_EXPRESSION "Got 1 sync changes")
endmacro()
DefineAudioTest(RADE)
DefineAudioTest(700D)
DefineAudioTest(700E)
DefineAudioTest(1600)
endif(UNITTEST)

View File

@ -0,0 +1,51 @@
#!/bin/bash
git clone https://github.com/tmiw/BlackHole.git
cd BlackHole
bundleID=audio.existential.BlackHoleRadio
driverName=BlackHoleRadio
xcodebuild \
-project BlackHole.xcodeproj \
-configuration Release \
-target BlackHole \
CONFIGURATION_BUILD_DIR=build \
PRODUCT_BUNDLE_IDENTIFIER=$bundleID \
GCC_PREPROCESSOR_DEFINITIONS="$GCC_PREPROCESSOR_DEFINITIONS \
kNumber_Of_Channels='2' \
kPlugIn_BundleID='\"$bundleID\"' \
kDriver_Name='\"$driverName\"' \
kDevice2_IsHidden=false \
kDevice2_HasInput=true \
kDevice2_HasOutput=true" \
MACOSX_DEPLOYMENT_TARGET=10.13
sudo mv build/BlackHole.driver /Library/Audio/Plug-Ins/HAL/$driverName.driver
for i in {1..2}; do
git reset --hard
rm -rf build
export bundleID=audio.existential.BlackHole$i
export driverName=BlackHole$i
xcodebuild \
-project BlackHole.xcodeproj \
-configuration Release \
-target BlackHole \
CONFIGURATION_BUILD_DIR=build \
PRODUCT_BUNDLE_IDENTIFIER=$bundleID \
GCC_PREPROCESSOR_DEFINITIONS="$GCC_PREPROCESSOR_DEFINITIONS \
kNumber_Of_Channels='2' \
kPlugIn_BundleID='\"$bundleID\"' \
kDriver_Name='\"$driverName\"' \
kDevice2_IsHidden=false \
kDevice2_HasInput=true \
kDevice2_HasOutput=true" \
MACOSX_DEPLOYMENT_TARGET=10.13
sudo mv build/BlackHole.driver /Library/Audio/Plug-Ins/HAL/$driverName.driver
done
sudo killall -9 coreaudiod

View File

@ -13,7 +13,7 @@ ExternalProject_Add(build_rade
SOURCE_DIR rade_src
BINARY_DIR rade_build
GIT_REPOSITORY https://github.com/drowe67/radae.git
GIT_TAG dr-reset
GIT_TAG main
CMAKE_ARGS ${RADE_CMAKE_ARGS}
#CMAKE_CACHE_ARGS -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=${CMAKE_OSX_DEPLOYMENT_TARGET}
INSTALL_COMMAND ""

View File

@ -1,4 +1,5 @@
#set(BUILD_SHARED_LIBS OFF CACHE STRING "Disable shared libraries for portaudio")
#set(PA_ENABLE_DEBUG_OUTPUT ON CACHE STRING "Enable debug output")
include(FetchContent)
FetchContent_Declare(

View File

@ -145,6 +145,7 @@ void PulseAudioDevice::start()
outputPendingLength_ = 0;
targetOutputPendingLength_ = PULSE_FPB * getNumChannels() * 2;
outputPendingThreadActive_ = true;
#if 0
if (direction_ == IAudioEngine::AUDIO_ENGINE_OUT)
{
outputPendingThread_ = new std::thread([&]() {
@ -200,6 +201,7 @@ void PulseAudioDevice::start()
});
assert(outputPendingThread_ != nullptr);
}
#endif
}
pa_threaded_mainloop_unlock(mainloop_);
@ -268,6 +270,7 @@ void PulseAudioDevice::StreamWriteCallback_(pa_stream *s, size_t length, void *u
memset(data, 0, sizeof(data));
PulseAudioDevice* thisObj = static_cast<PulseAudioDevice*>(userdata);
#if 0
{
std::unique_lock<std::mutex> lk(thisObj->outputPendingMutex_);
if (thisObj->outputPendingLength_ >= numSamples)
@ -286,7 +289,12 @@ void PulseAudioDevice::StreamWriteCallback_(pa_stream *s, size_t length, void *u
thisObj->targetOutputPendingLength_ = std::max(thisObj->targetOutputPendingLength_, 2 * numSamples);
}
#endif
if (thisObj->onAudioDataFunction)
{
thisObj->onAudioDataFunction(*thisObj, data, numSamples / thisObj->getNumChannels(), thisObj->onAudioDataState);
}
pa_stream_write(s, &data[0], length, NULL, 0LL, PA_SEEK_RELATIVE);
}
}

View File

@ -72,7 +72,8 @@ FreeDVInterface::FreeDVInterface() :
lastSyncRxMode_(nullptr),
rade_(nullptr),
lpcnetEncState_(nullptr),
radeTxStep_(nullptr)
radeTxStep_(nullptr),
sync_(0)
{
// empty
}
@ -125,6 +126,7 @@ float FreeDVInterface::GetMinimumSNR_(int mode)
void FreeDVInterface::start(int txMode, int fifoSizeMs, bool singleRxThread, bool usingReliableText)
{
sync_ = 0;
singleRxThread_ = singleRxThread;
modemStatsList_ = new MODEM_STATS[enabledModes_.size()];
@ -426,13 +428,7 @@ void FreeDVInterface::setSync(int val)
int FreeDVInterface::getSync() const
{
// Special case for RADE.
if (currentRxMode_ == nullptr)
{
return rade_sync(rade_);
}
return freedv_get_sync(currentRxMode_);
return sync_;
}
void FreeDVInterface::setEq(int val)
@ -676,7 +672,7 @@ IPipelineStep* FreeDVInterface::createTransmitPipeline(int inputSampleRate, int
std::function<int(ParallelStep*)> modeFn =
[&](ParallelStep*) {
int index = 0;
// Special handling for RADE.
if (txMode_ >= FREEDV_MODE_RADE) return 0;
@ -879,5 +875,7 @@ skipSyncCheck:
*state->getRxStateFn() = rade_sync(rade_);
}
sync_ = *state->getRxStateFn();
return indexWithSync;
};

View File

@ -191,7 +191,8 @@ private:
FARGANState fargan_;
LPCNetEncState *lpcnetEncState_;
RADETransmitStep *radeTxStep_;
int sync_;
int preProcessRxFn_(ParallelStep* ps);
int postProcessRxFn_(ParallelStep* ps);
};

View File

@ -31,6 +31,7 @@
#include <climits>
#include <wx/cmdline.h>
#include <wx/stdpaths.h>
#include <wx/uiaction.h>
#include "version.h"
#include "main.h"
@ -49,6 +50,8 @@
#include "rade_api.h"
using namespace std::chrono_literals;
#define wxUSE_FILEDLG 1
#define wxUSE_LIBPNG 1
#define wxUSE_LIBJPEG 1
@ -184,15 +187,174 @@ FILE *ftest;
// Config file management
wxConfigBase *pConfig = NULL;
// Unit test management
wxString testName;
wxString utFreeDVMode;
// WxWidgets - initialize the application
IMPLEMENT_APP(MainApp);
void MainApp::UnitTest_()
{
// List audio devices
auto engine = AudioEngineFactory::GetAudioEngine();
engine->start();
for (auto& dev : engine->getAudioDeviceList(IAudioEngine::AUDIO_ENGINE_IN))
{
fprintf(stderr, "Input audio device: %s (ID %d, sample rate %d, valid channels: %d-%d)\n", (const char*)dev.name.ToUTF8(), dev.deviceId, dev.defaultSampleRate, dev.minChannels, dev.maxChannels);
}
for (auto& dev : engine->getAudioDeviceList(IAudioEngine::AUDIO_ENGINE_OUT))
{
fprintf(stderr, "Output audio device: %s (ID %d, sample rate %d, valid channels: %d-%d)\n", (const char*)dev.name.ToUTF8(), dev.deviceId, dev.defaultSampleRate, dev.minChannels, dev.maxChannels);
}
engine->stop();
// Bring window to the front
CallAfter([&]() {
frame->Iconize(false);
frame->SetFocus();
frame->Raise();
frame->Show(true);
});
// Wait 100ms for FreeDV to come to foreground
std::this_thread::sleep_for(100ms);
// Select FreeDV mode. Note, 2020 is deprecated so not testable here.
wxRadioButton* modeBtn = nullptr;
if (utFreeDVMode == "RADE")
{
modeBtn = frame->m_rbRADE;
}
/*else if (utFreeDVMode == "700C")
{
modeBtn = frame->m_rb700c;
}*/
else if (utFreeDVMode == "700D")
{
modeBtn = frame->m_rb700d;
}
else if (utFreeDVMode == "700E")
{
modeBtn = frame->m_rb700e;
}
/*else if (utFreeDVMode == "800XA")
{
modeBtn = frame->m_rb800xa;
}*/
else if (utFreeDVMode == "1600")
{
modeBtn = frame->m_rb1600;
}
if (modeBtn != nullptr)
{
fprintf(stderr, "Firing mode change\n");
/*sim.MouseMove(modeBtn->GetScreenPosition());
sim.MouseClick();*/
CallAfter([&]() {
modeBtn->SetValue(true);
wxCommandEvent* modeEvent = new wxCommandEvent(wxEVT_RADIOBUTTON, modeBtn->GetId());
modeEvent->SetEventObject(modeBtn);
QueueEvent(modeEvent);
});
}
// Fire event to start FreeDV
fprintf(stderr, "Firing start\n");
CallAfter([&]() {
frame->m_togBtnOnOff->SetValue(true);
wxCommandEvent* onEvent = new wxCommandEvent(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, frame->m_togBtnOnOff->GetId());
onEvent->SetEventObject(frame->m_togBtnOnOff);
frame->OnTogBtnOnOff(*onEvent);
delete onEvent;
//QueueEvent(onEvent);
});
/*sim.MouseMove(frame->m_togBtnOnOff->GetScreenPosition());
sim.MouseClick();*/
// Wait 5 seconds for FreeDV to start
std::this_thread::sleep_for(5s);
if (testName == "tx")
{
// Fire event to begin TX
//sim.MouseMove(frame->m_btnTogPTT->GetScreenPosition());
//sim.MouseClick();
fprintf(stderr, "Firing PTT\n");
CallAfter([&]() {
frame->m_btnTogPTT->SetValue(true);
wxCommandEvent* txEvent = new wxCommandEvent(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, frame->m_btnTogPTT->GetId());
txEvent->SetEventObject(frame->m_btnTogPTT);
//QueueEvent(txEvent);
frame->OnTogBtnPTT(*txEvent);
delete txEvent;
});
// Transmit for 60 seconds
std::this_thread::sleep_for(60s);
// Stop transmitting
fprintf(stderr, "Firing PTT\n");
CallAfter([&]() {
frame->m_btnTogPTT->SetValue(false);
wxCommandEvent* rxEvent = new wxCommandEvent(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, frame->m_btnTogPTT->GetId());
rxEvent->SetEventObject(frame->m_btnTogPTT);
frame->OnTogBtnPTT(*rxEvent);
delete rxEvent;
//QueueEvent(rxEvent);
});
/*sim.MouseMove(frame->m_btnTogPTT->GetScreenPosition());
sim.MouseClick();*/
// Wait 5 seconds for FreeDV to stop
std::this_thread::sleep_for(5s);
}
else
{
// Receive for 60 seconds
auto sync = 0;
for (int i = 0; i < 60*10; i++)
{
std::this_thread::sleep_for(100ms);
auto newSync = freedvInterface.getSync();
if (newSync != sync)
{
fprintf(stderr, "Sync changed from %d to %d\n", sync, newSync);
sync = newSync;
}
}
}
// Fire event to stop FreeDV
fprintf(stderr, "Firing stop\n");
CallAfter([&]() {
wxCommandEvent* offEvent = new wxCommandEvent(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, frame->m_togBtnOnOff->GetId());
offEvent->SetEventObject(frame->m_togBtnOnOff);
frame->m_togBtnOnOff->SetValue(false);
//QueueEvent(offEvent);
frame->OnTogBtnOnOff(*offEvent);
delete offEvent;
});
//sim.MouseMove(frame->m_togBtnOnOff->GetScreenPosition());
//sim.MouseClick();
// Wait 5 seconds for FreeDV to stop
std::this_thread::sleep_for(5s);
// Destroy main window to exit application. Must be done in UI thread to avoid problems.
CallAfter([&]() {
frame->Destroy();
});
}
void MainApp::OnInitCmdLine(wxCmdLineParser& parser)
{
wxApp::OnInitCmdLine(parser);
parser.AddOption("f", "config", "Use different configuration file instead of the default.");
parser.AddOption("ut", "unit_test", "Execute FreeDV in unit test mode.");
parser.AddOption("utmode", wxEmptyString, "Switch FreeDV to the given mode before UT execution.");
}
bool MainApp::OnCmdLineParsed(wxCmdLineParser& parser)
@ -216,6 +378,15 @@ bool MainApp::OnCmdLineParsed(wxCmdLineParser& parser)
}
pConfig->SetRecordDefaults();
if (parser.Found("ut", &testName))
{
fprintf(stderr, "Executing test %s\n", (const char*)testName.ToUTF8());
if (parser.Found("utmode", &utFreeDVMode))
{
fprintf(stderr, "Using mode %s for tests\n", (const char*)utFreeDVMode.ToUTF8());
}
}
return true;
}
@ -298,8 +469,15 @@ bool MainApp::OnInit()
frame->m_auiNbookCtrl->ChangeSelection(0);
frame->Layout();
frame->Show();
g_parent =frame;
g_parent = frame;
// Begin test execution
if (testName != "")
{
std::thread utThread(std::bind(&MainApp::UnitTest_, this));
utThread.detach();
}
return true;
}
@ -2794,15 +2972,15 @@ void MainFrame::startRxStream()
// loop.
int m_fifoSize_ms = wxGetApp().appConfiguration.fifoSizeMs;
int soundCard1InFifoSizeSamples = m_fifoSize_ms*wxGetApp().appConfiguration.audioConfiguration.soundCard1In.sampleRate/1000;
int soundCard1OutFifoSizeSamples = m_fifoSize_ms*wxGetApp().appConfiguration.audioConfiguration.soundCard1Out.sampleRate/1000;
int soundCard1InFifoSizeSamples = wxGetApp().appConfiguration.audioConfiguration.soundCard1In.sampleRate;
int soundCard1OutFifoSizeSamples = wxGetApp().appConfiguration.audioConfiguration.soundCard1Out.sampleRate;
g_rxUserdata->infifo1 = codec2_fifo_create(soundCard1InFifoSizeSamples);
g_rxUserdata->outfifo1 = codec2_fifo_create(soundCard1OutFifoSizeSamples);
if (txInSoundDevice && txOutSoundDevice)
{
int soundCard2InFifoSizeSamples = m_fifoSize_ms*wxGetApp().appConfiguration.audioConfiguration.soundCard2In.sampleRate/1000;
int soundCard2OutFifoSizeSamples = m_fifoSize_ms*wxGetApp().appConfiguration.audioConfiguration.soundCard2Out.sampleRate/1000;
int soundCard2InFifoSizeSamples = m_fifoSize_ms*wxGetApp().appConfiguration.audioConfiguration.soundCard2In.sampleRate / 1000;
int soundCard2OutFifoSizeSamples = m_fifoSize_ms*wxGetApp().appConfiguration.audioConfiguration.soundCard2Out.sampleRate / 1000;
g_rxUserdata->outfifo2 = codec2_fifo_create(soundCard2OutFifoSizeSamples);
g_rxUserdata->infifo2 = codec2_fifo_create(soundCard2InFifoSizeSamples);
@ -2885,15 +3063,18 @@ void MainFrame::startRxStream()
// Set sound card callbacks
auto errorCallback = [&](IAudioDevice&, std::string error, void*)
{
CallAfter([&, error]() {
fprintf(stderr, "AUDIO ERROR: %s\n", error.c_str());
/*CallAfter([&, error]() {
wxMessageBox(wxString::Format("Error encountered while processing audio: %s", error), wxT("Error"), wxOK);
});
});*/
};
rxInSoundDevice->setOnAudioData([&](IAudioDevice& dev, void* data, size_t size, void* state) {
paCallBackData* cbData = static_cast<paCallBackData*>(state);
short* audioData = static_cast<short*>(data);
short indata[size];
//fprintf(stderr, "recoded %d samples\n", size);
for (size_t i = 0; i < size; i++, audioData += dev.getNumChannels())
{
indata[i] = audioData[0];
@ -2901,6 +3082,7 @@ void MainFrame::startRxStream()
if (codec2_fifo_write(cbData->infifo1, indata, size))
{
fprintf(stderr, "RX FIFO full\n");
g_infifo1_full++;
}

View File

@ -225,6 +225,8 @@ class MainApp : public wxApp
int m_reportCounter;
protected:
private:
void UnitTest_();
};
// declare global static function wxGetApp()
@ -442,6 +444,8 @@ class MainFrame : public TopFrame
void OnSetMonitorTxAudioVol( wxCommandEvent& event );
private:
friend class MainApp; // needed for unit tests
std::shared_ptr<IAudioDevice> rxInSoundDevice;
std::shared_ptr<IAudioDevice> rxOutSoundDevice;
std::shared_ptr<IAudioDevice> txInSoundDevice;

View File

@ -20,6 +20,9 @@
//
//=========================================================================
#include <chrono>
using namespace std::chrono_literals;
// This forces us to use freedv-gui's version rather than another one.
// TBD -- may not be needed once we fully switch over to the audio pipeline.
#include "../defines.h"
@ -478,6 +481,7 @@ void* TxRxThread::Entry()
pthread_setname_np(pthread_self(), threadName);
#endif // defined(__linux__)
#if 0
{
std::unique_lock<std::mutex> lk(m_processingMutex);
if (m_processingCondVar.wait_for(lk, std::chrono::milliseconds(100)) == std::cv_status::timeout)
@ -485,9 +489,15 @@ void* TxRxThread::Entry()
fprintf(stderr, "txRxThread: timeout while waiting for CV, tx = %d\n", m_tx);
}
}
#endif
auto currentTime = std::chrono::steady_clock::now();
if (!m_run) break;
if (m_tx) txProcessing_();
else rxProcessing_();
std::this_thread::sleep_until(currentTime + 20ms);
}
// Force pipeline to delete itself when we're done with the thread.
@ -509,8 +519,10 @@ void TxRxThread::terminateThread()
void TxRxThread::notify()
{
#if 0
std::unique_lock<std::mutex> lk(m_processingMutex);
m_processingCondVar.notify_all();
#endif
}
void TxRxThread::clearFifos_()

View File

@ -82,9 +82,7 @@ class wxListViewComboPopup;
/// Class TopFrame
///////////////////////////////////////////////////////////////////////////////
class TopFrame : public wxFrame
{
private:
{
protected:
wxPanel* m_panel;
wxMenuBar* m_menubarMain;

View File

@ -0,0 +1,138 @@
<#
.SYNOPSIS
Executes full-duplex test of FreeDV.
.DESCRIPTION
This script starts FreeDV in full-duplex mode for approximately 60 seconds using an autogenerated configuration file
that will access the audio devices passed in. After 60 seconds, FreeDV will terminate and this script will examine
the output to determine the number of times that it went into and out of sync. If it detects that FreeDV went out
of sync during the test, the test is marked as having failed.
.INPUTS
None. You can't pipe objects to this script.
.OUTPUTS
The script outputs the mode tested as well as the number of passed/failed tests to the console.
.EXAMPLE
PS> .\TestFreeDVFullDuplex.ps1 -ModeToTest RADE -RadioToComputerDevice "Microphone (USB Audio CODEC)" -ComputerToRadioDevice "Speakers (USB Audio CODEC)" -ComputerToSpeakerDevice "Speakers (Realtek High Definition Audio(SST))" -MicrophoneToComputerDevice "Microphone Array (Realtek High Definition Audio(SST))"
#>
param (
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
# The sound device to receive RX audio from.
$RadioToComputerDevice,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
# The sound device to emit decoded audio to.
$ComputerToSpeakerDevice,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
# The sound device to receive analog audio from.
$MicrophoneToComputerDevice,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
# The sound device to emit TX audio to.
$ComputerToRadioDevice,
[ValidateSet("RADE", "700D", "700E", "1600")]
[ValidateNotNullOrEmpty()]
[string]
# The FreeDV mode to use for testing.
$ModeToTest="RADE",
[int]
# The number of times to execute the test.
$NumberOfRuns=10)
<#
.Description
Performs the actual test with FreeDV by generating the needed configuration file, starting FreeDV and then examining the output.
#>
function Test-FreeDV {
param (
$ModeToTest,
$RadioToComputerDevice,
$ComputerToSpeakerDevice,
$MicrophoneToComputerDevice,
$ComputerToRadioDevice
)
$current_loc = Get-Location
# Generate new conf
$conf_tmpl = Get-Content "$current_loc\freedv-ctest-fullduplex.conf.tmpl"
$conf_tmpl = $conf_tmpl.Replace("@FREEDV_RADIO_TO_COMPUTER_DEVICE@", $RadioToComputerDevice)
$conf_tmpl = $conf_tmpl.Replace("@FREEDV_COMPUTER_TO_RADIO_DEVICE@", $ComputerToRadioDevice)
$conf_tmpl = $conf_tmpl.Replace("@FREEDV_MICROPHONE_TO_COMPUTER_DEVICE@", $MicrophoneToComputerDevice)
$conf_tmpl = $conf_tmpl.Replace("@FREEDV_COMPUTER_TO_SPEAKER_DEVICE@", $ComputerToSpeakerDevice)
$tmp_file = New-TemporaryFile
$conf_tmpl | Set-Content -Path $tmp_file.FullName
# Start freedv.exe
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.CreateNoWindow = $true
$psi.UseShellExecute = $false
$psi.RedirectStandardError = $true
$psi.RedirectStandardOutput = $true
$psi.FileName = "$current_loc\freedv.exe"
$psi.WorkingDirectory = $current_loc
$quoted_tmp_filename = "`"" + $tmp_file.FullName + "`""
$psi.Arguments = @("/f $quoted_tmp_filename /ut txrx /utmode $ModeToTest")
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $psi
[void]$process.Start()
# Read output from process
$err_output = $process.StandardError.ReadToEnd();
$output = $process.StandardOutput.ReadToEnd();
$process.WaitForExit()
Write-Host "$err_output"
$syncs = $err_output.Split([Environment]::NewLine) | Where { $_.Contains("Sync changed") }
if ($syncs.Count -eq 1) {
return $true
}
return $false
}
$passes = 0
$fails = 0
for (($i = 0); $i -lt $NumberOfRuns; $i++)
{
$result = Test-FreeDV `
-ModeToTest $ModeToTest `
-RadioToComputerDevice $RadioToComputerDevice `
-ComputerToSpeakerDevice $ComputerToSpeakerDevice `
-MicrophoneToComputerDevice $MicrophoneToComputerDevice `
-ComputerToRadioDevice $ComputerToRadioDevice
if ($result -eq $true)
{
$passes++
}
else
{
$fails++
}
}
Write-Host "Mode: $ModeToTest, Total Runs: $NumberOfRuns, Passed: $passes, Failures: $fails"
if ($fails -gt 0) {
throw "Test failed"
exit 1
}

View File

@ -0,0 +1,30 @@
#!/usr/bin/python3
import sys
import struct
index = 0
detected = False
detected_index = -1
first_detected_index = -1
with open(sys.argv[1], "rb") as f:
while True:
bytes_to_read = struct.calcsize("h")
buffer = f.read(bytes_to_read)
if len(buffer) != bytes_to_read:
break
if buffer[0] == 0:
if first_detected_index == -1:
first_detected_index = index
detected = True
detected_index = index
elif index == (detected_index + 1):
if (index - first_detected_index) > 1:
sys.stderr.write(f"Zero audio detected at index {index} ({index / 8000} seconds)")
sys.stderr.write(f" - lasted {(index - first_detected_index) / 8000} seconds\n")
first_detected_index = -1
index = index + 1
if detected is False:
print("No zeros found.")

View File

@ -0,0 +1,189 @@
FirstTimeUse=0
ExperimentalFeatures=0
[Audio]
soundCard1SampleRate=-1
soundCard2SampleRate=-1
soundCard1InDeviceName=@FREEDV_RADIO_TO_COMPUTER_DEVICE@
soundCard1InSampleRate=48000
soundCard1OutDeviceName=@FREEDV_COMPUTER_TO_RADIO_DEVICE@
soundCard1OutSampleRate=48000
soundCard2InDeviceName=@FREEDV_MICROPHONE_TO_COMPUTER_DEVICE@
soundCard2InSampleRate=48000
soundCard2OutDeviceName=@FREEDV_COMPUTER_TO_SPEAKER_DEVICE@
soundCard2OutSampleRate=48000
SquelchActive=1
SquelchLevel=-4
fifoSize_ms=440
transmitLevel=0
snrSlow=0
mode=4
TxRxDelayMilliseconds=0
[Filter]
codec2LPCPostFilterGamma=50
codec2LPCPostFilterBeta=20
MicInBassFreqHz=100
MicInBassGaindB=0
MicInTrebleFreqHz=3000
MicInTrebleGaindB=0
MicInMidFreqHz=1500
MicInMidGaindB=0
MicInMidQ=100
MicInVolInDB=0
SpkOutBassFreqHz=100
SpkOutBassGaindB=0
SpkOutTrebleFreqHz=3000
SpkOutTrebleGaindB=0
SpkOutMidFreqHz=1500
SpkOutMidGaindB=0
SpkOutMidQ=100
SpkOutVolInDB=0
codec2LPCPostFilterEnable=1
codec2LPCPostFilterBassBoost=1
speexpp_enable=1
700C_EQ=1
[Filter/MicIn]
EQEnable=0
BassFreqHz=100
BassGaindB=0
TrebleFreqHz=3000
TrebleGaindB=0
MidFreqHz=1500
MidGaindB=0
MidQ=1
VolInDB=0
[Filter/SpkOut]
EQEnable=0
BassFreqHz=100
BassGaindB=0
TrebleFreqHz=3000
TrebleGaindB=0
MidFreqHz=1500
MidGaindB=0
MidQ=1
VolInDB=0
[Filter/codec2LPCPostFilter]
Gamma=50
Beta=20
[Hamlib]
UseForPTT=0
EnableFreqModeChanges=1
UseAnalogModes=0
IcomCIVHex=0
RigNameStr=ADAT www.adat.ch ADT-200A
PttType=0
SerialRate=0
SerialPort=
PttSerialPort=
RigName=0
[Rig]
UseSerialPTT=0
Port=
UseRTS=1
RTSPolarity=1
UseDTR=0
DTRPolarity=0
UseSerialPTTInput=0
PttInPort=
CTSPolarity=0
leftChannelVoxTone=0
EnableSpacebarForPTT=1
HalfDuplex=0
MultipleRx=1
SingleRxThread=0
[PSKReporter]
Enable=0
Callsign=
GridSquare=
FrequencyHzStr=0
[Data]
CallSign=
[Reporting]
Enable=0
Callsign=
GridSquare=
FrequencyAsKHz=0
FrequencyList=1.997000,3.625000,3.643000,3.693000,3.697000,3.850000,5.403500,5.366500,5.368500,7.177000,7.197000,14.236000,14.240000,18.118000,21.313000,24.933000,28.330000,28.720000,10489.640000
ManualFrequencyReporting=0
DirectionAsCardinal=0
Frequency=0
[Reporting/PSKReporter]
Enable=1
[Reporting/FreeDV]
Enable=1
Hostname=qso.freedv.org
CurrentBandFilter=0
UseMetricDistances=1
BandFilterTracksFrequency=0
ForceReceiveOnly=0
StatusText=
RecentStatusTexts=
TxRowBackgroundColor=#FC4500
TxRowForegroundColor=#000000
RxRowBackgroundColor=#379BAF
RxRowForegroundColor=#000000
MsgRowBackgroundColor=#E58BE5
MsgRowForegroundColor=#000000
[Reporting/FreeDV/BandFilterTracking]
TracksFreqBand=1
TracksExactFreq=0
[CallsignList]
UseUTCTime=0
[FreeDV2020]
Allowed=0
[MainFrame]
left=26
top=23
width=800
height=780
rxNbookCtrl=0
TabLayout=
[Windows]
[Windows/AudioConfig]
left=26
top=23
width=1148
height=732
[Windows/FreeDVReporter]
left=20
top=20
width=-1
height=-1
visible=0
currentSort=-1
currentSortDirection=1
reportingUserMsgColWidth=130
[File]
playFileToMicInPath=
recFileFromRadioPath=
recFileFromRadioSecs=60
recFileFromModulatorPath=
recFileFromModulatorSecs=60
playFileFromRadioPath=
[VoiceKeyer]
WaveFilePath=/home/mooneer/Documents
WaveFile=
RxPause=10
Repeats=5
[FreeDV700]
txClip=1
txBPF=1
[Noise]
noise_snr=2
[Debug]
console=0
verbose=0
APIverbose=0
[Waterfall]
Color=0
[Stats]
ResetTime=10
[Plot]
[Plot/Spectrum]
CurrentAveraging=0
[Monitor]
VoiceKeyerAudio=0
TransmitAudio=0
VoiceKeyerAudioVol=0
TransmitAudioVol=0
[QuickRecord]
SavePath=/home/mooneer/Documents

View File

@ -0,0 +1,189 @@
FirstTimeUse=0
ExperimentalFeatures=0
[Audio]
soundCard1SampleRate=-1
soundCard2SampleRate=-1
soundCard1InDeviceName=@FREEDV_RADIO_TO_COMPUTER_DEVICE@
soundCard1InSampleRate=48000
soundCard1OutDeviceName=@FREEDV_COMPUTER_TO_RADIO_DEVICE@
soundCard1OutSampleRate=48000
soundCard2InDeviceName=@FREEDV_MICROPHONE_TO_COMPUTER_DEVICE@
soundCard2InSampleRate=48000
soundCard2OutDeviceName=@FREEDV_COMPUTER_TO_SPEAKER_DEVICE@
soundCard2OutSampleRate=48000
SquelchActive=1
SquelchLevel=-4
fifoSize_ms=440
transmitLevel=0
snrSlow=0
mode=257
TxRxDelayMilliseconds=0
[Filter]
codec2LPCPostFilterGamma=50
codec2LPCPostFilterBeta=20
MicInBassFreqHz=100
MicInBassGaindB=0
MicInTrebleFreqHz=3000
MicInTrebleGaindB=0
MicInMidFreqHz=1500
MicInMidGaindB=0
MicInMidQ=100
MicInVolInDB=0
SpkOutBassFreqHz=100
SpkOutBassGaindB=0
SpkOutTrebleFreqHz=3000
SpkOutTrebleGaindB=0
SpkOutMidFreqHz=1500
SpkOutMidGaindB=0
SpkOutMidQ=100
SpkOutVolInDB=0
codec2LPCPostFilterEnable=1
codec2LPCPostFilterBassBoost=1
speexpp_enable=1
700C_EQ=1
[Filter/MicIn]
EQEnable=0
BassFreqHz=100
BassGaindB=0
TrebleFreqHz=3000
TrebleGaindB=0
MidFreqHz=1500
MidGaindB=0
MidQ=1
VolInDB=0
[Filter/SpkOut]
EQEnable=0
BassFreqHz=100
BassGaindB=0
TrebleFreqHz=3000
TrebleGaindB=0
MidFreqHz=1500
MidGaindB=0
MidQ=1
VolInDB=0
[Filter/codec2LPCPostFilter]
Gamma=50
Beta=20
[Hamlib]
UseForPTT=0
EnableFreqModeChanges=1
UseAnalogModes=0
IcomCIVHex=0
RigNameStr=ADAT www.adat.ch ADT-200A
PttType=0
SerialRate=0
SerialPort=
PttSerialPort=
RigName=0
[Rig]
UseSerialPTT=0
Port=
UseRTS=1
RTSPolarity=1
UseDTR=0
DTRPolarity=0
UseSerialPTTInput=0
PttInPort=
CTSPolarity=0
leftChannelVoxTone=0
EnableSpacebarForPTT=1
HalfDuplex=1
MultipleRx=1
SingleRxThread=1
[PSKReporter]
Enable=0
Callsign=
GridSquare=
FrequencyHzStr=0
[Data]
CallSign=
[Reporting]
Enable=0
Callsign=
GridSquare=
FrequencyAsKHz=0
FrequencyList=1.9970,3.6250,3.6430,3.6930,3.6970,3.8500,5.4035,5.3665,5.3685,7.1770,7.1970,14.2360,14.2400,18.1180,21.3130,24.9330,28.3300,28.7200,10489.6400
ManualFrequencyReporting=0
DirectionAsCardinal=0
Frequency=0
[Reporting/PSKReporter]
Enable=1
[Reporting/FreeDV]
Enable=1
Hostname=qso.freedv.org
CurrentBandFilter=0
UseMetricDistances=1
BandFilterTracksFrequency=0
ForceReceiveOnly=0
StatusText=
RecentStatusTexts=
TxRowBackgroundColor=#fc4500
TxRowForegroundColor=#000000
RxRowBackgroundColor=#379baf
RxRowForegroundColor=#000000
MsgRowBackgroundColor=#E58BE5
MsgRowForegroundColor=#000000
[Reporting/FreeDV/BandFilterTracking]
TracksFreqBand=1
TracksExactFreq=0
[CallsignList]
UseUTCTime=0
[FreeDV2020]
Allowed=0
[MainFrame]
left=26
top=23
width=800
height=780
rxNbookCtrl=0
TabLayout=
[Windows]
[Windows/AudioConfig]
left=26
top=23
width=918
height=739
[Windows/FreeDVReporter]
left=20
top=20
width=-1
height=-1
visible=0
currentSort=-1
currentSortDirection=1
reportingUserMsgColWidth=130
[File]
playFileToMicInPath=
recFileFromRadioPath=
recFileFromRadioSecs=60
recFileFromModulatorPath=
recFileFromModulatorSecs=60
playFileFromRadioPath=
[VoiceKeyer]
WaveFilePath=/home/mooneer/Documents
WaveFile=voicekeyer.wav
RxPause=10
Repeats=5
[FreeDV700]
txClip=1
txBPF=1
[Noise]
noise_snr=2
[Debug]
console=0
verbose=0
APIverbose=0
[Waterfall]
Color=0
[Stats]
ResetTime=10
[Plot]
[Plot/Spectrum]
CurrentAveraging=0
[Monitor]
VoiceKeyerAudio=0
TransmitAudio=0
VoiceKeyerAudioVol=0
TransmitAudioVol=0
[QuickRecord]
SavePath=/home/mooneer/Documents

123
test/test_zeros.sh 100755
View File

@ -0,0 +1,123 @@
#!/bin/bash
FREEDV_TEST=$1
FREEDV_MODE=$2
FREEDV_RX_FILE=$3
# Determine sox driver to use for recording/playback
OPERATING_SYSTEM=`uname`
SOX_DRIVER=alsa
FREEDV_BINARY=src/freedv
if [ "$OPERATING_SYSTEM" == "Darwin" ]; then
SOX_DRIVER=coreaudio
FREEDV_BINARY=src/FreeDV.app/Contents/MacOS/freedv
fi
createVirtualAudioCable () {
CABLE_NAME=$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}"
FREEDV_COMPUTER_TO_SPEAKER_DEVICE="${FREEDV_COMPUTER_TO_SPEAKER_DEVICE:-FreeDV_Computer_To_Speaker}"
FREEDV_MICROPHONE_TO_COMPUTER_DEVICE="${FREEDV_MICROPHONE_TO_COMPUTER_DEVICE:-FreeDV_Microphone_To_Computer}"
FREEDV_COMPUTER_TO_RADIO_DEVICE="${FREEDV_COMPUTER_TO_RADIO_DEVICE:-FreeDV_Computer_To_Radio}"
# Automated script to help find audio dropouts.
# NOTE: this must be run from "build_linux". Also assumes PulseAudio/pipewire.
if [ "$OPERATING_SYSTEM" == "Linux" ]; then
DRIVER_INDEX_FREEDV_RADIO_TO_COMPUTER=$(createVirtualAudioCable FreeDV_Radio_To_Computer)
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"`
fi
# For debugging--list sink info
#pactl list sinks
# If full duplex test, use correct config file and assume "rx" mode.
FREEDV_CONF_FILE=freedv-ctest.conf
if [ "$FREEDV_TEST" == "txrx" ]; then
FREEDV_TEST=rx
FREEDV_CONF_FILE=freedv-ctest-fullduplex.conf
REC_DEVICE="$FREEDV_COMPUTER_TO_SPEAKER_DEVICE"
# Generate sine wave for input
if [ "$OPERATING_SYSTEM" == "Linux" ]; then
sox -r 48000 -n -b 16 -c 1 -t wav - synth 120 sin 1000 vol -10dB | paplay -d "$FREEDV_MICROPHONE_TO_COMPUTER_DEVICE" &
else
sox -r 48000 -n -b 16 -c 1 -t $SOX_DRIVER "$FREEDV_MICROPHONE_TO_COMPUTER_DEVICE" - synth 120 sin 1000 vol -10dB &
fi
PLAY_PID=$!
elif [ "$FREEDV_TEST" == "rx" ]; then
# Start playback if RX
if [ "$OPERATING_SYSTEM" == "Linux" ]; then
paplay -d "$FREEDV_RADIO_TO_COMPUTER_DEVICE" $FREEDV_RX_FILE &
else
sox $FREEDV_RX_FILE -t $SOX_DRIVER "$FREEDV_RADIO_TO_COMPUTER_DEVICE" &
fi
PLAY_PID=$!
REC_DEVICE="$FREEDV_COMPUTER_TO_SPEAKER_DEVICE.monitor"
else
REC_DEVICE="$FREEDV_COMPUTER_TO_RADIO_DEVICE.monitor"
fi
# Generate config file
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
if [ "$FREEDV_RADIO_TO_COMPUTER_DEVICE" == "FreeDV_Radio_To_Computer" ] && [ "$OPERATING_SYSTEM" == "Linux" ]; then
sed "s/@FREEDV_RADIO_TO_COMPUTER_DEVICE@/$FREEDV_RADIO_TO_COMPUTER_DEVICE.monitor/g" $SCRIPTPATH/$FREEDV_CONF_FILE.tmpl > $(pwd)/$FREEDV_CONF_FILE
else
sed "s/@FREEDV_RADIO_TO_COMPUTER_DEVICE@/$FREEDV_RADIO_TO_COMPUTER_DEVICE/g" $SCRIPTPATH/$FREEDV_CONF_FILE.tmpl > $(pwd)/$FREEDV_CONF_FILE
fi
sed "s/@FREEDV_COMPUTER_TO_RADIO_DEVICE@/$FREEDV_COMPUTER_TO_RADIO_DEVICE/g" $(pwd)/$FREEDV_CONF_FILE > $(pwd)/$FREEDV_CONF_FILE.tmp
mv $(pwd)/$FREEDV_CONF_FILE.tmp $(pwd)/$FREEDV_CONF_FILE
sed "s/@FREEDV_COMPUTER_TO_SPEAKER_DEVICE@/$FREEDV_COMPUTER_TO_SPEAKER_DEVICE/g" $(pwd)/$FREEDV_CONF_FILE > $(pwd)/$FREEDV_CONF_FILE.tmp
mv $(pwd)/$FREEDV_CONF_FILE.tmp $(pwd)/$FREEDV_CONF_FILE
if [ "$FREEDV_MICROPHONE_TO_COMPUTER_DEVICE" == "FreeDV_Microphone_To_Computer" ] && [ "$OPERATING_SYSTEM" == "Linux" ]; then
sed "s/@FREEDV_MICROPHONE_TO_COMPUTER_DEVICE@/$FREEDV_MICROPHONE_TO_COMPUTER_DEVICE.monitor/g" $(pwd)/$FREEDV_CONF_FILE > $(pwd)/$FREEDV_CONF_FILE.tmp
else
sed "s/@FREEDV_MICROPHONE_TO_COMPUTER_DEVICE@/$FREEDV_MICROPHONE_TO_COMPUTER_DEVICE/g" $(pwd)/$FREEDV_CONF_FILE > $(pwd)/$FREEDV_CONF_FILE.tmp
fi
mv $(pwd)/$FREEDV_CONF_FILE.tmp $(pwd)/$FREEDV_CONF_FILE
# Start recording
if [ "$FREEDV_TEST" == "tx" ]; then
if [ "$OPERATING_SYSTEM" == "Linux" ]; then
parecord --channels=1 --rate 8000 --file-format=wav --device "$REC_DEVICE" --latency 1 test.wav &
else
sox -t $SOX_DRIVER "$REC_DEVICE" -c 1 -r 8000 -t wav test.wav &
fi
RECORD_PID=$!
fi
# Start FreeDV in test mode
$FREEDV_BINARY -f $(pwd)/$FREEDV_CONF_FILE -ut $FREEDV_TEST -utmode $FREEDV_MODE 2>&1 | tee tmp.log
FDV_PID=$!
#sleep 30
#screencapture ../screenshot.png
#wpctl status
#pw-top -b -n 5
#wait $FDV_PID
# Stop recording/playback and process data
if [ "$FREEDV_TEST" == "rx" ]; then
kill $PLAY_PID || echo "Already done playing"
NUM_RESYNCS=`grep "Sync changed" tmp.log | wc -l | xargs`
echo "Got $NUM_RESYNCS sync changes"
else
kill $RECORD_PID
sox test.wav -t raw -r 8k -c 1 -b 16 -e signed-integer test.raw silence 1 0.1 0.1% reverse silence 1 0.1 0.1% reverse
python3 $SCRIPTPATH/check-for-zeros.py test.raw
fi
# Clean up PulseAudio virtual devices
if [ "$OPERATING_SYSTEM" == "Linux" ]; then
pactl unload-module $DRIVER_INDEX_LOOPBACK
pactl unload-module $DRIVER_INDEX_FREEDV_RADIO_TO_COMPUTER
pactl unload-module $DRIVER_INDEX_FREEDV_COMPUTER_TO_SPEAKER
pactl unload-module $DRIVER_INDEX_FREEDV_COMPUTER_TO_RADIO
pactl unload-module $DRIVER_INDEX_FREEDV_MICROPHONE_TO_COMPUTER
fi

35
test/vac464.cer 100644
View File

@ -0,0 +1,35 @@
-----BEGIN CERTIFICATE-----
MIIGDDCCBPSgAwIBAgIMW5i7HVbfDAJdVbddMA0GCSqGSIb3DQEBCwUAMG4xCzAJ
BgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMUQwQgYDVQQDEztH
bG9iYWxTaWduIEV4dGVuZGVkIFZhbGlkYXRpb24gQ29kZVNpZ25pbmcgQ0EgLSBT
SEEyNTYgLSBHMzAeFw0yMDA1MjYxMjE4NDBaFw0yMzA4MjYxMjE4NDBaMIIBOzEd
MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xGDAWBgNVBAUTDzMxNjU0NzYw
MDEwNTU1NjETMBEGCysGAQQBgjc8AgEDEwJSVTEmMCQGCysGAQQBgjc8AgECExVO
b3Zvc2liaXJza2F5YSBvYmxhc3QxCzAJBgNVBAYTAlJVMR4wHAYDVQQIExVOb3Zv
c2liaXJza2F5YSBvYmxhc3QxFDASBgNVBAcTC05vdm9zaWJpcnNrMSswKQYDVQQK
EyJNdXp5Y2hlbmtvIEV2Z2VuaWkgVmlrdG9yb3ZpY2gsIElQMSswKQYDVQQDEyJN
dXp5Y2hlbmtvIEV2Z2VuaWkgVmlrdG9yb3ZpY2gsIElQMSYwJAYJKoZIhvcNAQkB
Fhdzb2Z0d2FyZUBtdXp5Y2hlbmtvLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMzy2+Ke77GcrnUkMh5D8SB99OrONoC3QkniNZ78H0k1t/9qdBB7
PgsGIGMXlyo3tLx5A6kWI9iczp5iWEpdZUmvTs7AV/NDPGIDbHU7dJR1gFlzc4yn
0CSYbW5GkQEnYLZutYDzfJP6WkteKdj7EHg2N1iO9AtpcyyC8k4CWRahymT8wQLO
TmhcwoIgayY1rn4Qfyx5rO4EPf4dYl3NwNItJk5IAL+QgWuSQSIHptqbxM8G1Eiu
wR0Q+Ab5v3tKktdQc7OQootJv1IX1IhLaT/AYHTxrKugiTArwzAv77fYo6HUZPZ4
a+lKt/FH/FF6wH0BZZoDtAab1NU2m15Cz8kCAwEAAaOCAdkwggHVMA4GA1UdDwEB
/wQEAwIHgDCBoAYIKwYBBQUHAQEEgZMwgZAwTgYIKwYBBQUHMAKGQmh0dHA6Ly9z
ZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2dzZXh0ZW5kY29kZXNpZ25zaGEy
ZzNvY3NwLmNydDA+BggrBgEFBQcwAYYyaHR0cDovL29jc3AyLmdsb2JhbHNpZ24u
Y29tL2dzZXh0ZW5kY29kZXNpZ25zaGEyZzMwVQYDVR0gBE4wTDBBBgkrBgEEAaAy
AQIwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xvYmFsc2lnbi5jb20vcmVw
b3NpdG9yeS8wBwYFZ4EMAQMwCQYDVR0TBAIwADBFBgNVHR8EPjA8MDqgOKA2hjRo
dHRwOi8vY3JsLmdsb2JhbHNpZ24uY29tL2dzZXh0ZW5kY29kZXNpZ25zaGEyZzMu
Y3JsMCIGA1UdEQQbMBmBF3NvZnR3YXJlQG11enljaGVua28ubmV0MBMGA1UdJQQM
MAoGCCsGAQUFBwMDMB8GA1UdIwQYMBaAFNwsWCwqbzUtn3mVqEhdxG0+U7+5MB0G
A1UdDgQWBBSIENjDBY/ZwM9nURvPaBjwD0BpIDANBgkqhkiG9w0BAQsFAAOCAQEA
KAHk8s47IfKW8g/P2+Ia1koc/CkHFy4Q+R+uVC3DF3GVvNeBY9qCj8f8xUpQfkF1
mRUyIxfGCWS6sg+/CtzMY+vuSZnGb1C/2GQ5hBP/CvXXGz1W+sKASy29EU/gzzKd
j0sBVAUk+dgQ18P8cyUDnBxOGaizG/Yn10/2jzLi2eyBoxnunpzxMcgaaLUhtt/7
q7Jmor+A6FQ2xiuJlWE6TnUTR4aJH6uPswa6s6msHwNj9P7mbQfgK9P3pVmLuHBK
k9K9jkyL9KAAlNa7GBoT98a86z4x2lC/GcKOAD78M09qrfsd9+8f2II0XIXVuEuE
nabx4jlGD9IwXJ+daDrhfA==
-----END CERTIFICATE-----