add PBP qualification tests and rename QoS profiles

- Rename AuracastQosHigh/Mid/Low to AuracastQosDefault/Fast for clarity
- Add Public Broadcast Profile (PBP) service data to advertising with dynamic feature calculation based on encryption and sample rate
- Add PBP/PBS/STR test cases (BV-01-C through BV-06-C) for standard and high quality streaming with various configurations
- Update all existing test cases and main scripts to use new QoS profile names
This commit is contained in:
Paul Obernesser
2025-12-08 16:18:40 +01:00
parent 46904797a4
commit ca461c47da
15 changed files with 362 additions and 27 deletions
+6 -11
View File
@@ -7,24 +7,19 @@ class AuracastQoSConfig(BaseModel):
number_of_retransmissions: int
max_transport_latency_ms: int
class AuracastQosHigh(AuracastQoSConfig):
class AuracastQosDefault(AuracastQoSConfig):
iso_int_multiple_10ms: int = 1
number_of_retransmissions:int = 4 #4
max_transport_latency_ms:int = 43 #varies from the default value in bumble (was 65)
class AuracastQosMid(AuracastQoSConfig):
iso_int_multiple_10ms: int = 2
number_of_retransmissions:int = 3
max_transport_latency_ms:int = 65
class AuracastQosLow(AuracastQoSConfig):
iso_int_multiple_10ms: int = 3
number_of_retransmissions:int = 2 #4
max_transport_latency_ms:int = 65 #varies from the default value in bumble (was 65)
class AuracastQosFast(AuracastQoSConfig):
iso_int_multiple_10ms: int = 1
number_of_retransmissions:int = 2
max_transport_latency_ms:int = 22
class AuracastGlobalConfig(BaseModel):
qos_config: AuracastQoSConfig = AuracastQosHigh()
qos_config: AuracastQoSConfig = AuracastQosDefault()
debug: bool = False
device_name: str = 'Auracaster'
transport: str = ''
+28 -6
View File
@@ -346,6 +346,32 @@ async def init_broadcast(
)
)
bigs[f'big{i}']['broadcast_audio_announcement'] = bap.BroadcastAudioAnnouncement(conf.id)
# Build advertising data types list
advertising_data_types = [
(core.AdvertisingData.BROADCAST_NAME, conf.name.encode()),
]
# [PBP] Add Public Broadcast Profile Service Data (UUID 0x1856)
# Required for PTS Qualification (PBP/PBS/STR)
# Dynamically calculate PBP features based on stream configuration
pbp_features = 0x00
# Bit 0: Encryption (set if broadcast_code is configured)
if conf.code is not None:
pbp_features |= 0x01
# Bit 1 vs Bit 2: Quality based on sample rate
if global_config.auracast_sampling_rate_hz in [16000, 24000]:
pbp_features |= 0x02 # Standard Quality
elif global_config.auracast_sampling_rate_hz == 48000:
pbp_features |= 0x04 # High Quality
pbp_service_data = struct.pack('<H', 0x1856) + bytes([pbp_features, 0x00])
advertising_data_types.append(
(core.AdvertisingData.SERVICE_DATA_16_BIT_UUID, pbp_service_data)
)
advertising_set = await device.create_advertising_set(
random_address=hci.Address(conf.random_address),
advertising_parameters=bumble.device.AdvertisingParameters(
@@ -362,11 +388,7 @@ async def init_broadcast(
),
advertising_data=(
bigs[f'big{i}']['broadcast_audio_announcement'].get_advertising_data()
+ bytes(
core.AdvertisingData(
[(core.AdvertisingData.BROADCAST_NAME, conf.name.encode())]
)
)
+ bytes(core.AdvertisingData(advertising_data_types))
+ advertising_manufacturer_data
),
periodic_advertising_parameters=bumble.device.PeriodicAdvertisingParameters(
@@ -890,7 +912,7 @@ if __name__ == "__main__":
)
# TODO: How can we use other iso interval than 10ms ?(medium or low rel) ? - nrf53audio receiver repports I2S tx underrun
config.qos_config=auracast_config.AuracastQosHigh()
config.qos_config=auracast_config.AuracastQosDefault()
#config.transport='serial:/dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_81BD14B8D71B5662-if00,1000000,rtscts' # transport for nrf52 dongle
#config.transport='serial:/dev/serial/by-id/usb-SEGGER_J-Link_001050076061-if02,1000000,rtscts' # transport for nrf53dk
+1 -1
View File
@@ -140,7 +140,7 @@ async def main():
os.chdir(os.path.dirname(__file__))
global_conf = auracast_config.AuracastGlobalConfig(
qos_config=auracast_config.AuracastQosHigh()
qos_config=auracast_config.AuracastQosDefault()
)
#global_conf.transport='serial:/dev/serial/by-id/usb-SEGGER_J-Link_001057705357-if02,1000000,rtscts' # transport for nrf54l15dk
global_conf.transport='serial:/dev/serial/by-id/usb-ZEPHYR_Zephyr_HCI_UART_sample_81BD14B8D71B5662-if00,115200,rtscts' #nrf52dongle hci_uart usb cdc
+1 -1
View File
@@ -159,7 +159,7 @@ if __name__ == "__main__":
],
immediate_rendering=False,
presentation_delay_us=40000,
qos_config=auracast_config.AuracastQosHigh(),
qos_config=auracast_config.AuracastQosDefault(),
auracast_sampling_rate_hz = LC3_SRATE,
octets_per_frame = OCTETS_PER_FRAME,
transport=TRANSPORT1,
+2 -2
View File
@@ -5,7 +5,7 @@ For BV36-C and BV 37-C to success just restart the stream while the testcase is
import logging
import os
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosHigh
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosDefault
from auracast.multicast import broadcast, run_async
@@ -22,7 +22,7 @@ if __name__ == "__main__":
config = AuracastGlobalConfig()
# Use same QoS profile as multicast main
config.qos_config = AuracastQosHigh()
config.qos_config = AuracastQosDefault()
# Transport similar to multicast main; adjust if needed for your setup
# config.transport = "auto" # let multicast auto-detect
+3 -2
View File
@@ -1,10 +1,11 @@
"""
"""
import logging
import os
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosHigh
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosDefault
from auracast.multicast import broadcast, run_async
@@ -21,7 +22,7 @@ if __name__ == "__main__":
config = AuracastGlobalConfig()
# Use same QoS profile as multicast main
config.qos_config = AuracastQosHigh()
config.qos_config = AuracastQosDefault()
# Transport similar to multicast main; adjust if needed for your setup
# config.transport = "auto" # let multicast auto-detect
+2 -2
View File
@@ -6,7 +6,7 @@ restart the stream when asked to terminate.
import logging
import os
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosHigh
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosDefault
from auracast.multicast import broadcast, run_async
@@ -20,7 +20,7 @@ if __name__ == "__main__":
config = AuracastGlobalConfig()
# Use same QoS profile as multicast main
config.qos_config = AuracastQosHigh()
config.qos_config = AuracastQosDefault()
# Transport similar to multicast main; adjust if needed for your setup
# config.transport = "auto" # let multicast auto-detect
+2 -2
View File
@@ -4,7 +4,7 @@
import logging
import os
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosHigh
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosDefault
from auracast.multicast import broadcast, run_async
@@ -21,7 +21,7 @@ if __name__ == "__main__":
config = AuracastGlobalConfig()
# Use same QoS profile as multicast main
config.qos_config = AuracastQosHigh()
config.qos_config = AuracastQosDefault()
# Transport similar to multicast main; adjust if needed for your setup
# config.transport = "auto" # let multicast auto-detect
@@ -0,0 +1,45 @@
"""
PBP/PBS/STR/BV-01-C: Standard Quality Streaming Support, 16_2_1 - PBS
(TSPC_PBP_1_1 AND TSPC_PBP_7_1) OR TSPC_ALL
Configuration: 16kHz, unencrypted, stereo (2 BISes)
PBP Features: 0x02 (Standard Quality)
"""
import logging
import os
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosFast
from auracast.multicast import broadcast, run_async
if __name__ == "__main__":
logging.basicConfig(
level=os.environ.get("LOG_LEVEL", logging.INFO),
format="%(module)s.py:%(lineno)d %(levelname)s: %(message)s",
)
os.chdir(os.path.join(os.path.dirname(__file__), "../../../../auracast"))
config = AuracastGlobalConfig()
config.qos_config = AuracastQosFast()
config.transport = "serial:/dev/ttyAMA3,1000000,rtscts"
# 16_2_1: 16kHz, stereo
config.auracast_sampling_rate_hz = 16000
config.octets_per_frame = 40
big = AuracastBigConfig()
big.random_address = "F1:F1:F2:F3:F4:F5"
big.audio_source = "file:./testdata/announcement_en_stereo.wav"
big.id = 12
big.num_bis = 2 # stereo
big.name = "Broadcast"
# Unencrypted (no code)
run_async(
broadcast(
config,
[big],
)
)
@@ -0,0 +1,45 @@
"""
PBP/PBS/STR/BV-02-C: High Quality Streaming Support - PBS
(TSPC_PBP_1_1 AND TSPC_PBP_6_5) OR TSPC_ALL
Configuration: 48kHz, unencrypted, stereo (2 BISes)
PBP Features: 0x04 (High Quality)
"""
import logging
import os
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosFast
from auracast.multicast import broadcast, run_async
if __name__ == "__main__":
logging.basicConfig(
level=os.environ.get("LOG_LEVEL", logging.INFO),
format="%(module)s.py:%(lineno)d %(levelname)s: %(message)s",
)
os.chdir(os.path.join(os.path.dirname(__file__), "../../../../auracast"))
config = AuracastGlobalConfig()
config.qos_config = AuracastQosFast()
config.transport = "serial:/dev/ttyAMA3,1000000,rtscts"
# High Quality: 48kHz
config.auracast_sampling_rate_hz = 48000
config.octets_per_frame = 100 # 48kHz typical
big = AuracastBigConfig()
big.random_address = "F1:F1:F2:F3:F4:F5"
big.audio_source = "file:./testdata/announcement_en_stereo.wav"
big.id = 12
big.num_bis = 2 # stereo
big.name = "Broadcast"
# Unencrypted (no code)
run_async(
broadcast(
config,
[big],
)
)
@@ -0,0 +1,45 @@
"""
PBP/PBS/STR/BV-03-C: Encrypted Streaming Support, Standard Quality - PBS
(TSPC_PBP_1_1 AND TSPC_PBP_6_6 AND TSPC_PBP_6_4) OR TSPC_ALL
Configuration: 16kHz, encrypted, stereo (2 BISes)
PBP Features: 0x03 (Standard Quality + Encrypted)
"""
import logging
import os
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosFast
from auracast.multicast import broadcast, run_async
if __name__ == "__main__":
logging.basicConfig(
level=os.environ.get("LOG_LEVEL", logging.INFO),
format="%(module)s.py:%(lineno)d %(levelname)s: %(message)s",
)
os.chdir(os.path.join(os.path.dirname(__file__), "../../../../auracast"))
config = AuracastGlobalConfig()
config.qos_config = AuracastQosFast()
config.transport = "serial:/dev/ttyAMA3,1000000,rtscts"
# Standard Quality: 16kHz
config.auracast_sampling_rate_hz = 16000
config.octets_per_frame = 40
big = AuracastBigConfig()
big.random_address = "F1:F1:F2:F3:F4:F5"
big.audio_source = "file:./testdata/announcement_en_stereo.wav"
big.id = 12
big.num_bis = 2 # stereo
big.name = "Broadcast"
big.code = "PBP_TEST_CODE" # Encrypted
run_async(
broadcast(
config,
[big],
)
)
@@ -0,0 +1,45 @@
"""
PBP/PBS/STR/BV-04-C: Encrypted Streaming Support, High Quality - PBS
(TSPC_PBP_1_1 AND TSPC_PBP_6_6 AND TSPC_PBP_6_5) OR TSPC_ALL
Configuration: 48kHz, encrypted, stereo (2 BISes)
PBP Features: 0x05 (High Quality + Encrypted)
"""
import logging
import os
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosFast
from auracast.multicast import broadcast, run_async
if __name__ == "__main__":
logging.basicConfig(
level=os.environ.get("LOG_LEVEL", logging.INFO),
format="%(module)s.py:%(lineno)d %(levelname)s: %(message)s",
)
os.chdir(os.path.join(os.path.dirname(__file__), "../../../../auracast"))
config = AuracastGlobalConfig()
config.qos_config = AuracastQosFast()
config.transport = "serial:/dev/ttyAMA3,1000000,rtscts"
# High Quality: 48kHz
config.auracast_sampling_rate_hz = 48000
config.octets_per_frame = 100
big = AuracastBigConfig()
big.random_address = "F1:F1:F2:F3:F4:F5"
big.audio_source = "file:./testdata/announcement_en_stereo.wav"
big.id = 12
big.num_bis = 2 # stereo
big.name = "Broadcast"
big.code = "PBP_TEST_CODE" # Encrypted
run_async(
broadcast(
config,
[big],
)
)
@@ -0,0 +1,46 @@
"""
PBP/PBS/STR/BV-05-C: Standard Quality Streaming Support, 16_2_2 - PBS
(TSPC_PBP_1_1 AND TSPC_PBP_7_3) OR TSPC_ALL
Configuration: 16kHz, unencrypted, stereo (2 BISes), QoS 16_2_2
PBP Features: 0x02 (Standard Quality)
"""
import logging
import os
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosDefault
from auracast.multicast import broadcast, run_async
if __name__ == "__main__":
logging.basicConfig(
level=os.environ.get("LOG_LEVEL", logging.INFO),
format="%(module)s.py:%(lineno)d %(levelname)s: %(message)s",
)
os.chdir(os.path.join(os.path.dirname(__file__), "../../../../auracast"))
config = AuracastGlobalConfig()
# 16_2_2 uses different QoS (RTN=2, higher latency)
config.qos_config = AuracastQosDefault()
config.transport = "serial:/dev/ttyAMA3,1000000,rtscts"
# 16_2_2: 16kHz
config.auracast_sampling_rate_hz = 16000
config.octets_per_frame = 40
big = AuracastBigConfig()
big.random_address = "F1:F1:F2:F3:F4:F5"
big.audio_source = "file:./testdata/announcement_en_stereo.wav"
big.id = 12
big.num_bis = 2 # stereo
big.name = "Broadcast"
# Unencrypted (no code)
run_async(
broadcast(
config,
[big],
)
)
@@ -0,0 +1,45 @@
"""
PBP/PBS/STR/BV-06-C: Standard Quality Streaming Support, 24_2_1 - PBS
(TSPC_PBP_1_1 AND TSPC_PBP_7_2) OR TSPC_ALL
Configuration: 24kHz, unencrypted, stereo (2 BISes)
PBP Features: 0x02 (Standard Quality)
"""
import logging
import os
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosFast
from auracast.multicast import broadcast, run_async
if __name__ == "__main__":
logging.basicConfig(
level=os.environ.get("LOG_LEVEL", logging.INFO),
format="%(module)s.py:%(lineno)d %(levelname)s: %(message)s",
)
os.chdir(os.path.join(os.path.dirname(__file__), "../../../../auracast"))
config = AuracastGlobalConfig()
config.qos_config = AuracastQosFast()
config.transport = "serial:/dev/ttyAMA3,1000000,rtscts"
# 24_2_1: 24kHz
config.auracast_sampling_rate_hz = 24000
config.octets_per_frame = 60
big = AuracastBigConfig()
big.random_address = "F1:F1:F2:F3:F4:F5"
big.audio_source = "file:./testdata/announcement_en_stereo.wav"
big.id = 12
big.num_bis = 2 # stereo
big.name = "Broadcast"
# Unencrypted (no code)
run_async(
broadcast(
config,
[big],
)
)
@@ -0,0 +1,46 @@
"""
PBP/PBS/STR/BV-07-C: Standard Quality Streaming Support, 24_2_2 - PBS
(TSPC_PBP_1_1 AND TSPC_PBP_7_4) OR TSPC_ALL
Configuration: 24kHz, unencrypted, stereo (2 BISes), QoS 24_2_2
PBP Features: 0x02 (Standard Quality)
"""
import logging
import os
from auracast.auracast_config import AuracastGlobalConfig, AuracastBigConfig, AuracastQosDefault
from auracast.multicast import broadcast, run_async
if __name__ == "__main__":
logging.basicConfig(
level=os.environ.get("LOG_LEVEL", logging.INFO),
format="%(module)s.py:%(lineno)d %(levelname)s: %(message)s",
)
os.chdir(os.path.join(os.path.dirname(__file__), "../../../../auracast"))
config = AuracastGlobalConfig()
# 24_2_2 uses different QoS (RTN=2, higher latency)
config.qos_config = AuracastQosDefault()
config.transport = "serial:/dev/ttyAMA3,1000000,rtscts"
# 24_2_2: 24kHz
config.auracast_sampling_rate_hz = 24000
config.octets_per_frame = 60
big = AuracastBigConfig()
big.random_address = "F1:F1:F2:F3:F4:F5"
big.audio_source = "file:./testdata/announcement_en_stereo.wav"
big.id = 12
big.num_bis = 2 # stereo
big.name = "Broadcast"
# Unencrypted (no code)
run_async(
broadcast(
config,
[big],
)
)