Compare commits
No commits in common. "master" and "offline" have entirely different histories.
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Oneliner to find script's directory. Please note - last path component should NOT be symlink.
|
||||||
|
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
|
||||||
|
|
||||||
|
/usr/bin/python3 ${SCRIPT_DIR}/src/utils_network_impairment.py --start
|
||||||
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Oneliner to find script's directory. Please note - last path component should NOT be symlink.
|
||||||
|
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
|
||||||
|
|
||||||
|
/usr/bin/python3 ${SCRIPT_DIR}/src/utils_network_impairment.py --stop
|
||||||
21
bin/aqua.cfg
21
bin/aqua.cfg
|
|
@ -1,21 +0,0 @@
|
||||||
AQuA:
|
|
||||||
mode: files
|
|
||||||
# src: file test_audio/jane_8k.wav
|
|
||||||
# tstf: test_audio/jane_8k_40.wav
|
|
||||||
avlp: off
|
|
||||||
smtnrm: off
|
|
||||||
decor: off
|
|
||||||
mprio: off
|
|
||||||
acr: auto
|
|
||||||
npnt: auto
|
|
||||||
voip: on
|
|
||||||
enorm: rms
|
|
||||||
g711: off
|
|
||||||
spfrcor: on
|
|
||||||
grad: off
|
|
||||||
tmc: on
|
|
||||||
miter: 1
|
|
||||||
ratem: "%%m"
|
|
||||||
trim: "r 15"
|
|
||||||
output: json
|
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
||||||
|
This pjsua requires 10.14 at least!
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
400
bin/pvqa.cfg
400
bin/pvqa.cfg
|
|
@ -1,232 +1,226 @@
|
||||||
Common:
|
BOF Common
|
||||||
IntervalLength: 0.68
|
IntervalLength = 0.68
|
||||||
IsUseUncertain: no
|
IsUseUncertain = false
|
||||||
IsUseMixMode: yes
|
IsUseMixMode = true
|
||||||
IsUseDistance: no
|
IsUseDistance = false
|
||||||
AllWeight: 1.0
|
AllWeight = 1.0
|
||||||
SilWeight: 1.0
|
SilWeight = 1
|
||||||
VoiWeight: 1.0
|
VoiWeight = 1
|
||||||
AllCoefficient: 1.0
|
AllCoefficient = 1.0
|
||||||
SilCoefficient: 1.0
|
SilCoefficient = 1.0
|
||||||
VoiCoefficient: 1.0
|
VoiCoefficient = 1.0
|
||||||
SilThreshold: -37.50
|
SilThreshold = -37.50
|
||||||
IsOnePointSil: no
|
IsOnePointSil = false
|
||||||
IsNormResult: yes
|
IsNormResult = true
|
||||||
IsMapScore: yes
|
IsMapScore = true
|
||||||
NormalizeByRms: yes
|
EOF Common
|
||||||
|
|
||||||
SilenceEraser:
|
BOF Detector
|
||||||
Enabled: no
|
Name = SNR
|
||||||
Options:
|
DetectorType = SNR
|
||||||
|
IntThresh = 0.10
|
||||||
|
FrameThresh = 14
|
||||||
|
DetThresh = 0.10
|
||||||
|
PVQA-Flag = true
|
||||||
|
PVQA-Weight = 1.0
|
||||||
|
DetMode = Both
|
||||||
|
EOF Detector
|
||||||
|
|
||||||
Detector:
|
BOF Detector
|
||||||
- Name: SNR
|
Name = DeadAir-00
|
||||||
DetectorType: SNR
|
DetectorType = DeadAir
|
||||||
IntThresh: 0.10
|
IntThresh = 0.60
|
||||||
FrameThresh: 14
|
DetThresh = 0.60
|
||||||
DetThresh: 0.10
|
PVQA-Flag = true
|
||||||
PVQA-Flag: yes
|
PVQA-Weight = 1.0
|
||||||
PVQA-Weight: 1.0
|
DetMode = Both
|
||||||
DetMode: both
|
EOF Detector
|
||||||
|
|
||||||
- Name: Noise
|
BOF Detector
|
||||||
DetectorType: Noise
|
Name = DeadAir-01
|
||||||
IntThresh: 0.99
|
DetectorType = DeadAir
|
||||||
DetThresh: 0.99
|
IntThresh = 0.5
|
||||||
# This is still experimental detector so its values are not participating in MOS calculation
|
DetThresh = 0.5
|
||||||
PVQA-Flag: false
|
PVQA-Flag = true
|
||||||
PVQA-Weight: 1.0
|
PVQA-Weight = 1.0
|
||||||
DetMode: both
|
DetMode = Both
|
||||||
|
EOF Detector
|
||||||
|
|
||||||
- Name: DTMF
|
BOF Detector
|
||||||
DetectorType: DTMF
|
Name = Click
|
||||||
IntThresh: 0.99
|
DetectorType = Clicking
|
||||||
DetThresh: 0.99
|
IntThresh = 0.10
|
||||||
|
DetThresh = 0.10
|
||||||
|
PVQA-Flag = true
|
||||||
|
PVQA-Weight = 1.0
|
||||||
|
DetMode = Both
|
||||||
|
EOF Detector
|
||||||
|
|
||||||
# There is no sense to use detected DTMF signal in MOS calculation in the current config
|
BOF Detector
|
||||||
PVQA-Flag: no
|
Name = VAD-Clipping
|
||||||
PVQA-Weight: 0.0
|
DetectorType = VADClipping
|
||||||
DetMode: both
|
IntThresh = 0.0
|
||||||
|
FrameThresh = 0.0
|
||||||
|
DetThresh = 0.0
|
||||||
|
PVQA-Flag = true
|
||||||
|
PVQA-Weight = 1.0
|
||||||
|
DetMode = Both
|
||||||
|
EOF Detector
|
||||||
|
|
||||||
- Name: DeadAir-00
|
BOF Detector
|
||||||
DetectorType: DeadAir
|
Name = Amplitude-Clipping
|
||||||
IntThresh: 0.60
|
DetectorType = AmpClipping
|
||||||
DetThresh: 0.60
|
IntThresh = 0.00
|
||||||
PVQA-Flag: true
|
FrameThresh = 1.00
|
||||||
PVQA-Weight: 1.0
|
DetThresh = 0.00
|
||||||
DetMode: both
|
PVQA-Flag = true
|
||||||
|
PVQA-Weight = 1.00
|
||||||
|
DetMode = Both
|
||||||
|
EOF Detector
|
||||||
|
|
||||||
- Name: DeadAir-01
|
BOF Detector
|
||||||
DetectorType: DeadAir
|
Name = Dynamic-Clipping
|
||||||
IntThresh: 0.5
|
DetectorType = AmpClipping
|
||||||
DetThresh: 0.5
|
IntThresh = 0.05
|
||||||
PVQA-Flag: yes
|
FrameThresh = 1.50
|
||||||
PVQA-Weight: 1.0
|
DetThresh = 0
|
||||||
DetMode: both
|
PVQA-Flag = true
|
||||||
Override:
|
PVQA-Weight = 0.0
|
||||||
MinLevelThreshold: 0
|
DetMode = Voice
|
||||||
|
EOF Detector
|
||||||
|
|
||||||
- Name: Click
|
BOF Base EchoMono
|
||||||
DetectorType: Clicking
|
SamplesType = UnKnownCodec
|
||||||
IntThresh: 0.10
|
StepLengthSec = 0.5
|
||||||
DetThresh: 0.10
|
MinDelayMs = 50
|
||||||
PVQA-Flag: true
|
MaxLengthMs = 2800
|
||||||
PVQA-Weight: 1.0
|
WindowFunckID = 0
|
||||||
DetMode: both
|
SpanLengthMs = 50
|
||||||
|
EOF Base EchoMono
|
||||||
|
|
||||||
- Name: VAD-Clipping
|
BOF Detector
|
||||||
DetectorType: VADClipping
|
Name = ECHO
|
||||||
IntThresh: 0.0
|
DetectorType = EchoMono
|
||||||
FrameThresh: 0.0
|
IntThresh = 0.00
|
||||||
DetThresh: 0.0
|
FrameThresh = -40.0
|
||||||
PVQA-Flag: true
|
DetThresh = 0.00
|
||||||
PVQA-Weight: 1.0
|
PVQA-Flag = true
|
||||||
DetMode: both
|
PVQA-Weight = 1.0
|
||||||
|
DetMode = Voice
|
||||||
|
STAT-Flag = true
|
||||||
|
SpanLengthMs = 50
|
||||||
|
EOF Detector
|
||||||
|
|
||||||
- Name: AmpClipping
|
BOF Detector
|
||||||
DetectorType: AmpClipping
|
Name = Silent-Call-Detection
|
||||||
IntThresh: 0.00
|
DetectorType = DeadAir
|
||||||
FrameThresh: 1.00
|
IntThresh = 0.99
|
||||||
DetThresh: 0.00
|
DetThresh = 0.99
|
||||||
PVQA-Flag: true
|
PVQA-Flag = false
|
||||||
PVQA-Weight: 1.00
|
PVQA-Weight = 1.0
|
||||||
DetMode: both
|
EOF Detector
|
||||||
|
|
||||||
- Name: DynClipping
|
BOF Base SNR
|
||||||
DetectorType: AmpClipping
|
MinPowerThresh = 1.0000
|
||||||
IntThresh: 0.05
|
LogEnergyCoefficient = 10.0000
|
||||||
FrameThresh: 1.50
|
MinSignalLevel = 40.0000
|
||||||
DetThresh: 0
|
MinSNRDelta = 0.0001
|
||||||
PVQA-Flag: true
|
MinEnergyDisp = 3.0000
|
||||||
PVQA-Weight: 0.0
|
MinEnergyDelta = 1.0000
|
||||||
DetMode: voice
|
SamplesType = UnKnownCodec
|
||||||
Override:
|
EOF Base SNR
|
||||||
FlyAddingCoefficient: 0.1000
|
|
||||||
SamplesType: UnKnownCodec
|
|
||||||
IsUseDynamicClipping: yes
|
|
||||||
|
|
||||||
- Name: Echo
|
BOF Base AmpClipping
|
||||||
DetectorType: EchoMono
|
FlyAddingCoefficient = 0.1000
|
||||||
IntThresh: 0.00
|
IsUseDynamicClipping = false
|
||||||
FrameThresh: -40.0
|
SamplesType = UnKnownCodec
|
||||||
DetThresh: 0.00
|
EOF Base AmpClipping
|
||||||
PVQA-Flag: true
|
|
||||||
PVQA-Weight: 1.0
|
|
||||||
DetMode: voice
|
|
||||||
STAT-Flag: true
|
|
||||||
SpanLengthMs: 50
|
|
||||||
|
|
||||||
- Name: SilentCall
|
BOF Base Clicking
|
||||||
DetectorType: DeadAir
|
SamplesType = UnKnownCodec
|
||||||
IntThresh: 0.99
|
EOF Base Clicking
|
||||||
DetThresh: 0.99
|
|
||||||
PVQA-Flag: false
|
|
||||||
PVQA-Weight: 1.0
|
|
||||||
Override:
|
|
||||||
MinLevelThreshold: 0
|
|
||||||
IsUseRMSPower: yes
|
|
||||||
MinRMSThreshold: -70
|
|
||||||
|
|
||||||
|
BOF Base DeadAir
|
||||||
|
StuckDeltaThreshold = 6
|
||||||
|
MinNonStuckTime = 80
|
||||||
|
MinStuckTime = 80
|
||||||
|
MinStartNonStuckTime = 1920
|
||||||
|
MinLevelThreshold = 256
|
||||||
|
SamplesType = UnKnownCodec
|
||||||
|
EOF Base DeadAir
|
||||||
|
|
||||||
|
BOF Base VADClipping
|
||||||
|
SamplesType = UnKnownCodec
|
||||||
|
EOF Base VADClipping
|
||||||
|
|
||||||
Base EchoMono:
|
BOF DeadAir-01
|
||||||
SamplesType: UnKnownCodec
|
MinLevelThreshold = 0
|
||||||
StepLengthSec: 0.5
|
EOF DeadAir-01
|
||||||
MinDelayMs: 50
|
|
||||||
MaxLengthMs: 2800
|
|
||||||
WindowFunckID: 0
|
|
||||||
SpanLengthMs: 50
|
|
||||||
|
|
||||||
Base SNR:
|
BOF Silent-Call-Detection
|
||||||
MinPowerThresh: 1.0000
|
MinLevelThreshold = 0
|
||||||
LogEnergyCoefficient: 10.0000
|
IsUseRMSPower = true
|
||||||
MinSignalLevel: 40.0000
|
MinRMSThreshold = -70
|
||||||
MinSNRDelta: 0.0001
|
EOF Silent-Call-Detection
|
||||||
MinEnergyDisp: 3.0000
|
|
||||||
MinEnergyDelta: 1.0000
|
|
||||||
SamplesType: UnKnownCodec
|
|
||||||
|
|
||||||
Base DTMF:
|
BOF Dynamic-Clipping
|
||||||
SamplesType: UnKnownCodec
|
FlyAddingCoefficient = 0.1000
|
||||||
|
SamplesType = UnKnownCodec
|
||||||
|
IsUseDynamicClipping = true
|
||||||
|
EOF Dynamic-Clipping
|
||||||
|
|
||||||
Base AmpClipping:
|
BOF Correction
|
||||||
FlyAddingCoefficient: 0.1000
|
IntStart = 5.0
|
||||||
IsUseDynamicClipping: no
|
IntEnd = 4.2
|
||||||
SamplesType: UnKnownCodec
|
Mult = 1.0
|
||||||
|
#Shift = -1.7
|
||||||
|
Shift = 0
|
||||||
|
EOF Correction
|
||||||
|
|
||||||
Base Clicking:
|
BOF Correction
|
||||||
SamplesType: UnKnownCodec
|
IntStart = 4.2
|
||||||
|
IntEnd = 3.5
|
||||||
|
Mult = 1.0
|
||||||
|
#Shift = -0.85
|
||||||
|
Shift = 0
|
||||||
|
EOF Correction
|
||||||
|
|
||||||
Base DeadAir:
|
BOF SR Correction
|
||||||
StuckDeltaThreshold: 6
|
SampleRate = 11000.0
|
||||||
MinNonStuckTime: 80
|
Shift = 0.05
|
||||||
MinStuckTime: 80
|
EOF SR Correction
|
||||||
MinStartNonStuckTime: 1920
|
|
||||||
MinLevelThreshold: 256
|
|
||||||
SamplesType: UnKnownCodec
|
|
||||||
|
|
||||||
Base VADClipping:
|
BOF SR Correction
|
||||||
SamplesType: UnKnownCodec
|
SampleRate = 16000.0
|
||||||
|
Shift = 0.1
|
||||||
|
EOF SR Correction
|
||||||
|
|
||||||
Base Noise:
|
BOF SR Correction
|
||||||
Interval: 0.1 # Seconds
|
SampleRate = 22000.0
|
||||||
DetectorType: RMS # This can be FFT as well
|
Shift = 0.2
|
||||||
NoiseThreshold: 20
|
EOF SR Correction
|
||||||
SignalThreshold: 80
|
|
||||||
Normalize: no
|
|
||||||
RemoveBias: no
|
|
||||||
ResultDb: yes
|
|
||||||
WindowType: Hann
|
|
||||||
WindowWidth: 3
|
|
||||||
|
|
||||||
# Moved to Override: sections
|
BOF SR Correction
|
||||||
# DeadAir-01:
|
SampleRate = 32000.0
|
||||||
# MinLevelThreshold: 0
|
Shift = 0.3
|
||||||
|
EOF SR Correction
|
||||||
|
|
||||||
# SilentCall:
|
BOF SR Correction
|
||||||
# MinLevelThreshold: 0
|
SampleRate = 48000.0
|
||||||
# IsUseRMSPower: yes
|
Shift = 0.45
|
||||||
# MinRMSThreshold: -70
|
EOF SR Correction
|
||||||
|
|
||||||
# Dynamic-Clipping:
|
BOF SR Correction
|
||||||
# FlyAddingCoefficient: 0.1000
|
SampleRate = 96000.0
|
||||||
# SamplesType: UnKnownCodec
|
Shift = 0.5
|
||||||
# IsUseDynamicClipping: yes
|
EOF SR Correction
|
||||||
|
|
||||||
Correction:
|
BOF SR Correction
|
||||||
- IntStart: 5.0
|
SampleRate = 192000.0
|
||||||
IntEnd: 4.2
|
Shift = 0.6
|
||||||
Mult: 1.0
|
EOF SR Correction
|
||||||
Shift: 0
|
|
||||||
|
|
||||||
- IntStart: 4.2
|
BOF Scores Map
|
||||||
IntEnd: 3.5
|
ScoresLine = 4;3.027000;2.935000;2.905000;2.818000;2.590000;2.432000;2.310000;1.665000;1.000000;
|
||||||
Mult: 1.0
|
EOF Scores Map
|
||||||
Shift: 0
|
|
||||||
|
|
||||||
|
|
||||||
SR Correction:
|
|
||||||
- SampleRate: 11000.0
|
|
||||||
Shift: 0.05
|
|
||||||
|
|
||||||
- SampleRate: 16000.0
|
|
||||||
Shift: 0.1
|
|
||||||
|
|
||||||
- SampleRate: 22000.0
|
|
||||||
Shift: 0.2
|
|
||||||
|
|
||||||
- SampleRate: 32000.0
|
|
||||||
Shift: 0.3
|
|
||||||
|
|
||||||
- SampleRate: 48000.0
|
|
||||||
Shift: 0.45
|
|
||||||
|
|
||||||
- SampleRate: 96000.0
|
|
||||||
Shift: 0.5
|
|
||||||
|
|
||||||
- SampleRate: 192000.0
|
|
||||||
Shift: 0.6
|
|
||||||
|
|
||||||
Scores Map:
|
|
||||||
ScoresLine: 4;3.027000;2.935000;2.905000;2.818000;2.590000;2.432000;2.310000;1.665000;1.000000;
|
|
||||||
|
|
||||||
|
|
|
||||||
BIN
bin/rpi/aqua-wb
BIN
bin/rpi/aqua-wb
Binary file not shown.
BIN
bin/rpi/pvqa
BIN
bin/rpi/pvqa
Binary file not shown.
Binary file not shown.
|
|
@ -25,8 +25,8 @@ rabbitmq:
|
||||||
cache_dir: cache
|
cache_dir: cache
|
||||||
audio:
|
audio:
|
||||||
# Silence prefix & suffix lengths (in seconds)
|
# Silence prefix & suffix lengths (in seconds)
|
||||||
silence_prefix: 10
|
silence_prefix: 30
|
||||||
silence_suffix: 10
|
silence_suffix: 30
|
||||||
|
|
||||||
bluetooth_mac: "MAC_ADDRESS"
|
bluetooth_mac: "MAC_ADDRESS"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
# Defaults for hostapd initscript
|
|
||||||
#
|
|
||||||
# WARNING: The DAEMON_CONF setting has been deprecated and will be removed
|
|
||||||
# in future package releases.
|
|
||||||
#
|
|
||||||
# See /usr/share/doc/hostapd/README.Debian for information about alternative
|
|
||||||
# methods of managing hostapd.
|
|
||||||
#
|
|
||||||
# Uncomment and set DAEMON_CONF to the absolute path of a hostapd configuration
|
|
||||||
# file and hostapd will be started during system boot. An example configuration
|
|
||||||
# file can be found at /usr/share/doc/hostapd/examples/hostapd.conf.gz
|
|
||||||
#
|
|
||||||
DAEMON_CONF="/etc/hostapd/hostapd.conf"
|
|
||||||
|
|
||||||
# Additional daemon options to be appended to hostapd command:-
|
|
||||||
# -d show more debug messages (-dd for even more)
|
|
||||||
# -K include key data in debug messages
|
|
||||||
# -t include timestamps in some debug messages
|
|
||||||
#
|
|
||||||
# Note that -B (daemon mode) and -P (pidfile) options are automatically
|
|
||||||
# configured by the init.d script and must not be added to DAEMON_OPTS.
|
|
||||||
#
|
|
||||||
#DAEMON_OPTS=""
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
# A sample configuration for dhcpcd.
|
|
||||||
# See dhcpcd.conf(5) for details.
|
|
||||||
|
|
||||||
# Allow users of this group to interact with dhcpcd via the control socket.
|
|
||||||
#controlgroup wheel
|
|
||||||
|
|
||||||
# Inform the DHCP server of our hostname for DDNS.
|
|
||||||
hostname
|
|
||||||
|
|
||||||
# Use the hardware address of the interface for the Client ID.
|
|
||||||
clientid
|
|
||||||
# or
|
|
||||||
# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.
|
|
||||||
# Some non-RFC compliant DHCP servers do not reply with this set.
|
|
||||||
# In this case, comment out duid and enable clientid above.
|
|
||||||
#duid
|
|
||||||
|
|
||||||
# Persist interface configuration when dhcpcd exits.
|
|
||||||
persistent
|
|
||||||
|
|
||||||
# Rapid commit support.
|
|
||||||
# Safe to enable by default because it requires the equivalent option set
|
|
||||||
# on the server to actually work.
|
|
||||||
option rapid_commit
|
|
||||||
|
|
||||||
# A list of options to request from the DHCP server.
|
|
||||||
option domain_name_servers, domain_name, domain_search, host_name
|
|
||||||
option classless_static_routes
|
|
||||||
# Respect the network MTU. This is applied to DHCP routes.
|
|
||||||
option interface_mtu
|
|
||||||
|
|
||||||
# Most distributions have NTP support.
|
|
||||||
#option ntp_servers
|
|
||||||
|
|
||||||
# A ServerID is required by RFC2131.
|
|
||||||
require dhcp_server_identifier
|
|
||||||
|
|
||||||
# Generate SLAAC address using the Hardware Address of the interface
|
|
||||||
#slaac hwaddr
|
|
||||||
# OR generate Stable Private IPv6 Addresses based from the DUID
|
|
||||||
slaac private
|
|
||||||
|
|
||||||
# Example static IP configuration:
|
|
||||||
#interface eth0
|
|
||||||
#static ip_address=192.168.0.10/24
|
|
||||||
#static ip6_address=fd51:42f8:caae:d92e::ff/64
|
|
||||||
#static routers=192.168.0.1
|
|
||||||
#static domain_name_servers=192.168.0.1 8.8.8.8 fd51:42f8:caae:d92e::1
|
|
||||||
|
|
||||||
# It is possible to fall back to a static IP if DHCP fails:
|
|
||||||
# define static profile
|
|
||||||
#profile static_eth0
|
|
||||||
#static ip_address=192.168.1.23/24
|
|
||||||
#static routers=192.168.1.1
|
|
||||||
#static domain_name_servers=192.168.1.1
|
|
||||||
|
|
||||||
# fallback to static profile on eth0
|
|
||||||
#interface eth0
|
|
||||||
#fallback static_eth0
|
|
||||||
#
|
|
||||||
#
|
|
||||||
interface wlan0
|
|
||||||
static ip_address=192.168.45.1/24
|
|
||||||
static routers=192.168.45.1
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
interface=wlan0 # Listening interface
|
|
||||||
dhcp-range=192.168.45.10,192.168.45.20,255.255.255.0,24h # Pool of IP addresses for wireless clients
|
|
||||||
domain=wlan # Domain
|
|
||||||
address=/gw.wlan/192.168.45.1 # Alias for router
|
|
||||||
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
country_code=GB
|
|
||||||
interface=wlan0
|
|
||||||
ssid=AGENT_GSM
|
|
||||||
hw_mode=g
|
|
||||||
channel=7
|
|
||||||
macaddr_acl=0
|
|
||||||
auth_algs=1
|
|
||||||
ignore_broadcast_ssid=0
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=dnsmasq - A lightweight DHCP and caching DNS server
|
|
||||||
Requires=network.target
|
|
||||||
Wants=network-online.target
|
|
||||||
Before=nss-lookup.target
|
|
||||||
After=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=forking
|
|
||||||
PIDFile=/run/dnsmasq/dnsmasq.pid
|
|
||||||
|
|
||||||
# Test the config file and refuse starting if it is not valid.
|
|
||||||
ExecStartPre=/usr/sbin/dnsmasq --test
|
|
||||||
|
|
||||||
# We run dnsmasq via the /etc/init.d/dnsmasq script which acts as a
|
|
||||||
# wrapper picking up extra configuration files and then execs dnsmasq
|
|
||||||
# itself, when called with the "systemd-exec" function.
|
|
||||||
ExecStart=/etc/init.d/dnsmasq systemd-exec
|
|
||||||
|
|
||||||
# The systemd-*-resolvconf functions configure (and deconfigure)
|
|
||||||
# resolvconf to work with the dnsmasq DNS server. They're called like
|
|
||||||
# this to get correct error handling (ie don't start-resolvconf if the
|
|
||||||
# dnsmasq daemon fails to start.
|
|
||||||
ExecStartPost=/etc/init.d/dnsmasq systemd-start-resolvconf
|
|
||||||
ExecStop=/etc/init.d/dnsmasq systemd-stop-resolvconf
|
|
||||||
|
|
||||||
|
|
||||||
ExecReload=/bin/kill -HUP $MAINPID
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
|
|
@ -3,12 +3,6 @@
|
||||||
# Oneliner to find script's directory. Please note - last path component should NOT be symlink.
|
# Oneliner to find script's directory. Please note - last path component should NOT be symlink.
|
||||||
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
|
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
|
||||||
|
|
||||||
for pid in $(pidof -x run_agent.sh); do
|
|
||||||
if [ $pid != $$ ]; then
|
|
||||||
echo "[$(date)] : run_agent.sh : Process is already running with PID $pid. Exiting."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
while :
|
while :
|
||||||
do
|
do
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ INSTALL_DIR=agent_gsm
|
||||||
GIT_SOURCE=https://git.sevana.biz/public/agent_gsm
|
GIT_SOURCE=https://git.sevana.biz/public/agent_gsm
|
||||||
|
|
||||||
# Install prerequisites
|
# Install prerequisites
|
||||||
sudo apt install --assume-yes git mc python3 sox vim libffi-dev screen python3-pip python3-numpy dnsmasq hostapd screen
|
sudo apt install --assume-yes git mc python3 sox vim libffi-dev screen python3-pip python3-numpy
|
||||||
sudo pip3 install pyyaml sox pyrabbit soundfile dbus_python pexpect requests rabbitpy bottle
|
sudo pip3 install pyyaml sox pyrabbit soundfile dbus_python pexpect pydub requests rabbitpy pydub
|
||||||
|
|
||||||
if [ -f "$INSTALL_DIR" ]; then
|
if [ -f "$INSTALL_DIR" ]; then
|
||||||
rm -rf "$INSTALL_DIR"
|
rm -rf "$INSTALL_DIR"
|
||||||
|
|
@ -45,7 +45,7 @@ cp config/agent.in.yaml config/agent.yaml
|
||||||
mkdir -p ~/.config/mc
|
mkdir -p ~/.config/mc
|
||||||
cp config/mc/ini ~/.config/mc
|
cp config/mc/ini ~/.config/mc
|
||||||
|
|
||||||
# Replace the values - finish preparing the agent configuration file
|
# Replace the values
|
||||||
if [[ $BACKEND_URL != "" ]]; then
|
if [[ $BACKEND_URL != "" ]]; then
|
||||||
sed -i "s|BACKEND|$BACKEND|" config/agent.yaml
|
sed -i "s|BACKEND|$BACKEND|" config/agent.yaml
|
||||||
fi
|
fi
|
||||||
|
|
@ -56,45 +56,11 @@ fi
|
||||||
|
|
||||||
sed -i "s|TASK_NAME|$TASK_NAME|" config/agent.yaml
|
sed -i "s|TASK_NAME|$TASK_NAME|" config/agent.yaml
|
||||||
|
|
||||||
|
# Update systemD unit file
|
||||||
|
cp config/systemd/agent_gsm.in.service config/systemd/agent_gsm.service
|
||||||
ABSOLUTE_INSTALL_DIR=`realpath .`
|
ABSOLUTE_INSTALL_DIR=`realpath .`
|
||||||
|
|
||||||
# Update systemD unit file
|
sed -i "s|ABSOLUTE_INSTALL_DIR|$ABSOLUTE_INSTALL_DIR|" config/systemd/agent_gsm.service
|
||||||
# cp config/systemd/agent_gsm.in.service config/systemd/agent_gsm.service
|
|
||||||
# sed -i "s|ABSOLUTE_INSTALL_DIR|$ABSOLUTE_INSTALL_DIR|" config/systemd/agent_gsm.service
|
|
||||||
|
|
||||||
install_ap() {
|
|
||||||
# $1 is AP name
|
|
||||||
sudo cp $ABSOLUTE_INSTALL_DIR/config/ap/etc/dhcpcd.conf /etc
|
|
||||||
sudo cp $ABSOLUTE_INSTALL_DIR/config/ap/etc/dnsmasq.conf /etc
|
|
||||||
sudo mkdir -p /etc/hostapd
|
|
||||||
sudo cp $ABSOLUTE_INSTALL_DIR/config/ap/etc/hostapd.conf /etc/hostapd
|
|
||||||
sudo sed -i "s|AGENT_GSM|$1|" /etc/hostapt/hostapd.conf
|
|
||||||
sudo cp $ABSOLUTE_INSTALL_DIR/config/ap/etc/default/hostapt /etd/default
|
|
||||||
sudo systemctl enable dnsmasq
|
|
||||||
sudo systemctl enable hostapd
|
|
||||||
sudo systemctl start dnsmasq
|
|
||||||
sudo systemctl start hostapd
|
|
||||||
}
|
|
||||||
|
|
||||||
function enable_autologin() {
|
|
||||||
sudo systemctl --quiet set-default multi-user.target
|
|
||||||
sudo cat > /etc/systemd/system/getty@tty1.service.d/autologin.conf << EOF
|
|
||||||
[Service]
|
|
||||||
ExecStart=
|
|
||||||
ExecStart=-/sbin/agetty --autologin $USER --noclear %I \$TERM
|
|
||||||
EOF
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# ToDo:
|
|
||||||
# - allow autologin in console mode for 'pi' user
|
|
||||||
enable_autologin
|
|
||||||
|
|
||||||
# - add $ABSOLUTE_INSTALL_DIR/run_agent_screen.sh to ~/.bashrc
|
|
||||||
echo "$ABSOLUTE_INSTALL_DIR/run_agent_screen.sh" >> ~/.bashrc
|
|
||||||
|
|
||||||
# - install wifi AP with name $PHONE_NAME
|
|
||||||
install_ap $PHONE_NAME
|
|
||||||
|
|
||||||
echo "Now the remaining prerequisites will be installed and system will reboot."
|
echo "Now the remaining prerequisites will be installed and system will reboot."
|
||||||
echo "You can connect the phone via Bluetooth after the reboot."
|
echo "You can connect the phone via Bluetooth after the reboot."
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,8 @@ class AgentConfig:
|
||||||
# Should the first task run immediately ?
|
# Should the first task run immediately ?
|
||||||
ForceRun = False
|
ForceRun = False
|
||||||
|
|
||||||
# Use external speech detector if needed
|
# Use silence eraser or not (speech detector is used in this case)
|
||||||
UseSpeechDetector = False
|
UseSilenceEraser = True
|
||||||
|
|
||||||
# Path to log file
|
# Path to log file
|
||||||
LogPath : Path = None
|
LogPath : Path = None
|
||||||
|
|
@ -90,7 +90,7 @@ class AgentConfig:
|
||||||
|
|
||||||
if 'speech_detector' in config:
|
if 'speech_detector' in config:
|
||||||
if config['speech_detector']:
|
if config['speech_detector']:
|
||||||
self.UseSpeechDetector = True
|
self.UseSilenceEraser = False
|
||||||
|
|
||||||
if 'audio' in config:
|
if 'audio' in config:
|
||||||
audio = config['audio']
|
audio = config['audio']
|
||||||
|
|
|
||||||
111
src/agent_gsm.py
111
src/agent_gsm.py
|
|
@ -24,7 +24,6 @@ from bt_signal import SignalBoundaries
|
||||||
from bt_call_controller import INTERRUPT_SIGNAL
|
from bt_call_controller import INTERRUPT_SIGNAL
|
||||||
import bt_call_controller
|
import bt_call_controller
|
||||||
|
|
||||||
import agent_point
|
|
||||||
|
|
||||||
CONFIG = AgentConfig()
|
CONFIG = AgentConfig()
|
||||||
|
|
||||||
|
|
@ -69,23 +68,17 @@ def detect_degraded_signal(file_test: Path, file_reference: Path) -> SignalBound
|
||||||
# Seems some problem with recording, return zero boundaries
|
# Seems some problem with recording, return zero boundaries
|
||||||
return SignalBoundaries()
|
return SignalBoundaries()
|
||||||
|
|
||||||
r = SignalBoundaries()
|
r = bt_signal.find_reference_signal(file_test)
|
||||||
if CONFIG.UseSpeechDetector:
|
|
||||||
r = bt_signal.find_reference_signal_via_speechdetector(file_test)
|
|
||||||
|
|
||||||
if r.offset_start == 0.0 and is_caller:
|
if r.offset_start == 0.0 and is_caller:
|
||||||
r.offset_start = 5.0 # Skip ringing tones
|
r.offset_start = 5.0 # Skip ringing tones
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def detect_reference_signal(file_reference: Path) -> SignalBoundaries:
|
def detect_reference_signal(file_reference: Path) -> SignalBoundaries:
|
||||||
# Run silence eraser on reference file as well
|
# Run silence eraser on reference file as well
|
||||||
result = SignalBoundaries()
|
result = bt_signal.find_reference_signal(file_reference)
|
||||||
if CONFIG.UseSpeechDetector:
|
|
||||||
result = bt_signal.find_reference_signal_via_speechdetector(file_reference)
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -99,8 +92,7 @@ def upload_results():
|
||||||
# Path to audio
|
# Path to audio
|
||||||
path_audio = t[1]
|
path_audio = t[1]
|
||||||
|
|
||||||
utils.log(f'Found {t} report pair.')
|
utils.log(f'Found {path_report.name} and {path_audio.name} files.')
|
||||||
if path_report is not None and path_report.exists():
|
|
||||||
try:
|
try:
|
||||||
with open(path_report, 'rt') as f:
|
with open(path_report, 'rt') as f:
|
||||||
report = json.loads(f.read())
|
report = json.loads(f.read())
|
||||||
|
|
@ -112,16 +104,26 @@ def upload_results():
|
||||||
if success:
|
if success:
|
||||||
utils.log(f'Report {upload_id} is uploaded ok.')
|
utils.log(f'Report {upload_id} is uploaded ok.')
|
||||||
|
|
||||||
os.remove(path_report)
|
# Rename files to make sync audio filename with reported ones
|
||||||
|
# path_report_fixed = CACHE.dir / f'{upload_id}.json'
|
||||||
|
# path_report = path_report.rename(path_report_fixed)
|
||||||
|
|
||||||
if path_audio is not None and path_audio.exists():
|
# path_audio_fixed = CACHE.dir / f'{upload_id}.wav'
|
||||||
|
# path_audio = path_audio.rename(path_audio_fixed)
|
||||||
|
if path_audio.exists():
|
||||||
utils.log(f'Uploading {path_audio.name} file...')
|
utils.log(f'Uploading {path_audio.name} file...')
|
||||||
# Upload recorded audio
|
# Upload recorded audio
|
||||||
upload_result = BACKEND.upload_audio(path_audio.stem, path_audio)
|
upload_result = BACKEND.upload_audio(upload_id, path_audio)
|
||||||
if upload_result:
|
if upload_result:
|
||||||
utils.log(f' Recorded audio {path_audio.stem}.wav is uploaded ok.')
|
utils.log(f' Recorded audio {upload_id}.wav is uploaded ok.')
|
||||||
os.remove(path_audio)
|
os.remove(path_audio)
|
||||||
|
else:
|
||||||
|
utils.log(f'No recorded audio file found, skipping audio upload.')
|
||||||
|
os.remove(path_report)
|
||||||
|
|
||||||
|
else:
|
||||||
|
utils.log(f'Failed to upload report {path_report.name}')
|
||||||
|
break
|
||||||
|
|
||||||
def run_analyze(file_test: str, file_reference: str, number: str) -> bool:
|
def run_analyze(file_test: str, file_reference: str, number: str) -> bool:
|
||||||
global CALL_COUNTER
|
global CALL_COUNTER
|
||||||
|
|
@ -145,28 +147,25 @@ def run_analyze(file_test: str, file_reference: str, number: str) -> bool:
|
||||||
utils.log(f'Recorded audio call duration: {test_audio_length}s, reference audio length: {ref_audio_length}s')
|
utils.log(f'Recorded audio call duration: {test_audio_length}s, reference audio length: {ref_audio_length}s')
|
||||||
|
|
||||||
# Check if audio length is strange - skip such calls. Usually this is missed call.
|
# Check if audio length is strange - skip such calls. Usually this is missed call.
|
||||||
is_caller_audio_big = is_caller and test_audio_length > ref_audio_length * 3
|
is_caller_audio_big = is_caller and test_audio_length > ref_audio_length * 1.5
|
||||||
is_answerer_audio_big = is_answerer and test_audio_length > ref_audio_length * 3
|
is_answerer_audio_big = is_answerer and test_audio_length > ref_audio_length * 1.5
|
||||||
|
|
||||||
if is_caller_audio_big or is_answerer_audio_big:
|
if is_caller_audio_big or is_answerer_audio_big:
|
||||||
utils.log_error(f'Recorded call is too big - looks like mobile operator prompt, skipping analysis')
|
utils.log_error(f'Recorded call is too big - looks like mobile operator prompt, skipping analysis')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
bounds_signal = SignalBoundaries()
|
bounds_signal : SignalBoundaries = detect_degraded_signal(Path(file_test), Path(file_reference))
|
||||||
if is_caller:
|
# bounds_signal.offset_start = 0
|
||||||
bounds_signal.offset_start = 10.0 # Skip ringtones
|
# bounds_signal.offset_finish = 0
|
||||||
bounds_signal.offset_finish = 1.0 # Eat possible end tone
|
print(f'Found signal bounds: {bounds_signal}')
|
||||||
elif is_answerer:
|
|
||||||
bounds_signal.offset_start = 0.0
|
|
||||||
bounds_signal.offset_finish = 1.0 # Eat possible end tone
|
|
||||||
|
|
||||||
# PVQA report
|
# PVQA report
|
||||||
pvqa_mos, pvqa_report, pvqa_rfactor = utils_sevana.find_pvqa_mos(file_test, bounds_signal.offset_start, bounds_signal.offset_finish)
|
pvqa_mos, pvqa_report, pvqa_rfactor = utils_sevana.find_pvqa_mos(file_test, bounds_signal.offset_start, bounds_signal.offset_finish)
|
||||||
utils.log(f'PVQA MOS: {pvqa_mos}, PVQA R-factor: {pvqa_rfactor}')
|
utils.log(f'PVQA MOS: {pvqa_mos}, PVQA R-factor: {pvqa_rfactor}')
|
||||||
|
|
||||||
# AQuA report
|
# AQuA report
|
||||||
bounds_reference : SignalBoundaries = SignalBoundaries()
|
bounds_reference : SignalBoundaries = detect_reference_signal(Path(file_reference))
|
||||||
bounds_reference.offset_start = 0
|
bounds_reference.offset_start = 0
|
||||||
bounds_reference.offset_finish = 0
|
bounds_reference.offset_finish = 0
|
||||||
|
|
||||||
|
|
@ -262,7 +261,7 @@ def make_call(target: str):
|
||||||
timelimit_seconds=ref_time_length,
|
timelimit_seconds=ref_time_length,
|
||||||
target=target)
|
target=target)
|
||||||
|
|
||||||
run_analyze(CONFIG.RecordFile, CONFIG.PreparedReferenceAudio, target)
|
run_analyze(CONFIG.RecordFile, CONFIG.ReferenceAudio, target)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
utils.log_error(f'BT I/O failed finally. Error: {str(e)}')
|
utils.log_error(f'BT I/O failed finally. Error: {str(e)}')
|
||||||
|
|
||||||
|
|
@ -298,7 +297,7 @@ def perform_answerer():
|
||||||
break
|
break
|
||||||
|
|
||||||
# Call analyzer script
|
# Call analyzer script
|
||||||
run_analyze(CONFIG.RecordFile, CONFIG.PreparedReferenceAudio, '')
|
run_analyze(CONFIG.RecordFile, CONFIG.ReferenceAudio, '')
|
||||||
|
|
||||||
# Increase counter of attempts
|
# Increase counter of attempts
|
||||||
attempt_idx += 1
|
attempt_idx += 1
|
||||||
|
|
@ -355,10 +354,10 @@ def run_caller_task(t):
|
||||||
make_call(target_addr)
|
make_call(target_addr)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Runs caller probe - load task list and perform calls
|
# Runs caller probe - load task list and perform calls
|
||||||
def run_probe():
|
def run_probe():
|
||||||
global TASK_LIST, CURRENT_TASK
|
global TASK_LIST, CURRENT_TASK
|
||||||
offline_mode : bool = False
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
# Get task list update
|
# Get task list update
|
||||||
|
|
@ -367,7 +366,6 @@ def run_probe():
|
||||||
# Check in cache
|
# Check in cache
|
||||||
utils.log('Checking for task list in cache...')
|
utils.log('Checking for task list in cache...')
|
||||||
new_tasks = CACHE.get_tasks(BACKEND.phone.name)
|
new_tasks = CACHE.get_tasks(BACKEND.phone.name)
|
||||||
offline_mode = True
|
|
||||||
|
|
||||||
# Did we fetch anything ?
|
# Did we fetch anything ?
|
||||||
if new_tasks:
|
if new_tasks:
|
||||||
|
|
@ -388,7 +386,7 @@ def run_probe():
|
||||||
if TASK_LIST.tasks is not None:
|
if TASK_LIST.tasks is not None:
|
||||||
utils.log_verbose(f"Resulting task list: {TASK_LIST.tasks}")
|
utils.log_verbose(f"Resulting task list: {TASK_LIST.tasks}")
|
||||||
|
|
||||||
# Run test immediately if specified
|
|
||||||
if CONFIG.ForceRun and len(TASK_LIST.tasks) > 0:
|
if CONFIG.ForceRun and len(TASK_LIST.tasks) > 0:
|
||||||
run_caller_task(TASK_LIST.tasks[0])
|
run_caller_task(TASK_LIST.tasks[0])
|
||||||
break
|
break
|
||||||
|
|
@ -403,9 +401,7 @@ def run_probe():
|
||||||
# Remove sheduled time
|
# Remove sheduled time
|
||||||
del t['scheduled_time']
|
del t['scheduled_time']
|
||||||
|
|
||||||
# Run task if we are online
|
# Run task
|
||||||
# Otherwise tasks run from the API point - via helper .apk
|
|
||||||
if not offline_mode:
|
|
||||||
run_caller_task(t)
|
run_caller_task(t)
|
||||||
|
|
||||||
utils.log_verbose(f'Call #{CALL_COUNTER.value} finished')
|
utils.log_verbose(f'Call #{CALL_COUNTER.value} finished')
|
||||||
|
|
@ -417,35 +413,15 @@ def run_probe():
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
utils.log_error(message="Unexpected error.", err=err)
|
utils.log_error(message="Unexpected error.", err=err)
|
||||||
|
|
||||||
# Sleep for
|
|
||||||
spent_time = utils.get_monotonic_time() - start_time
|
spent_time = utils.get_monotonic_time() - start_time
|
||||||
|
|
||||||
# Wait 1 minute
|
# Wait 1 minute
|
||||||
if spent_time < 60:
|
if spent_time < 60:
|
||||||
timeout_time = 60 - spent_time
|
time.sleep(60 - spent_time)
|
||||||
else:
|
|
||||||
timeout_time = 0
|
|
||||||
|
|
||||||
# Try to get next task
|
|
||||||
try:
|
|
||||||
if agent_point.WEB_QUEUE is None:
|
|
||||||
utils.log('Web task queue is None')
|
|
||||||
|
|
||||||
task = agent_point.WEB_QUEUE.get(block=True, timeout=timeout_time)
|
|
||||||
|
|
||||||
if task is not None:
|
|
||||||
run_caller_task(task)
|
|
||||||
|
|
||||||
except multiprocessing.Queue.empty:
|
|
||||||
# Ignore this exception, this is normal
|
|
||||||
pass
|
|
||||||
except Exception as err:
|
|
||||||
utils.log_error(message='Error when running t')
|
|
||||||
|
|
||||||
|
|
||||||
# In case of empty task list wait 1 minute before refresh
|
# In case of empty task list wait 1 minute before refresh
|
||||||
# if len(TASK_LIST.tasks) == 0:
|
if len(TASK_LIST.tasks) == 0:
|
||||||
# time.sleep(60)
|
time.sleep(60)
|
||||||
|
|
||||||
|
|
||||||
def remove_pid_on_exit():
|
def remove_pid_on_exit():
|
||||||
|
|
@ -460,9 +436,6 @@ def receive_signal(signal_number, frame):
|
||||||
# Delete PID file
|
# Delete PID file
|
||||||
remove_pid_on_exit()
|
remove_pid_on_exit()
|
||||||
|
|
||||||
# Stop optional access point
|
|
||||||
agent_point.stop()
|
|
||||||
|
|
||||||
# Debugging info
|
# Debugging info
|
||||||
print(f'Got signal {signal_number} from {frame}')
|
print(f'Got signal {signal_number} from {frame}')
|
||||||
|
|
||||||
|
|
@ -474,6 +447,7 @@ def receive_signal(signal_number, frame):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Check if Python version is ok
|
# Check if Python version is ok
|
||||||
assert sys.version_info >= (3, 6)
|
assert sys.version_info >= (3, 6)
|
||||||
|
|
||||||
|
|
@ -497,14 +471,6 @@ if __name__ == '__main__':
|
||||||
signal.signal(signal.SIGINT, receive_signal)
|
signal.signal(signal.SIGINT, receive_signal)
|
||||||
signal.signal(signal.SIGQUIT, receive_signal)
|
signal.signal(signal.SIGQUIT, receive_signal)
|
||||||
|
|
||||||
if CONFIG.CacheDir:
|
|
||||||
CACHE = utils_cache.InfoCache(dir=CONFIG.CacheDir)
|
|
||||||
|
|
||||||
# Start own hotspot and API server
|
|
||||||
agent_point.CONFIG = CONFIG
|
|
||||||
agent_point.CACHE = CACHE
|
|
||||||
agent_point.start()
|
|
||||||
|
|
||||||
# Preconnect the phone
|
# Preconnect the phone
|
||||||
if CONFIG.BT_MAC:
|
if CONFIG.BT_MAC:
|
||||||
# Connect to phone before
|
# Connect to phone before
|
||||||
|
|
@ -516,7 +482,7 @@ if __name__ == '__main__':
|
||||||
utils.log_error(f'No BT MAC specified, cannot connect. Exiting.')
|
utils.log_error(f'No BT MAC specified, cannot connect. Exiting.')
|
||||||
raise SystemExit(EXIT_ERROR)
|
raise SystemExit(EXIT_ERROR)
|
||||||
|
|
||||||
# Init BT modem - here we wait for it
|
# Init BT modem
|
||||||
bt_call_controller.init()
|
bt_call_controller.init()
|
||||||
|
|
||||||
# Logging settings
|
# Logging settings
|
||||||
|
|
@ -525,12 +491,18 @@ if __name__ == '__main__':
|
||||||
if CONFIG.LogPath:
|
if CONFIG.LogPath:
|
||||||
utils.open_log_file(CONFIG.LogPath, 'at')
|
utils.open_log_file(CONFIG.LogPath, 'at')
|
||||||
|
|
||||||
|
|
||||||
|
if CONFIG.CacheDir:
|
||||||
|
CACHE = utils_cache.InfoCache(dir=CONFIG.CacheDir)
|
||||||
|
|
||||||
|
|
||||||
# Update path to pvqa/aqua-wb
|
# Update path to pvqa/aqua-wb
|
||||||
VOICE_QUALITY_AVAILABLE = utils_sevana.find_binaries(DIR_PROJECT / 'bin')
|
VOICE_QUALITY_AVAILABLE = utils_sevana.find_binaries(DIR_PROJECT / 'bin')
|
||||||
|
|
||||||
# Load latest licenses & configs - this requires utils_sevana.find_binaries() to be called before
|
# Load latest licenses & configs - this requires utils_sevana.find_binaries() to be called before
|
||||||
# utils_sevana.load_config_and_licenses(config['backend'])
|
# utils_sevana.load_config_and_licenses(config['backend'])
|
||||||
|
|
||||||
|
|
||||||
# Limit number of calls
|
# Limit number of calls
|
||||||
if CONFIG.TaskLimit:
|
if CONFIG.TaskLimit:
|
||||||
utils.log(f'Limiting number of calls to {CONFIG.TaskLimit}')
|
utils.log(f'Limiting number of calls to {CONFIG.TaskLimit}')
|
||||||
|
|
@ -598,7 +570,4 @@ if __name__ == '__main__':
|
||||||
# Close log file
|
# Close log file
|
||||||
utils.close_log_file()
|
utils.close_log_file()
|
||||||
|
|
||||||
# Stop optional access point
|
|
||||||
agent_point.stop()
|
|
||||||
|
|
||||||
sys.exit(EXIT_OK)
|
sys.exit(EXIT_OK)
|
||||||
|
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
#!/usr/bin/python3
|
|
||||||
import bottle
|
|
||||||
import multiprocessing
|
|
||||||
import time
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import utils_cache
|
|
||||||
from agent_config import AgentConfig
|
|
||||||
|
|
||||||
class AccessPoint:
|
|
||||||
active: bool = False
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Just a stub for now
|
|
||||||
ACCESS_POINT = AccessPoint()
|
|
||||||
|
|
||||||
# Web server process
|
|
||||||
SERVER_PROCESS = None
|
|
||||||
|
|
||||||
# Good status response
|
|
||||||
RESPONSE_OK = {'status': 'ok'}
|
|
||||||
|
|
||||||
# Available information in cache
|
|
||||||
CACHE : utils_cache.InfoCache = None
|
|
||||||
CONFIG: AgentConfig = None
|
|
||||||
|
|
||||||
# Web queue
|
|
||||||
WEB_QUEUE = multiprocessing.Manager().Queue()
|
|
||||||
|
|
||||||
@bottle.route('/status')
|
|
||||||
def web_status():
|
|
||||||
print(f'Serving /status request...')
|
|
||||||
|
|
||||||
r = RESPONSE_OK
|
|
||||||
if CONFIG is not None:
|
|
||||||
r['name'] = CONFIG.Name
|
|
||||||
r['backend'] = CONFIG.Backend
|
|
||||||
r['bt_mac'] = CONFIG.BT_MAC
|
|
||||||
|
|
||||||
if CACHE is not None:
|
|
||||||
print('Cache is found...')
|
|
||||||
# Phone information
|
|
||||||
phone = CACHE.get_phone(CONFIG.Name)
|
|
||||||
if phone is not None:
|
|
||||||
print('Phone information is found...')
|
|
||||||
r['phone'] = phone.to_dict()
|
|
||||||
|
|
||||||
# Task list information
|
|
||||||
task_list = CACHE.get_tasks(CONFIG.Name)
|
|
||||||
if task_list is not None and task_list.tasks is not None:
|
|
||||||
r['task_list'] = task_list.tasks
|
|
||||||
else:
|
|
||||||
print('Cache not found.')
|
|
||||||
return r
|
|
||||||
|
|
||||||
@bottle.route('/reboot')
|
|
||||||
def web_reboot():
|
|
||||||
os.system('sudo reboot')
|
|
||||||
return RESPONSE_OK
|
|
||||||
|
|
||||||
@bottle.route('/halt')
|
|
||||||
def web_reboot():
|
|
||||||
os.system('sudo halt')
|
|
||||||
return RESPONSE_OK
|
|
||||||
|
|
||||||
@bottle.route('/cache')
|
|
||||||
def web_list_cache():
|
|
||||||
result = []
|
|
||||||
if CACHE is None:
|
|
||||||
return result
|
|
||||||
|
|
||||||
# Iterate cache and return available files list
|
|
||||||
for f in os.listdir(CACHE.dir):
|
|
||||||
result.append(f)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
@bottle.route('/call', method=['POST'])
|
|
||||||
def web_call():
|
|
||||||
global WEB_QUEUE
|
|
||||||
try:
|
|
||||||
data = bottle.request.json
|
|
||||||
|
|
||||||
# Send task definition
|
|
||||||
print('Sending data to ougoing queue...')
|
|
||||||
WEB_QUEUE.put_nowait(data)
|
|
||||||
|
|
||||||
print('Returning OK response.')
|
|
||||||
return RESPONSE_OK
|
|
||||||
except Exception as e:
|
|
||||||
print(f'{str(e)}')
|
|
||||||
return RESPONSE_OK
|
|
||||||
|
|
||||||
|
|
||||||
def web_process(mp_queue: multiprocessing.Queue):
|
|
||||||
#global WEB_QUEUE
|
|
||||||
#WEB_QUEUE = mp_queue
|
|
||||||
|
|
||||||
print(f'Run web process...')
|
|
||||||
bottle.run(host='0.0.0.0', port=8080)
|
|
||||||
|
|
||||||
def start():
|
|
||||||
global ACCESS_POINT, SERVER_PROCESS
|
|
||||||
|
|
||||||
ACCESS_POINT.start()
|
|
||||||
SERVER_PROCESS = multiprocessing.Process(target=web_process, args=(None,), name='agent_gsm_web')
|
|
||||||
SERVER_PROCESS.start()
|
|
||||||
|
|
||||||
def stop():
|
|
||||||
global ACCESS_POINT, SERVER_PROCESS
|
|
||||||
|
|
||||||
ACCESS_POINT.stop()
|
|
||||||
SERVER_PROCESS.kill()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# Start test stuff
|
|
||||||
start()
|
|
||||||
|
|
||||||
# Wait 120 seconds for tests
|
|
||||||
time.sleep(120.0)
|
|
||||||
|
|
||||||
# Stop test
|
|
||||||
stop()
|
|
||||||
|
|
@ -3,46 +3,51 @@
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
from utils_types import SignalBoundaries
|
|
||||||
from utils_sevana import speech_detector
|
|
||||||
|
|
||||||
# from pydub import silence, AudioSegment
|
from pydub import silence, AudioSegment
|
||||||
|
|
||||||
SILENCE_DELTA = 16
|
class SignalBoundaries:
|
||||||
|
# Offset from start (in seconds)
|
||||||
|
offset_start: float
|
||||||
|
|
||||||
# def find_reference_signal(input_file: pathlib.Path, output_file: pathlib.Path = None, use_end_offset: bool = True) -> SignalBoundaries:
|
# Offset from finish (in seconds)
|
||||||
# myaudio = AudioSegment.from_wav(str(input_file))
|
offset_finish: float
|
||||||
# dBFS = myaudio.dBFS
|
|
||||||
|
|
||||||
# # Find silence intervals
|
def __init__(self, offset_start = 0.0, offset_finish = 0.0) -> None:
|
||||||
# intervals = silence.detect_nonsilent(myaudio, min_silence_len=1000, silence_thresh=dBFS-SILENCE_DELTA, seek_step=50)
|
self.offset_start = offset_start
|
||||||
|
self.offset_finish = offset_finish
|
||||||
|
|
||||||
# # Translate to seconds
|
def __repr__(self) -> str:
|
||||||
# intervals = [((start/1000),(stop/1000)) for start,stop in intervals] # in sec
|
return f'[offset_start: {round(self.offset_start, 3)}, offset_finish : {round(self.offset_finish, 3)}]'
|
||||||
|
|
||||||
# # print(intervals)
|
|
||||||
|
|
||||||
# # Example of intervals: [(5.4, 6.4), (18.7, 37.05)]
|
|
||||||
# for p in intervals:
|
|
||||||
# if p[1] - p[0] > 17:
|
|
||||||
# bounds = SignalBoundaries(offset_start=p[0], offset_finish=p[1])
|
|
||||||
# if output_file is not None:
|
|
||||||
# signal = myaudio[bounds.offset_start * 1000 : bounds.offset_finish * 1000]
|
|
||||||
# signal.export(str(output_file), format='wav', parameters=['-ar', '44100', '-sample_fmt', 's16'])
|
|
||||||
|
|
||||||
# if use_end_offset:
|
|
||||||
# bounds.offset_finish = myaudio.duration_seconds - bounds.offset_finish
|
|
||||||
|
|
||||||
# return bounds
|
|
||||||
|
|
||||||
# return SignalBoundaries()
|
|
||||||
|
|
||||||
|
|
||||||
def find_reference_signal_via_speechdetector(input_file: pathlib.Path) -> SignalBoundaries:
|
def find_reference_signal(input_file: pathlib.Path, output_file: pathlib.Path = None, use_end_offset: bool = True) -> SignalBoundaries:
|
||||||
bounds = speech_detector(str(input_file))
|
myaudio = AudioSegment.from_wav(str(input_file))
|
||||||
r = SignalBoundaries(bounds[0], bounds[1])
|
dBFS = myaudio.dBFS
|
||||||
|
|
||||||
|
# Find silence intervals
|
||||||
|
intervals = silence.detect_nonsilent(myaudio, min_silence_len=1000, silence_thresh=dBFS-17, seek_step=50)
|
||||||
|
|
||||||
|
# Translate to seconds
|
||||||
|
intervals = [((start/1000),(stop/1000)) for start,stop in intervals] #in sec
|
||||||
|
|
||||||
|
# print(intervals)
|
||||||
|
|
||||||
|
# Example of intervals: [(5.4, 6.4), (18.7, 37.05)]
|
||||||
|
for p in intervals:
|
||||||
|
if p[1] - p[0] > 17:
|
||||||
|
bounds = SignalBoundaries(offset_start=p[0], offset_finish=p[1])
|
||||||
|
if output_file is not None:
|
||||||
|
signal = myaudio[bounds.offset_start * 1000 : bounds.offset_finish * 1000]
|
||||||
|
signal.export(str(output_file), format='wav', parameters=['-ar', '44100', '-sample_fmt', 's16'])
|
||||||
|
|
||||||
|
if use_end_offset:
|
||||||
|
bounds.offset_finish = myaudio.duration_seconds - bounds.offset_finish
|
||||||
|
|
||||||
return bounds
|
return bounds
|
||||||
|
|
||||||
|
return SignalBoundaries()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print(f'Please specify input filename.')
|
print(f'Please specify input filename.')
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ class InfoCache:
|
||||||
utils.log_error(str(e))
|
utils.log_error(str(e))
|
||||||
self.dir = None
|
self.dir = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def is_active(self) -> bool:
|
def is_active(self) -> bool:
|
||||||
return self.dir is not None
|
return self.dir is not None
|
||||||
|
|
||||||
|
|
@ -78,16 +80,11 @@ class InfoCache:
|
||||||
lst = os.listdir(self.dir)
|
lst = os.listdir(self.dir)
|
||||||
for n in lst:
|
for n in lst:
|
||||||
p = self.dir / n
|
p = self.dir / n
|
||||||
if self.is_valid_uuid(p.stem) and (n.endswith('.json') or n.endswith(".wav")):
|
if self.is_valid_uuid(p.stem) and n.endswith('.json'):
|
||||||
# Probe found
|
# Probe found
|
||||||
p_json = p.with_suffix('.json')
|
|
||||||
p_audio = p.with_suffix('.wav')
|
p_audio = p.with_suffix('.wav')
|
||||||
if p_json.exists() and p_audio.exists():
|
if p_audio.exists():
|
||||||
r.append((p_json, p_audio))
|
r.append((p, p.with_suffix('.wav')))
|
||||||
elif p_json.exists():
|
|
||||||
r.append((p_json, None))
|
|
||||||
elif p_audio.exists():
|
|
||||||
r.append((None, p_audio))
|
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,6 @@ TRACE_TOTAL_TIMEOUT = 30
|
||||||
# a webpage is mostly I/O bound, it's not going to be significant.
|
# a webpage is mostly I/O bound, it's not going to be significant.
|
||||||
|
|
||||||
def trace_function(frame, event, arg):
|
def trace_function(frame, event, arg):
|
||||||
global TRACE_START_TIME
|
|
||||||
if time.time() - TRACE_START_TIME > TRACE_TOTAL_TIMEOUT:
|
if time.time() - TRACE_START_TIME > TRACE_TOTAL_TIMEOUT:
|
||||||
raise Exception('Timed out!') # Use whatever exception you consider appropriate.
|
raise Exception('Timed out!') # Use whatever exception you consider appropriate.
|
||||||
|
|
||||||
|
|
@ -66,13 +65,12 @@ def trace_function(frame, event, arg):
|
||||||
class QualtestBackend:
|
class QualtestBackend:
|
||||||
address: str
|
address: str
|
||||||
instance: str
|
instance: str
|
||||||
online: bool
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.address = ""
|
self.address = ""
|
||||||
self.instance = ""
|
self.instance = ""
|
||||||
self.__phone = None
|
self.__phone = None
|
||||||
self.online = False
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def phone(self) -> Phone:
|
def phone(self) -> Phone:
|
||||||
|
|
@ -116,7 +114,6 @@ class QualtestBackend:
|
||||||
|
|
||||||
|
|
||||||
def upload_audio(self, probe_id, path_recorded: Path):
|
def upload_audio(self, probe_id, path_recorded: Path):
|
||||||
global TRACE_START_TIME
|
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
# Log about upload attempt
|
# Log about upload attempt
|
||||||
|
|
@ -191,17 +188,8 @@ class QualtestBackend:
|
||||||
response = urllib.request.urlopen(url, timeout=utils.NETWORK_TIMEOUT)
|
response = urllib.request.urlopen(url, timeout=utils.NETWORK_TIMEOUT)
|
||||||
if response.getcode() != 200:
|
if response.getcode() != 200:
|
||||||
raise RuntimeError(f'Failed to load phone definition from server. Error code: {response.getcode()}')
|
raise RuntimeError(f'Failed to load phone definition from server. Error code: {response.getcode()}')
|
||||||
|
|
||||||
# Consider backend as working one
|
|
||||||
self.online = True
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
utils.log_error(f'Problem when loading the phone definition from backend. Error: {str(e)}')
|
utils.log_error(f'Problem when loading the phone definition from backend. Error: {str(e)}')
|
||||||
|
|
||||||
# Consider backend as non-working
|
|
||||||
self.online = False
|
|
||||||
|
|
||||||
# Try to get data from the cache
|
|
||||||
r = cache.get_phone(self.instance)
|
r = cache.get_phone(self.instance)
|
||||||
if r is None:
|
if r is None:
|
||||||
raise RuntimeError(f'No cached phone definition.')
|
raise RuntimeError(f'No cached phone definition.')
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ import time
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from colorama import Fore, Style
|
||||||
|
from utils_cache import InfoCache
|
||||||
|
|
||||||
PVQA_CMD = "{pvqa} --license {pvqa_lic} --config {pvqa_cfg} --mode analysis --channel 0 " \
|
PVQA_CMD = "{pvqa} --license {pvqa_lic} --config {pvqa_cfg} --mode analysis --channel 0 " \
|
||||||
"--report {output} --input {input} --cut-begin {cut_begin} --cut-end {cut_end}"
|
"--report {output} --input {input} --cut-begin {cut_begin} --cut-end {cut_end}"
|
||||||
|
|
@ -19,8 +21,10 @@ PVQA_CMD = "{pvqa} --license {pvqa_lic} --config {pvqa_cfg} --mode analysis --ch
|
||||||
PVQA_CMD_LIC_SERVER = "{pvqa} --license-server {pvqa_lic} --config {pvqa_cfg} --mode analysis --channel 0 " \
|
PVQA_CMD_LIC_SERVER = "{pvqa} --license-server {pvqa_lic} --config {pvqa_cfg} --mode analysis --channel 0 " \
|
||||||
"--report {output} --input {input}"
|
"--report {output} --input {input}"
|
||||||
|
|
||||||
AQUA_CMD = ("{aqua} {aqua_lic} -mode files -src file \"{reference}\" -tstf \"{input}\" -config {aqua_config} "
|
AQUA_CMD = ("{aqua} {aqua_lic} -mode files -src file \"{reference}\" -tstf \"{input}\" -avlp off -smtnrm on "
|
||||||
"-specp 32 {spectrum} -fau {faults} -cut-tst {cut_begin} {cut_end} -cut-src {cut_begin_src} {cut_end_src}")
|
"-decor off -mprio off -acr auto -npnt auto -voip on -enorm rms -g711 off "
|
||||||
|
"-spfrcor on -grad off -tmc on -hist-pitch on on -hist-levels on on on -miter 1 -specp 32 {spectrum} "
|
||||||
|
"-ratem %%m -fau {faults} -output json -trim r 15 -cut-tst {cut_begin} {cut_end} -cut-src {cut_begin_src} {cut_end_src}")
|
||||||
|
|
||||||
PVQA_PATH = ""
|
PVQA_PATH = ""
|
||||||
PVQA_LIC_PATH = "pvqa.lic"
|
PVQA_LIC_PATH = "pvqa.lic"
|
||||||
|
|
@ -28,7 +32,6 @@ PVQA_CFG_PATH = "pvqa.cfg"
|
||||||
|
|
||||||
AQUA_PATH = ""
|
AQUA_PATH = ""
|
||||||
AQUA_LIC_PATH = "aqua-wb.lic"
|
AQUA_LIC_PATH = "aqua-wb.lic"
|
||||||
AQUA_CFG_PATH = "aqua.cfg"
|
|
||||||
|
|
||||||
SILER_PATH = ""
|
SILER_PATH = ""
|
||||||
|
|
||||||
|
|
@ -75,16 +78,12 @@ def load_config_and_licenses(server: str):
|
||||||
load_file(utils.join_host_and_path(server, '/deploy/pvqa.cfg'), PVQA_CFG_PATH)
|
load_file(utils.join_host_and_path(server, '/deploy/pvqa.cfg'), PVQA_CFG_PATH)
|
||||||
load_file(utils.join_host_and_path(server, '/deploy/pvqa.lic'), PVQA_LIC_PATH)
|
load_file(utils.join_host_and_path(server, '/deploy/pvqa.lic'), PVQA_LIC_PATH)
|
||||||
load_file(utils.join_host_and_path(server, '/deploy/aqua-wb.lic'), AQUA_LIC_PATH)
|
load_file(utils.join_host_and_path(server, '/deploy/aqua-wb.lic'), AQUA_LIC_PATH)
|
||||||
load_file(utils.join_host_and_path(server, '/deploy/aqua.cfg'), AQUA_CFG_PATH)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
utils.log_error(f'Failed to fetch new licenses and config. Skipping it.')
|
utils.log_error(f'Failed to fetch new licenses and config. Skipping it.')
|
||||||
|
|
||||||
|
|
||||||
def find_binaries(bin_directory: Path, license_server: str = None) -> bool:
|
def find_binaries(bin_directory: Path, license_server: str = None) -> bool:
|
||||||
# Update path to pvqa/aqua-wb
|
# Update path to pvqa/aqua-wb
|
||||||
global PVQA_CFG_PATH, PVQA_LIC_PATH, AQUA_LIC_PATH, AQUA_CFG_PATH
|
global PVQA_CFG_PATH, PVQA_LIC_PATH, AQUA_LIC_PATH, PVQA_PATH, AQUA_PATH, PVQA_CMD, AQUA_CMD, SILER_PATH, SPEECH_DETECTOR_PATH
|
||||||
global PVQA_PATH, AQUA_PATH, PVQA_CMD, AQUA_CMD
|
|
||||||
global SILER_PATH, SPEECH_DETECTOR_PATH
|
|
||||||
|
|
||||||
# Find platform prefix
|
# Find platform prefix
|
||||||
platform_prefix = platform.system().lower()
|
platform_prefix = platform.system().lower()
|
||||||
|
|
@ -95,9 +94,8 @@ def find_binaries(bin_directory: Path, license_server: str = None) -> bool:
|
||||||
PVQA_LIC_PATH = bin_directory / PVQA_LIC_PATH
|
PVQA_LIC_PATH = bin_directory / PVQA_LIC_PATH
|
||||||
PVQA_CFG_PATH = bin_directory / PVQA_CFG_PATH
|
PVQA_CFG_PATH = bin_directory / PVQA_CFG_PATH
|
||||||
AQUA_PATH = bin_directory / platform_prefix / AQUA_PATH
|
AQUA_PATH = bin_directory / platform_prefix / AQUA_PATH
|
||||||
AQUA_CFG_PATH = bin_directory / AQUA_CFG_PATH
|
|
||||||
AQUA_LIC_PATH = bin_directory / AQUA_LIC_PATH
|
AQUA_LIC_PATH = bin_directory / AQUA_LIC_PATH
|
||||||
# SILER_PATH = bin_directory / platform_prefix / SILER_PATH
|
SILER_PATH = bin_directory / platform_prefix / SILER_PATH
|
||||||
SPEECH_DETECTOR_PATH = bin_directory / platform_prefix / SPEECH_DETECTOR_PATH
|
SPEECH_DETECTOR_PATH = bin_directory / platform_prefix / SPEECH_DETECTOR_PATH
|
||||||
|
|
||||||
utils.log(f'Looking for binaries/licenses/configs at {bin_directory}...')
|
utils.log(f'Looking for binaries/licenses/configs at {bin_directory}...')
|
||||||
|
|
@ -111,23 +109,16 @@ def find_binaries(bin_directory: Path, license_server: str = None) -> bool:
|
||||||
PVQA_CFG_PATH = Path(utils.get_script_path()) / 'pvqa.cfg'
|
PVQA_CFG_PATH = Path(utils.get_script_path()) / 'pvqa.cfg'
|
||||||
|
|
||||||
if not PVQA_CFG_PATH.exists():
|
if not PVQA_CFG_PATH.exists():
|
||||||
utils.log_error(f'Failed to find PVQA config file.')
|
utils.log_error(f'Failed to find pvqa config.')
|
||||||
return False
|
|
||||||
|
|
||||||
if not AQUA_CFG_PATH.exists():
|
|
||||||
AQUA_CFG_PATH = Path(utils.get_script_path()) / 'aqua.cfg'
|
|
||||||
|
|
||||||
if not AQUA_CFG_PATH.exists():
|
|
||||||
utils.log_error(f'Failed to find AQuA config file.')
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not AQUA_PATH.exists():
|
if not AQUA_PATH.exists():
|
||||||
utils.log_error(f'Failed to find aqua-wb binary.')
|
utils.log_error(f'Failed to find aqua-wb binary.')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# if not SILER_PATH.exists():
|
if not SILER_PATH.exists():
|
||||||
# utils.log_error(f'Failed to find silence_eraser binary..')
|
utils.log_error(f'Failed to find silence_eraser binary..')
|
||||||
# return False
|
return False
|
||||||
|
|
||||||
if license_server is not None:
|
if license_server is not None:
|
||||||
AQUA_LIC_PATH = '"license://' + license_server + '"'
|
AQUA_LIC_PATH = '"license://' + license_server + '"'
|
||||||
|
|
@ -247,7 +238,7 @@ def find_aqua_mos(good_path, test_path, test_file_offset_begin: float = 0.0, tes
|
||||||
good_file_offset_begin: float = 0.0, good_file_offset_end: float = 0.0):
|
good_file_offset_begin: float = 0.0, good_file_offset_end: float = 0.0):
|
||||||
try:
|
try:
|
||||||
out_data = ""
|
out_data = ""
|
||||||
cmd = AQUA_CMD.format(aqua=AQUA_PATH, aqua_lic=AQUA_LIC_PATH, aqua_config = AQUA_CFG_PATH,
|
cmd = AQUA_CMD.format(aqua=AQUA_PATH, aqua_lic=AQUA_LIC_PATH,
|
||||||
reference=good_path, input=test_path, spectrum=AQUA_SPECTRUM,
|
reference=good_path, input=test_path, spectrum=AQUA_SPECTRUM,
|
||||||
faults=AQUA_FAULTS,
|
faults=AQUA_FAULTS,
|
||||||
cut_begin=int(test_file_offset_begin * 1000), cut_end=int(test_file_offset_end * 1000),
|
cut_begin=int(test_file_offset_begin * 1000), cut_end=int(test_file_offset_end * 1000),
|
||||||
|
|
|
||||||
|
|
@ -6,25 +6,12 @@ import utils
|
||||||
import json
|
import json
|
||||||
from crontab import CronTab
|
from crontab import CronTab
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Exit codes
|
# Exit codes
|
||||||
EXIT_OK = 0
|
EXIT_OK = 0
|
||||||
EXIT_ERROR = 1
|
EXIT_ERROR = 1
|
||||||
|
|
||||||
class SignalBoundaries:
|
|
||||||
# Offset from start (in seconds)
|
|
||||||
offset_start: float
|
|
||||||
|
|
||||||
# Offset from finish (in seconds)
|
|
||||||
offset_finish: float
|
|
||||||
|
|
||||||
def __init__(self, offset_start = 0.0, offset_finish = 0.0) -> None:
|
|
||||||
self.offset_start = offset_start
|
|
||||||
self.offset_finish = offset_finish
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'[offset_start: {round(self.offset_start, 3)}, offset_finish : {round(self.offset_finish, 3)}]'
|
|
||||||
|
|
||||||
|
|
||||||
class Phone:
|
class Phone:
|
||||||
identifier: int = 0
|
identifier: int = 0
|
||||||
name: str = ""
|
name: str = ""
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue