Skip to content
5.767348 -125.99
6.117197 -125.73
6.491126 -125.41
6.890947 -125.15
7.315032 -124.90
7.764832 -124.64
8.239069 -124.44
8.738852 -120.26
9.240211 -117.82
9.740038 -117.59
10.287374 -117.03
10.887133 -116.79
11.511741 -116.68
12.161496 -116.44
12.882118 -115.79
13.681756 -115.53
14.552726 -111.48
15.502251 -108.58
16.455202 -108.84
17.404829 -108.60
18.403366 -108.41
19.452963 -108.17
20.574747 -100.82
21.774265 -98.48
23.023481 -98.38
24.322992 -98.14
25.718433 -96.65
27.217833 -89.95
28.83674 -89.53
30.585981 -89.28
32.455627 -85.76
34.454735 -84.32
36.575161 -84.07
38.824162 -80.50
41.195343 -79.84
43.69426 -78.35
46.201054 -77.27
48.700188 -76.74
51.436867 -73.57
54.435665 -72.94
57.673481 -70.25
61.171963 -69.11
64.911255 -67.76
68.90947 -65.99
73.150322 -65.76
77.648323 -65.95
82.390686 -66.85
87.388519 -66.60
92.402107 -59.37
97.400375 -54.62
102.873734 -51.67
108.87133 -46.46
115.117416 -42.61
121.614967 -42.93
128.592163 -44.43
136.089157 -43.77
144.752487 -44.44
154.746918 -42.15
164.781372 -40.97
174.777039 -42.02
184.804214 -46.33
194.800751 -48.35
205.747467 -50.65
217.742661 -50.31
230.234833 -43.68
243.229935 -44.81
257.184326 -49.42
272.178314 -46.58
288.367401 -47.33
305.859802 -45.88
324.556274 -41.78
344.547333 -41.56
365.751617 -44.34
388.241608 -47.58
411.95343 -46.58
436.942596 -47.59
462.010529 -41.35
487.001862 -36.42
514.368652 -42.53
544.356628 -41.60
576.734802 -39.85
611.719604 -40.99
649.112549 -44.87
689.094666 -39.46
731.503235 -41.66
776.483215 -48.56
823.90686 -47.09
873.885193 -46.32
924.021057 -51.27
974.003723 -50.35
1028.737305 -50.43
1088.713257 -50.40
1151.174072 -49.41
1216.149658 -53.54
1285.921631 -51.57
1360.891602 -53.07
1447.52478 -55.84
1547.469116 -54.63
1647.813721 -54.38
1747.770386 -53.16
1848.042114 -54.48
1948.007446 -56.36
2057.474609 -57.00
2177.426514 -58.22
2302.348145 -58.88
2432.299316 -60.06
2571.843262 -60.16
2721.783203 -61.42
2883.674072 -60.98
3058.598145 -61.00
3245.562744 -58.75
3445.473389 -54.98
3657.516357 -56.58
3882.416016 -59.55
4119.53418 -59.44
4369.425781 -58.41
4620.105469 -59.69
4870.018555 -58.31
5143.686523 -55.73
5443.566406 -49.00
5767.348145 -48.41
6117.196289 -51.72
6491.125488 -53.16
6890.946777 -56.89
7315.032715 -57.64
7764.832031 -55.73
8239.068359 -56.28
8738.851563 -59.34
9240.210938 -59.02
9740.037109 -57.42
10287.373047 -59.31
10887.132813 -60.90
11511.741211 -61.69
12161.496094 -64.62
12859.216797 -67.91
13608.916016 -68.45
14475.248047 -67.52
15474.691406 -66.33
16478.136719 -68.60
17477.703125 -71.00
18480.421875 -74.78
19480.074219 -74.56
20574.746094 -75.06
21774.265625 -78.82
23023.482422 -83.67
24322.992188 -90.39
5.767348 -196.47
6.117197 -196.22
6.491126 -195.97
6.890947 -195.72
7.315032 -195.47
7.764832 -195.22
8.239069 -194.97
8.738852 -187.49
9.240211 -181.85
9.740038 -181.60
10.287374 -181.35
10.887133 -181.10
11.511741 -180.85
12.161496 -180.60
12.882118 -180.35
13.681756 -180.10
14.552726 -167.62
15.502251 -163.27
16.455202 -163.02
17.404829 -162.77
18.403366 -162.52
19.452963 -162.27
20.574747 -147.86
21.774265 -144.93
23.023481 -144.68
24.322992 -144.43
25.718433 -137.96
27.217833 -130.76
28.83674 -130.51
30.585981 -130.26
32.455627 -123.29
34.454735 -121.66
36.575161 -121.41
38.824162 -115.22
41.195343 -114.11
43.69426 -111.68
46.201054 -108.48
48.700188 -107.25
51.436867 -102.84
54.435665 -101.38
57.673481 -97.89
61.171963 -95.68
64.911255 -93.81
68.90947 -90.90
73.150322 -89.97
77.648323 -89.25
82.390686 -89.14
87.388519 -88.04
92.402107 -79.95
97.400375 -73.79
102.873734 -70.25
108.87133 -64.12
115.117416 -59.42
121.614967 -59.41
128.592163 -60.09
136.089157 -58.88
144.752487 -59.82
154.746918 -56.70
164.781372 -54.33
174.777039 -54.11
184.804214 -57.37
194.800751 -59.01
205.747467 -61.16
217.742661 -60.33
230.234833 -52.97
243.229935 -53.79
257.184326 -57.75
272.178314 -54.10
288.367401 -55.01
305.859802 -52.42
324.556274 -48.23
344.547333 -47.69
365.751617 -49.98
388.241608 -52.76
411.95343 -51.33
436.942596 -52.16
462.010529 -45.25
487.001862 -39.63
514.368652 -45.88
544.356628 -43.89
576.734802 -42.44
611.719604 -43.25
649.112549 -46.86
689.094666 -41.18
731.503235 -42.77
776.483215 -49.06
823.90686 -48.67
873.885193 -46.22
924.021057 -51.40
974.003723 -50.13
1028.737305 -50.31
1088.713257 -50.08
1151.174072 -48.89
1216.149658 -53.42
1285.921631 -50.80
1360.891602 -51.95
1447.52478 -56.92
1547.469116 -54.46
1647.813721 -53.62
1747.770386 -52.14
1848.042114 -53.20
1948.007446 -54.23
2057.474609 -55.65
2177.426514 -56.92
2302.348145 -57.33
2432.299316 -58.88
2571.843262 -58.79
2721.783203 -60.23
2883.674072 -59.73
3058.598145 -59.88
3245.562744 -57.54
3445.473389 -53.98
3657.516357 -55.51
3882.416016 -58.74
4119.53418 -58.37
4369.425781 -57.79
4620.105469 -58.75
4870.018555 -57.35
5143.686523 -55.28
5443.566406 -48.20
5767.348145 -48.77
6117.196289 -51.77
6491.125488 -53.42
6890.946777 -57.49
7315.032715 -58.48
7764.832031 -56.73
8239.068359 -57.64
8738.851563 -60.41
9240.210938 -61.22
9740.037109 -59.21
10287.373047 -61.98
10887.132813 -63.98
11511.741211 -65.17
12161.496094 -68.86
12859.216797 -72.60
13608.916016 -73.37
14475.248047 -73.99
15474.691406 -73.00
16478.136719 -75.09
17477.703125 -78.55
18480.421875 -81.54
19480.074219 -82.95
20574.746094 -84.26
21774.265625 -88.03
23023.482422 -92.79
24322.992188 -100.22
6.3 -119.67
8 -117.73
10 -111.06
12.5 -110.06
16 -103.36
20 -95.94
25 -87.83
31.5 -80.71
40 -74.52
50 -68.42
63 -62.10
80 -60.36
100 -44.54
125 -37.24
160 -36.47
200 -41.94
250 -39.49
315 -37.51
400 -40.42
500 -33.58
630 -35.02
800 -38.86
1000 -44.41
1250 -45.46
1600 -48.93
2000 -49.92
2500 -53.94
3150 -52.22
4000 -52.38
5000 -47.06
6300 -45.81
8000 -51.05
10000 -52.82
12500 -58.74
16000 -62.36
20000 -68.88
25000 -82.77
6.3 -190.07
8 -185.71
10 -175.44
12.5 -174.44
16 -157.77
20 -143.04
25 -129.71
31.5 -118.75
40 -108.42
50 -98.02
63 -87.80
80 -83.02
100 -62.73
125 -53.41
160 -49.68
200 -53.20
250 -48.30
315 -43.88
400 -45.41
500 -36.87
630 -36.97
800 -39.88
1000 -44.43
1250 -44.92
1600 -47.94
2000 -48.76
2500 -52.67
3150 -51.05
4000 -51.39
5000 -46.72
6300 -45.80
8000 -52.10
10000 -55.24
12500 -62.72
16000 -68.70
20000 -77.59
25000 -92.07
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -5,24 +5,33 @@ Created on Jan 16 2025 21:31 ...@@ -5,24 +5,33 @@ Created on Jan 16 2025 21:31
@author: Jan.Reimes @author: Jan.Reimes
""" """
import numpy as np
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any, Self, Optional
import shutil import shutil
from enum import Enum from enum import Enum
import urllib.request import urllib.request
class Downloads(Enum): class Downloads(Enum):
P501 = r'https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-P.501-202005-I!!SOFT-ZST-E&type=items' P501_B = (P501_ANNEX_B := r'https://www.itu.int/wftp3/public/t/testsignal/GenAudio/P501/v2025_04/Speech_Signals_AnnexB.zip')
P501_C = (P501_ANNEX_C :=r'https://www.itu.int/wftp3/public/t/testsignal/GenAudio/P501/v2025_04/Speech_Signals_AnnexC.zip')
P501_D = (P501_ANNEX_D :=r'https://www.itu.int/wftp3/public/t/testsignal/GenAudio/P501/v2025_04/Speech_Signals_AnnexD.zip')
P501_E = (P501_ANNEX_E :=r'https://www.itu.int/wftp3/public/t/testsignal/GenAudio/P501/v2025_04/Speech_Signals_AnnexE.zip')
P501_7 = (P501_CL_7 :=r'https://www.itu.int/wftp3/public/t/testsignal/GenAudio/P501/v2025_04/Test_Signals_Clause_7.zip')
P501 = [P501_ANNEX_B, P501_ANNEX_C, P501_ANNEX_D, P501_ANNEX_E, P501_CL_7]
G191 = r'https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-G.191-202405-I!!SOFT-ZST-E&type=items' G191 = r'https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-G.191-202405-I!!SOFT-ZST-E&type=items'
evs = 'https://www.3gpp.org/ftp/Specs/archive/26_series/26.442/26442-i00.zip' EVS = r'https://www.3gpp.org/ftp/Specs/archive/26_series/26.442/26442-i00.zip'
amr_wb = 'https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/EVS_Permanent_Documents/EVS-7a_S4-130155.zip' AMR_WB = r'https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/EVS_Permanent_Documents/EVS-7a_S4-130155.zip'
amr = 'https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/EVS_Permanent_Documents/EVS-7a_S4-130155.zip' AMR = r'https://www.3gpp.org/ftp/tsg_sa/WG4_CODEC/EVS_Permanent_Documents/EVS-7a_S4-130155.zip'
@classmethod @classmethod
def get(cls, x: Any) -> 'Downloads': def get(cls, x: Any) -> Self:
if isinstance(x, cls):
return x
try: try:
return cls[str(x).lower()] return cls[str(x).upper()]
except: # noqa: E722 except: # noqa: E722
return cls(x) return cls(x)
...@@ -33,7 +42,7 @@ def download(url: str, dl_path: Path) -> Path: ...@@ -33,7 +42,7 @@ def download(url: str, dl_path: Path) -> Path:
return Path(x[0]) return Path(x[0])
class FileFetcher: class FileFetcher:
def __init__(self, doc_download: Downloads, dl_dir: Path, extract_dir: Path = None): def __init__(self, doc_download: Downloads, dl_dir: Path, extract_dir: Optional[Path] = None):
self.doc_download = Downloads.get(doc_download) self.doc_download = Downloads.get(doc_download)
self.dl_dir = Path(dl_dir).absolute() self.dl_dir = Path(dl_dir).absolute()
...@@ -41,12 +50,12 @@ class FileFetcher: ...@@ -41,12 +50,12 @@ class FileFetcher:
self.extract_dir = Path(dl_dir if extract_dir is None else extract_dir).absolute() / self.doc_download.name self.extract_dir = Path(dl_dir if extract_dir is None else extract_dir).absolute() / self.doc_download.name
def get_file(self, name: str) -> Path: def get_file(self, name: str) -> Path|None:
self.dl_dir.mkdir(parents=True, exist_ok=True) self.dl_dir.mkdir(parents=True, exist_ok=True)
self.extract_dir.mkdir(parents=True, exist_ok=True) self.extract_dir.mkdir(parents=True, exist_ok=True)
def _search_file(name: str, raise_if_not_found: bool = True) -> Path: def _search_file(name: str, raise_if_not_found: bool = True) -> Path|None:
candidates = list(self.extract_dir.rglob(name + '*')) candidates = list(self.extract_dir.rglob(name + '*'))
if len(candidates) == 0: if len(candidates) == 0:
if raise_if_not_found: if raise_if_not_found:
...@@ -61,15 +70,23 @@ class FileFetcher: ...@@ -61,15 +70,23 @@ class FileFetcher:
self.zip_dl_file.unlink(missing_ok=True) self.zip_dl_file.unlink(missing_ok=True)
shutil.rmtree(self.extract_dir, ignore_errors=True) shutil.rmtree(self.extract_dir, ignore_errors=True)
download((url := self.doc_download.value), self.zip_dl_file) # download value might contain multiple URLs
urls = np.atleast_1d(self.doc_download.value)
for url in urls:
if not isinstance(url, str):
raise ValueError(f"Invalid URL: {url}")
download(url, self.zip_dl_file)
shutil.unpack_archive(self.zip_dl_file, self.extract_dir) shutil.unpack_archive(self.zip_dl_file, self.extract_dir)
# extract all zip files (inside the downloaded zip) recursively # Search for zip files in the extract directory and unpack them.
# This is useful if the archive contains nested zip files.
while len(zip_files := list(self.extract_dir.rglob('*.zip'))) > 0: while len(zip_files := list(self.extract_dir.rglob('*.zip'))) > 0:
for zip_file in zip_files: for zip_file in zip_files:
shutil.unpack_archive(zip_file, zip_file.parent) shutil.unpack_archive(zip_file, zip_file.parent)
zip_file.unlink(missing_ok=True) zip_file.unlink(missing_ok=True)
# final search for the file
return _search_file(name) return _search_file(name)
else: else:
return file return file
......
This diff is collapsed.
...@@ -12,7 +12,7 @@ from typing import Optional, Union, Iterable ...@@ -12,7 +12,7 @@ from typing import Optional, Union, Iterable
from collections import namedtuple from collections import namedtuple
from ensure import check from ensure import check
from nonlinearity.spectrum import calculate_spectrum_vs_time from nonlinearity.spectrum import FftSpectrumAvg
THDResult = namedtuple('THDResult', THDResult = namedtuple('THDResult',
['thdn', 'thd', 'freqs_harmonics', 'levels_harmonics', 'level_harmonics_total', ['thdn', 'thd', 'freqs_harmonics', 'levels_harmonics', 'level_harmonics_total',
...@@ -25,13 +25,16 @@ def thd_plus_n(signal: NDArray, fs: int, f0: float, harmonics: Optional[Union[in ...@@ -25,13 +25,16 @@ def thd_plus_n(signal: NDArray, fs: int, f0: float, harmonics: Optional[Union[in
fmin: float = 20.0, fmax: float = 20_000.0, delta_f_factor: Optional[float] = None, fmin: float = 20.0, fmax: float = 20_000.0, delta_f_factor: Optional[float] = None,
always_2d: bool = False, always_2d: bool = False,
) -> THDResult: ) -> THDResult:
n_channels = 1
if len(signal.shape) == 1: if len(signal.shape) == 1:
signal = signal[:, np.newaxis] signal = signal[:, np.newaxis]
single_channel = True single_channel = True
elif len(signal.shape) == 2:
single_channel = False
n_channels = signal.shape[1]
elif len(signal.shape) > 2: elif len(signal.shape) > 2:
raise ValueError(f"Invalid signal shape: {signal.shape}") raise ValueError(f"Invalid signal shape: {signal.shape}")
else:
single_channel = False
# parse harmonics # parse harmonics
all_harmonics = np.arange(2 * f0, fmax, step=f0) all_harmonics = np.arange(2 * f0, fmax, step=f0)
...@@ -53,8 +56,9 @@ def thd_plus_n(signal: NDArray, fs: int, f0: float, harmonics: Optional[Union[in ...@@ -53,8 +56,9 @@ def thd_plus_n(signal: NDArray, fs: int, f0: float, harmonics: Optional[Union[in
raise ValueError(f"Parameter harmonics must be either int or List[int], but got {type(harmonics)}") raise ValueError(f"Parameter harmonics must be either int or List[int], but got {type(harmonics)}")
# average FFT spectrum (full frequency range) in power domain # average FFT spectrum (full frequency range) in power domain
freq, time, spec, wcf = calculate_spectrum_vs_time(signal, fs, window, nperseg, noverlap, nfft, fmin=0, fmax=fs / 2) spectrum = FftSpectrumAvg.from_signals(signal, fs, always_2d=True, window=window, nperseg=nperseg, noverlap=noverlap, nfft=nfft, fmin=20.0, fmax=fs / 2)
spec_avg = spec.mean(axis=1) * np.power(10, wcf / 10) spec_avg = spectrum.get_values('pow', include_window_correction=True)
freq = spectrum.get_freqs()
# find indices for fmin, fmax, f0 +/- margin # find indices for fmin, fmax, f0 +/- margin
idx_fmin = np.argmin(np.abs(freq - fmin)) idx_fmin = np.argmin(np.abs(freq - fmin))
...@@ -80,16 +84,16 @@ def thd_plus_n(signal: NDArray, fs: int, f0: float, harmonics: Optional[Union[in ...@@ -80,16 +84,16 @@ def thd_plus_n(signal: NDArray, fs: int, f0: float, harmonics: Optional[Union[in
# energy of fundamental frequency # energy of fundamental frequency
s = slice(idx0_lower, idx0_upper + 1) s = slice(idx0_lower, idx0_upper + 1)
level_fund = spec_avg[s, :].sum(axis=0) level_fund = spec_avg[s].sum(axis=0)
### Calculation of THD+N ### ### Calculation of THD+N ###
# levels below and above f0 # levels below and above f0
s = slice(idx_fmin, idx0_lower) s = slice(idx_fmin, idx0_lower)
level_below_f0 = spec_avg[s, :].sum(axis=0) level_below_f0 = spec_avg[s].sum(axis=0)
s = slice(idx0_upper + 1, idx_fmax + 1) s = slice(idx0_upper + 1, idx_fmax + 1)
level_above_f0 = spec_avg[s, :].sum(axis=0) level_above_f0 = spec_avg[s].sum(axis=0)
thdn = 10 * np.log10(level_fund) - 10 * np.log10(level_below_f0 + level_above_f0) thdn = 10 * np.log10(level_fund) - 10 * np.log10(level_below_f0 + level_above_f0)
...@@ -101,16 +105,16 @@ def thd_plus_n(signal: NDArray, fs: int, f0: float, harmonics: Optional[Union[in ...@@ -101,16 +105,16 @@ def thd_plus_n(signal: NDArray, fs: int, f0: float, harmonics: Optional[Union[in
# find harmonics, calculate levels # find harmonics, calculate levels
harmonics_idx = [np.argmin(np.abs(freq - fh)) for fh in harmonics] harmonics_idx = [np.argmin(np.abs(freq - fh)) for fh in harmonics]
levels_harmonics = np.zeros_like(harmonics_idx, dtype=spec_avg.dtype) levels_harmonics = np.zeros((len(harmonics), n_channels), dtype=spec_avg.dtype)
for j, h_ii in enumerate(harmonics_idx): for j, h_ii in enumerate(harmonics_idx):
s = slice(max(idx_fmin, h_ii - nbr_margin_lower), min(idx_fmax, h_ii + nbr_margin_upper) + 1) s = slice(max(idx_fmin, h_ii - nbr_margin_lower), min(idx_fmax, h_ii + nbr_margin_upper) + 1)
levels_harmonics[j] = spec_avg[s, :].sum(axis=0) levels_harmonics[j, :] = spec_avg[s].sum(axis=0)
level_harmonics_total = levels_harmonics.sum() level_harmonics_total = levels_harmonics.sum(axis=0)
thd = 10 * np.log10(level_fund) - 10 * np.log10(level_harmonics_total) thd = 10 * np.log10(level_fund) - 10 * np.log10(level_harmonics_total)
# additional overall level for information # additional overall level for information
level_overall = spec_avg[idx_fmin:idx_fmax + 1, :].sum(axis=0) level_overall = spec_avg[idx_fmin:idx_fmax + 1].sum(axis=0)
# result dict (w/o harmonics) # result dict (w/o harmonics)
result = dict(thdn=thdn, thd=thd, level_harmonics_total=level_harmonics_total, result = dict(thdn=thdn, thd=thd, level_harmonics_total=level_harmonics_total,
......
This diff is collapsed.