更新UCD-API库及文档
This commit is contained in:
@@ -30,6 +30,9 @@ class OpfHandlerInternal(OpfHandlerBase):
|
||||
19: OPFFunctions.opf_19_handler,
|
||||
20: OPFFunctions.opf_20_handler,
|
||||
21: OPFFunctions.opf_21_handler,
|
||||
22: OPFFunctions.opf_22_handler,
|
||||
23: OPFFunctions.opf_23_handler,
|
||||
24: OPFFunctions.opf_24_handler,
|
||||
101: OPFFunctions.opf_101_handler,
|
||||
102: OPFFunctions.opf_102_handler,
|
||||
103: OPFFunctions.opf_103_handler,
|
||||
@@ -37,6 +40,7 @@ class OpfHandlerInternal(OpfHandlerBase):
|
||||
105: OPFFunctions.opf_105_handler,
|
||||
106: OPFFunctions.opf_106_handler,
|
||||
107: OPFFunctions.opf_107_handler,
|
||||
108: OPFFunctions.opf_108_handler,
|
||||
120: OPFFunctions.opf_120_handler,
|
||||
121: OPFFunctions.opf_121_handler,
|
||||
122: OPFFunctions.opf_122_handler,
|
||||
@@ -47,6 +51,8 @@ class OpfHandlerInternal(OpfHandlerBase):
|
||||
143: OPFFunctions.opf_143_handler,
|
||||
144: OPFFunctions.opf_144_handler,
|
||||
145: OPFFunctions.opf_145_handler,
|
||||
146: OPFFunctions.opf_146_handler,
|
||||
147: OPFFunctions.opf_147_handler,
|
||||
150: OPFFunctions.opf_150_handler,
|
||||
161: OPFFunctions.opf_161_handler
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import copy
|
||||
import math
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
@@ -17,22 +18,19 @@ from UniTAP.utils.function_wrapper import function_scheduler
|
||||
from UniTAP.libs.lib_dscl.dscl import DSC_TOOLS_FOLDER, DSCL_ExtractPPSFromData, DSCL_Encode, DSCL_Decode
|
||||
from UniTAP.libs.lib_dscl.dscl_utils import calculate_slice_size, dscl_image_to_dsc_vf
|
||||
from UniTAP.libs.lib_pdl.pdl import generate_pattern_as_vf, PatternType
|
||||
from UniTAP.libs.lib_tsi.tsi_types import TSI_MEMORY_WRITE_W, TSI_MEMORY_BLOCK_INDEX, TSI_MEMORY_LAYOUT, \
|
||||
TSI_DP20_SINKCTS_SUPPORT_444CRC, TSI_DP14_SINKCTS_SUPPORT_444CRC
|
||||
from UniTAP.libs.lib_tsi.tsi_private_types import TSI_DSC_MEMORY_BLOCK, TSI_DSC_DATA_SIZE, \
|
||||
TSI_DSC_TX_CRC, TSI_DPRX_DSC_TEST_CRC, TSI_SELECT_SUITE
|
||||
from UniTAP.libs.lib_tsi.tsi import TSIX_TS_SetConfigItem, TSIX_TS_GetConfigItem
|
||||
import UniTAP.libs.lib_tsi.tsi as tsi
|
||||
from ctypes import c_uint64, c_uint32
|
||||
from UniTAP.libs.lib_tsi import TSI_OPF_RETURN_CODE_ABORT, TSI_OPF_RETURN_CODE_PASS, \
|
||||
TSI_OPF_RETURN_CODE_PROCEED, TSI_OPF_RETURN_CODE_FAIL, TSI_OPF_RETURN_CODE_AUTO_CLOSED
|
||||
|
||||
import UniTAP.libs.lib_tsi.tsi_private_types as p_ci
|
||||
import UniTAP.libs.lib_tsi.tsi_types as ci
|
||||
|
||||
|
||||
class OPFDialogAnswer(IntEnum):
|
||||
ABORT = TSI_OPF_RETURN_CODE_ABORT
|
||||
PASS = TSI_OPF_RETURN_CODE_PASS
|
||||
PROCEED = TSI_OPF_RETURN_CODE_PROCEED
|
||||
FAIL = TSI_OPF_RETURN_CODE_FAIL
|
||||
NOTHING = TSI_OPF_RETURN_CODE_AUTO_CLOSED
|
||||
ABORT = ci.TSI_OPF_RETURN_CODE_ABORT
|
||||
PASS = ci.TSI_OPF_RETURN_CODE_PASS
|
||||
PROCEED = ci.TSI_OPF_RETURN_CODE_PROCEED
|
||||
FAIL = ci.TSI_OPF_RETURN_CODE_FAIL
|
||||
NOTHING = ci.TSI_OPF_RETURN_CODE_AUTO_CLOSED
|
||||
|
||||
|
||||
class OPFFunctions:
|
||||
@@ -113,7 +111,7 @@ class OPFFunctions:
|
||||
def check_video(dev_rx) -> bool:
|
||||
def is_video_available(_dev_rx):
|
||||
return _dev_rx.link.status.stream(0).crc != [0, 0, 0] and \
|
||||
_dev_rx.link.status.stream(0).video_mode.timing.hactive != 0
|
||||
_dev_rx.link.status.stream(0).video_mode.timing.hactive != 0
|
||||
|
||||
return function_scheduler(is_video_available, dev_rx, interval=2, timeout=10)
|
||||
|
||||
@@ -380,7 +378,16 @@ class OPFFunctions:
|
||||
for i in range(iterations):
|
||||
|
||||
vf = capture_frame(dprx)
|
||||
vf = video_frame_to_ci(vf, vf.color_info, OPFFunctions.g_vf.data_info)
|
||||
|
||||
if OPFFunctions.g_vf is None:
|
||||
data_info = DataInfo()
|
||||
data_info.packing = DataInfo.Packing.P_PACKED
|
||||
data_info.component_order = DataInfo.ComponentOrder.CO_RGB
|
||||
data_info.alignment = DataInfo.Alignment.A_LSB
|
||||
else:
|
||||
data_info = OPFFunctions.g_vf.data_info
|
||||
|
||||
vf = video_frame_to_ci(vf, vf.color_info, data_info)
|
||||
|
||||
if vf is not None and len(vf.data) > 0:
|
||||
if OPFFunctions.g_vf is not None:
|
||||
@@ -390,11 +397,88 @@ class OPFFunctions:
|
||||
else:
|
||||
if OPFFunctions.g_vf.data in vf.data:
|
||||
return OPFDialogAnswer.PASS
|
||||
else:
|
||||
return OPFDialogAnswer.PASS
|
||||
|
||||
return OPFDialogAnswer.FAIL
|
||||
|
||||
@staticmethod
|
||||
def opf_13_handler(dptx, dprx, message: str) -> OPFDialogAnswer:
|
||||
return OPFFunctions.opf_pass_handler(dptx, dprx, message)
|
||||
audio_mode = dptx.ag.audio_mode
|
||||
|
||||
dptx.ag.setup(audio_mode, UniTAP.AudioPattern.SignalPing, 2000, 100)
|
||||
|
||||
if not dptx.ag.apply():
|
||||
return OPFDialogAnswer.FAIL
|
||||
|
||||
dprx.audio_capturer.start(m_sec=10000)
|
||||
dprx.audio_capturer.stop()
|
||||
|
||||
result = dprx.audio_capturer.capture_result
|
||||
|
||||
sample_size_bytes = int(math.ceil(result.audio_mode.bits / 8))
|
||||
|
||||
samples = {}
|
||||
for channel in range(result.audio_mode.channel_count):
|
||||
samples[channel] = []
|
||||
|
||||
for frame in result.buffer:
|
||||
frame: UniTAP.AudioFrameData
|
||||
sample_per_channel = int(len(frame.data) / sample_size_bytes / result.audio_mode.channel_count)
|
||||
|
||||
for sample_index in range(sample_per_channel):
|
||||
for channel in range(result.audio_mode.channel_count):
|
||||
sample_offset = (sample_index * sample_size_bytes * result.audio_mode.channel_count) + (
|
||||
channel * sample_size_bytes)
|
||||
sample_value = int.from_bytes(frame.data[sample_offset:sample_offset + sample_size_bytes], "little",
|
||||
signed=True)
|
||||
samples[channel].append(sample_value)
|
||||
|
||||
logging.info(f"Total samples per channel 0: {len(samples[0])}")
|
||||
|
||||
def find_next_transition(start_pos: int, sample_list: list):
|
||||
for pos in range(start_pos + 1, len(sample_list) - 1):
|
||||
previous_value = sample_list[pos - 1]
|
||||
current_value = sample_list[pos]
|
||||
next_value = sample_list[pos + 1]
|
||||
|
||||
if previous_value == 0 and current_value == 0 and next_value != 0:
|
||||
return "burst", pos + 1
|
||||
elif previous_value != 0 and current_value == 0 and next_value == 0:
|
||||
return "silence", pos + 1
|
||||
|
||||
return None, -1
|
||||
|
||||
transition_type, transition_pos = find_next_transition(0, samples[0])
|
||||
if transition_type is None:
|
||||
logging.warn("OPF 13: No transitions were found. Audio may be invalid.")
|
||||
return OPFDialogAnswer.FAIL
|
||||
|
||||
#
|
||||
# Cut beginning of captured data to start exactly with transition.
|
||||
#
|
||||
samples[0] = samples[0][transition_pos:]
|
||||
chunk_info_list = []
|
||||
transition_pos = 0
|
||||
while (t_info := find_next_transition(transition_pos, samples[0])) != (None, -1):
|
||||
chuck_size = t_info[1] - transition_pos + 1
|
||||
chunk_length_ms = int(chuck_size / result.audio_mode.sample_rate * 1000)
|
||||
logging.info(f"Chuck size: {chuck_size}")
|
||||
logging.info(f"Chuck length: {chunk_length_ms}ms")
|
||||
|
||||
chunk_info_list.append((t_info[0], chuck_size, chunk_length_ms))
|
||||
|
||||
transition_pos = t_info[1]
|
||||
|
||||
valid = True
|
||||
for chunk in chunk_info_list:
|
||||
if chunk[2] not in range(245, 255):
|
||||
valid = False
|
||||
break
|
||||
logging.info(f"Chunk validation: {'PASS' if valid else 'FAIL'}")
|
||||
logging.info(f"Chunks: {chunk_info_list}")
|
||||
|
||||
return OPFDialogAnswer.PASS if valid else OPFDialogAnswer.FAIL
|
||||
|
||||
@staticmethod
|
||||
def opf_14_handler(dptx, dprx, message: str) -> OPFDialogAnswer:
|
||||
@@ -553,10 +637,10 @@ class OPFFunctions:
|
||||
|
||||
try:
|
||||
if not OPFFunctions.check_video(dprx):
|
||||
print('Video is not available.')
|
||||
logging.error('Video is not available.')
|
||||
return OPFDialogAnswer.ABORT
|
||||
except BaseException as e:
|
||||
print(e)
|
||||
logging.error(e)
|
||||
return OPFDialogAnswer.ABORT
|
||||
|
||||
return OPFDialogAnswer.PROCEED
|
||||
@@ -582,16 +666,10 @@ class OPFFunctions:
|
||||
def opf_19_handler(dptx, dprx, use_3tap) -> OPFDialogAnswer:
|
||||
|
||||
if not OPFFunctions.check_dprx(dprx):
|
||||
return OPFDialogAnswer.ABORT
|
||||
return OPFDialogAnswer.FAIL
|
||||
|
||||
# check DSC state
|
||||
if not dprx.link.capabilities.link_caps_status().dsc:
|
||||
return OPFDialogAnswer.ABORT
|
||||
|
||||
incorrect_crc = [0xFFFF, 0xFFFF, 0xFFFF, 0x1111, 0x1111, 0x1111]
|
||||
|
||||
def load_dsc(handle: int, values: list) -> OPFDialogAnswer:
|
||||
TSIX_TS_SetConfigItem(handle, TSI_DPRX_DSC_TEST_CRC, values, data_count=6)
|
||||
return OPFDialogAnswer.FAIL
|
||||
|
||||
# TODO: Need to change on public interface. Temporary solution.
|
||||
@@ -599,140 +677,153 @@ class OPFFunctions:
|
||||
__getattribute__('_MemoryManager__io'). \
|
||||
__getattribute__('device_handle')
|
||||
|
||||
iterations = 5
|
||||
|
||||
for i in range(iterations):
|
||||
# capture frame
|
||||
try:
|
||||
dprx.video_capturer.start(frames_count=1)
|
||||
vf = dprx.video_capturer.capture_result.buffer[-1]
|
||||
dprx.video_capturer.stop()
|
||||
except BaseException:
|
||||
dprx.video_capturer.stop()
|
||||
return load_dsc(device_handle, incorrect_crc)
|
||||
|
||||
def create_reference_image(video_frame, crc_values: list):
|
||||
try:
|
||||
pps_info = DSCL_ExtractPPSFromData(video_frame.data)
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
sampling = UICL_Sampling.Sampling_444
|
||||
if pps_info.is_yuv():
|
||||
if pps_info.is_420():
|
||||
sampling = UICL_Sampling.Sampling_420
|
||||
elif pps_info.is_422():
|
||||
sampling = UICL_Sampling.Sampling_422
|
||||
elif pps_info.is_simple_422():
|
||||
sampling = UICL_Sampling.Sampling_422
|
||||
|
||||
image = UICL_Image()
|
||||
parameters = UICL_ImageParameters()
|
||||
parameters.Width = pps_info.width()
|
||||
parameters.Height = pps_info.height()
|
||||
parameters.BitsPerColor = pps_info.bpc()
|
||||
parameters.Alignment = UICL_Alignment.Alignment_LSB
|
||||
parameters.IsFullRange = False
|
||||
|
||||
if pps_info.is_yuv():
|
||||
parameters.Colorspace = UICL_Colorspace.Colorspace_YCbCr
|
||||
parameters.Colorimetry = UICL_Colorimetry.Colorimetry_ITU_R_BT709
|
||||
parameters.Sampling = sampling
|
||||
parameters.ComponentOrder = UICL_ComponentOrder.Order_YCbCr
|
||||
parameters.Packing = UICL_Packing.Packing_Planar
|
||||
image.Parameters = parameters
|
||||
|
||||
try:
|
||||
image.DataSize = UICL_GetRequiredBufferSize(image)
|
||||
image.DataPtr = (c_uint8 * image.DataSize)()
|
||||
if use_3tap:
|
||||
res = UICL_GeneratePattern_3Tap(image, 1)
|
||||
else:
|
||||
res = UICL_GeneratePattern(image, 1)
|
||||
|
||||
if res < UICL_SUCCESS:
|
||||
return
|
||||
|
||||
except BaseException:
|
||||
return
|
||||
else:
|
||||
parameters.Colorspace = UICL_Colorspace.Colorspace_RGB
|
||||
parameters.Colorimetry = UICL_Colorimetry.Colorimetry_Unknown
|
||||
parameters.Sampling = sampling
|
||||
parameters.ComponentOrder = UICL_ComponentOrder.Order_RGB
|
||||
parameters.Packing = UICL_Packing.Packing_Packed
|
||||
image.Parameters = parameters
|
||||
|
||||
try:
|
||||
image.DataSize = UICL_GetRequiredBufferSize(image)
|
||||
image.DataPtr = (c_uint8 * image.DataSize)()
|
||||
if use_3tap:
|
||||
res = UICL_GeneratePattern_3Tap(image, 0)
|
||||
else:
|
||||
res = UICL_GeneratePattern(image, 0)
|
||||
|
||||
if res < UICL_SUCCESS:
|
||||
return
|
||||
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
dsc_encoded_image = DSCL_Encode(image, pps_info)
|
||||
OPFFunctions.g_vf = dscl_image_to_dsc_vf(dsc_encoded_image)
|
||||
dsc_decoded_uicl_image = DSCL_Decode(dsc_encoded_image)
|
||||
|
||||
reference_image_crc = UICL_CRC16()
|
||||
try:
|
||||
res = UICL_CalculateCRC16(dsc_decoded_uicl_image, reference_image_crc)
|
||||
if res < UICL_SUCCESS:
|
||||
crc_values.extend([0xFFFF, 0xFFFF, 0xFFFF])
|
||||
else:
|
||||
crc_values.extend([reference_image_crc.R, reference_image_crc.G, reference_image_crc.B])
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
def decompress_dsc(video_frame, crc_values: list):
|
||||
decoded_captured_vf = decode_video_frame(video_frame)
|
||||
uicl_decoded_image_2 = image_from_vf(decoded_captured_vf)
|
||||
decompressed_image_crc = UICL_CRC16()
|
||||
try:
|
||||
res = UICL_CalculateCRC16(uicl_decoded_image_2, decompressed_image_crc)
|
||||
if res < UICL_SUCCESS:
|
||||
crc_values.extend([0x1111, 0x1111, 0x1111])
|
||||
else:
|
||||
crc_values.extend([decompressed_image_crc.R, decompressed_image_crc.G,
|
||||
decompressed_image_crc.B])
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
reference_image_crc_values = []
|
||||
decompressed_image_crc_values = []
|
||||
|
||||
thread_create_reference_image = threading.Thread(target=create_reference_image,
|
||||
args=(vf, reference_image_crc_values))
|
||||
thread_decompressed_image = threading.Thread(target=decompress_dsc,
|
||||
args=(vf, decompressed_image_crc_values))
|
||||
|
||||
thread_create_reference_image.start()
|
||||
thread_decompressed_image.start()
|
||||
|
||||
thread_create_reference_image.join()
|
||||
thread_decompressed_image.join()
|
||||
|
||||
if len(reference_image_crc_values) > 0 and len(decompressed_image_crc_values) > 0 and \
|
||||
((reference_image_crc_values == decompressed_image_crc_values) or
|
||||
(reference_image_crc_values != decompressed_image_crc_values and (i == iterations - 1))):
|
||||
# Write crc to register
|
||||
load_dsc(device_handle, [decompressed_image_crc_values[0],
|
||||
decompressed_image_crc_values[1],
|
||||
decompressed_image_crc_values[2],
|
||||
reference_image_crc_values[0],
|
||||
reference_image_crc_values[1],
|
||||
reference_image_crc_values[2]])
|
||||
|
||||
def upload_dut_and_te_crc(handle: int, values: list) -> OPFDialogAnswer:
|
||||
if tsi.TSIX_TS_SetConfigItem(handle, p_ci.TSI_DPRX_DSC_TEST_CRC, values, data_count=6) > ci.TSI_SUCCESS:
|
||||
return OPFDialogAnswer.PROCEED
|
||||
else:
|
||||
return OPFDialogAnswer.FAIL
|
||||
|
||||
return load_dsc(device_handle, incorrect_crc)
|
||||
def create_reference_image(video_frame, crc_value: list):
|
||||
try:
|
||||
pps_info = DSCL_ExtractPPSFromData(video_frame.data)
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
sampling = UICL_Sampling.Sampling_444
|
||||
if pps_info.is_yuv():
|
||||
if pps_info.is_420():
|
||||
sampling = UICL_Sampling.Sampling_420
|
||||
elif pps_info.is_422():
|
||||
sampling = UICL_Sampling.Sampling_422
|
||||
elif pps_info.is_simple_422():
|
||||
sampling = UICL_Sampling.Sampling_422
|
||||
|
||||
image = UICL_Image()
|
||||
parameters = UICL_ImageParameters()
|
||||
parameters.Width = pps_info.width()
|
||||
parameters.Height = pps_info.height()
|
||||
parameters.BitsPerColor = pps_info.bpc()
|
||||
parameters.Alignment = UICL_Alignment.Alignment_LSB
|
||||
parameters.IsFullRange = False
|
||||
|
||||
if pps_info.is_yuv():
|
||||
parameters.Colorspace = UICL_Colorspace.Colorspace_YCbCr
|
||||
parameters.Colorimetry = UICL_Colorimetry.Colorimetry_ITU_R_BT709
|
||||
parameters.Sampling = sampling
|
||||
parameters.ComponentOrder = UICL_ComponentOrder.Order_YCbCr
|
||||
parameters.Packing = UICL_Packing.Packing_Planar
|
||||
image.Parameters = parameters
|
||||
|
||||
try:
|
||||
image.DataSize = UICL_GetRequiredBufferSize(image)
|
||||
image.DataPtr = (c_uint8 * image.DataSize)()
|
||||
if use_3tap:
|
||||
res = UICL_GeneratePattern_3Tap(image, 1)
|
||||
else:
|
||||
res = UICL_GeneratePattern(image, 1)
|
||||
|
||||
if res < UICL_SUCCESS:
|
||||
return
|
||||
|
||||
except BaseException:
|
||||
return
|
||||
else:
|
||||
parameters.Colorspace = UICL_Colorspace.Colorspace_RGB
|
||||
parameters.Colorimetry = UICL_Colorimetry.Colorimetry_Unknown
|
||||
parameters.Sampling = sampling
|
||||
parameters.ComponentOrder = UICL_ComponentOrder.Order_RGB
|
||||
parameters.Packing = UICL_Packing.Packing_Packed
|
||||
image.Parameters = parameters
|
||||
|
||||
try:
|
||||
image.DataSize = UICL_GetRequiredBufferSize(image)
|
||||
image.DataPtr = (c_uint8 * image.DataSize)()
|
||||
if use_3tap:
|
||||
res = UICL_GeneratePattern_3Tap(image, 0)
|
||||
else:
|
||||
res = UICL_GeneratePattern(image, 0)
|
||||
|
||||
if res < UICL_SUCCESS:
|
||||
return
|
||||
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
dsc_encoded_image = DSCL_Encode(image, pps_info)
|
||||
OPFFunctions.g_vf = dscl_image_to_dsc_vf(dsc_encoded_image)
|
||||
dsc_decoded_uicl_image = DSCL_Decode(dsc_encoded_image)
|
||||
|
||||
reference_image_crc = UICL_CRC16()
|
||||
try:
|
||||
res = UICL_CalculateCRC16(dsc_decoded_uicl_image, reference_image_crc)
|
||||
if res >= UICL_SUCCESS:
|
||||
crc = [reference_image_crc.R, reference_image_crc.G, reference_image_crc.B]
|
||||
logging.info(f"TE successfully generated image from DUT's PPS and calculated CRC: {crc}")
|
||||
crc_value[:] = crc
|
||||
else:
|
||||
logging.error(f"TE failed to generate image from DUT's PPS without exception with error: {res}")
|
||||
except BaseException as exc:
|
||||
logging.error(f"TE failed to generated image fro DUT's PPS due an exception: {exc}")
|
||||
|
||||
def decompress_dsc(video_frame, crc_value: list):
|
||||
decoded_captured_vf = decode_video_frame(video_frame)
|
||||
uicl_decoded_image_2 = image_from_vf(decoded_captured_vf)
|
||||
decompressed_image_crc = UICL_CRC16()
|
||||
try:
|
||||
res = UICL_CalculateCRC16(uicl_decoded_image_2, decompressed_image_crc)
|
||||
if res >= UICL_SUCCESS:
|
||||
crc = [decompressed_image_crc.R, decompressed_image_crc.G, decompressed_image_crc.B]
|
||||
logging.info(f"DUT's DSC CRC successfully calculated: {crc}")
|
||||
crc_value[:] = crc
|
||||
else:
|
||||
logging.error(f"DUT's DSC CRC calculation failed, without exception: {res}")
|
||||
except BaseException as exc:
|
||||
logging.error(f"Failed to decompress DUT's DSC image due an exception: {exc}")
|
||||
|
||||
reference_image_crc_values = [0x0BAD, 0x0BAD, 0x0BAD]
|
||||
decompressed_image_crc_values = [0xBAD0, 0xBAD0, 0xBAD0]
|
||||
max_frames_to_validate = 5
|
||||
|
||||
try:
|
||||
dprx.video_capturer.start()
|
||||
|
||||
for frame_idx in range(1, max_frames_to_validate + 1):
|
||||
try:
|
||||
logging.info(f"Iteration {frame_idx}. Start frame capturing.")
|
||||
frame = dprx.video_capturer.pop_element()
|
||||
|
||||
thread_create_reference_image = threading.Thread(target=create_reference_image,
|
||||
args=(frame, reference_image_crc_values))
|
||||
thread_decompressed_image = threading.Thread(target=decompress_dsc,
|
||||
args=(frame, decompressed_image_crc_values))
|
||||
|
||||
thread_create_reference_image.start()
|
||||
thread_decompressed_image.start()
|
||||
|
||||
thread_create_reference_image.join()
|
||||
thread_decompressed_image.join()
|
||||
|
||||
if reference_image_crc_values == decompressed_image_crc_values:
|
||||
logging.info(f"Frame {frame_idx} is OK.")
|
||||
break
|
||||
else:
|
||||
te_crc_str = [f"0x{value:04X}" for value in reference_image_crc_values]
|
||||
dut_crc_str = [f"0x{value:04X}" for value in decompressed_image_crc_values]
|
||||
logging.info(f"Frame {frame_idx} is NOT OK. "
|
||||
f"TE CRC: {te_crc_str}. "
|
||||
f"DUT CRC: {dut_crc_str}.")
|
||||
|
||||
except BaseException as exception:
|
||||
logging.error(f"Frame {frame_idx} is NOT OK. Exception: {exception}")
|
||||
|
||||
except BaseException as exc:
|
||||
logging.error(f"Failed to process OPF 19 due an exception: {exc}")
|
||||
finally:
|
||||
dprx.video_capturer.stop()
|
||||
logging.info(f"Capturing has been stopped.")
|
||||
combined_crc_list = decompressed_image_crc_values + reference_image_crc_values
|
||||
logging.info("OPF 19 finished.")
|
||||
return upload_dut_and_te_crc(device_handle, combined_crc_list)
|
||||
|
||||
@staticmethod
|
||||
def opf_20_handler(dptx, dprx, res_x, res_y, res_frate, res_bpc, col_format, col_range, col_yc, tim_std,
|
||||
@@ -742,6 +833,14 @@ class OPFFunctions:
|
||||
|
||||
logging.info((res_x, res_y, res_frate, res_bpc, col_format, col_range, col_yc, tim_std, pattern_name))
|
||||
|
||||
if res_x == 0 and res_y == 0:
|
||||
logging.info("Received request to disable PG.")
|
||||
dptx.pg.set_pattern(pattern=UniTAP.VideoPattern.Disabled)
|
||||
res = dptx.pg.apply()
|
||||
logging.info(f"PG perform apply. Success: {res}.")
|
||||
|
||||
return UniTAP.OPFDialogAnswer.PASS if res else UniTAP.OPFDialogAnswer.FAIL
|
||||
|
||||
if pattern_name is not None:
|
||||
if pattern_name == 'No Video':
|
||||
pattern_name = VideoPattern.Disabled
|
||||
@@ -812,7 +911,7 @@ class OPFFunctions:
|
||||
audio_mode.bits = sample_size
|
||||
audio_mode.sample_rate = sample_freq
|
||||
audio_pattern_value = UniTAP.AudioPattern.SignalSawtooth if audio_pattern.find("Sawtooth") != -1 else (
|
||||
UniTAP.AudioPattern.SignalSine)
|
||||
UniTAP.AudioPattern.SignalSine)
|
||||
|
||||
dptx.ag.setup(audio_mode=audio_mode,
|
||||
audio_pattern=audio_pattern_value,
|
||||
@@ -823,6 +922,18 @@ class OPFFunctions:
|
||||
return OPFDialogAnswer.ABORT
|
||||
return OPFDialogAnswer.PROCEED
|
||||
|
||||
@staticmethod
|
||||
def opf_22_handler(dptx, dprx, message: str) -> OPFDialogAnswer:
|
||||
return OPFDialogAnswer.PROCEED
|
||||
|
||||
@staticmethod
|
||||
def opf_23_handler(dptx, dprx, message: str) -> OPFDialogAnswer:
|
||||
return OPFDialogAnswer.PROCEED
|
||||
|
||||
@staticmethod
|
||||
def opf_24_handler(dptx, dprx, message: str) -> OPFDialogAnswer:
|
||||
return OPFDialogAnswer.PROCEED
|
||||
|
||||
@staticmethod
|
||||
def opf_101_handler(dptx, dprx, message: str) -> OPFDialogAnswer:
|
||||
if not OPFFunctions.check_dprx(dprx):
|
||||
@@ -834,7 +945,7 @@ class OPFFunctions:
|
||||
return UniTAP.OPFDialogAnswer.PASS if len(
|
||||
dprx.audio_capturer.capture_result.buffer) > 0 else UniTAP.OPFDialogAnswer.FAIL
|
||||
except BaseException as e:
|
||||
print(f"Cannot capture audio. Error: {e}")
|
||||
logging.error(f"Cannot capture audio. Error: {e}")
|
||||
return UniTAP.OPFDialogAnswer.FAIL
|
||||
|
||||
@staticmethod
|
||||
@@ -894,7 +1005,7 @@ class OPFFunctions:
|
||||
if len(vf.data) > 0:
|
||||
return OPFDialogAnswer.PASS
|
||||
|
||||
return OPFDialogAnswer.FAIL
|
||||
return OPFDialogAnswer.FAIL
|
||||
|
||||
@staticmethod
|
||||
def opf_103_handler(dptx, dprx, width, height, rate, color_format, is_block_prediction_enabled, bpc,
|
||||
@@ -926,7 +1037,7 @@ class OPFFunctions:
|
||||
OPFFunctions.g_h_slice_size = h_slice_size
|
||||
OPFFunctions.g_v_slice_size = v_slice_size
|
||||
|
||||
res, suite_id = TSIX_TS_GetConfigItem(device_handle, TSI_SELECT_SUITE, c_uint32)
|
||||
res, suite_id = tsi.TSIX_TS_GetConfigItem(device_handle, p_ci.TSI_SELECT_SUITE, c_uint32)
|
||||
if res > 0:
|
||||
suite_id = TestGroupId(suite_id)
|
||||
else:
|
||||
@@ -936,10 +1047,12 @@ class OPFFunctions:
|
||||
try:
|
||||
if suite_id in [TestGroupId.DP_TX_LL_CTS, TestGroupId.DP_TX_LL_CTS_DSC,
|
||||
TestGroupId.DP_TX_DISPLAYID, TestGroupId.DP_TX_ADAPTIVESYNC]:
|
||||
res, is_simple_as_444 = TSIX_TS_GetConfigItem(device_handle, TSI_DP14_SINKCTS_SUPPORT_444CRC, c_uint32)
|
||||
res, is_simple_as_444 = tsi.TSIX_TS_GetConfigItem(device_handle, ci.TSI_DP14_SINKCTS_SUPPORT_444CRC,
|
||||
c_uint32)
|
||||
elif suite_id in [TestGroupId.DP_2_1_TX_LL_CTS, TestGroupId.DP_2_1_TX_DSC_CTS,
|
||||
TestGroupId.DP_2_1_TX_DISPAYID, TestGroupId.DP_2_1_TX_ADAPTIVESYNC]:
|
||||
res, is_simple_as_444 = TSIX_TS_GetConfigItem(device_handle, TSI_DP20_SINKCTS_SUPPORT_444CRC, c_uint32)
|
||||
res, is_simple_as_444 = tsi.TSIX_TS_GetConfigItem(device_handle, ci.TSI_DP20_SINKCTS_SUPPORT_444CRC,
|
||||
c_uint32)
|
||||
except BaseException as e:
|
||||
pass
|
||||
|
||||
@@ -964,18 +1077,18 @@ class OPFFunctions:
|
||||
|
||||
OPFFunctions.g_vm = copy.deepcopy(encoded_video_frame)
|
||||
|
||||
TSIX_TS_SetConfigItem(device_handle, TSI_MEMORY_LAYOUT, len(encoded_video_frame.data), c_uint64)
|
||||
TSIX_TS_SetConfigItem(device_handle, TSI_MEMORY_BLOCK_INDEX, 0, data_size=4)
|
||||
TSIX_TS_SetConfigItem(device_handle, TSI_MEMORY_WRITE_W, bytearray(encoded_video_frame.data),
|
||||
data_type=c_uint8,
|
||||
data_count=len(encoded_video_frame.data))
|
||||
TSIX_TS_SetConfigItem(device_handle, TSI_DSC_MEMORY_BLOCK, 0)
|
||||
TSIX_TS_SetConfigItem(device_handle, TSI_DSC_DATA_SIZE, len(encoded_video_frame.data))
|
||||
tsi.TSIX_TS_SetConfigItem(device_handle, ci.TSI_MEMORY_LAYOUT, len(encoded_video_frame.data), c_uint64)
|
||||
tsi.TSIX_TS_SetConfigItem(device_handle, ci.TSI_MEMORY_BLOCK_INDEX, 0, data_size=4)
|
||||
tsi.TSIX_TS_SetConfigItem(device_handle, ci.TSI_MEMORY_WRITE_W, bytearray(encoded_video_frame.data),
|
||||
data_type=c_uint8,
|
||||
data_count=len(encoded_video_frame.data))
|
||||
tsi.TSIX_TS_SetConfigItem(device_handle, p_ci.TSI_DSC_MEMORY_BLOCK, 0)
|
||||
tsi.TSIX_TS_SetConfigItem(device_handle, p_ci.TSI_DSC_DATA_SIZE, len(encoded_video_frame.data))
|
||||
|
||||
decoded_vf = decode_video_frame(encoded_video_frame)
|
||||
|
||||
crc0, crc1, crc2 = calculate_crc(decoded_vf)
|
||||
TSIX_TS_SetConfigItem(device_handle, TSI_DSC_TX_CRC, [crc0, crc1, crc2], data_count=3)
|
||||
tsi.TSIX_TS_SetConfigItem(device_handle, p_ci.TSI_DSC_TX_CRC, [crc0, crc1, crc2], data_count=3)
|
||||
|
||||
return OPFDialogAnswer.PROCEED
|
||||
|
||||
@@ -1020,14 +1133,17 @@ class OPFFunctions:
|
||||
vf.data[pps_size + slice_size:slice_size * 2]:
|
||||
return OPFDialogAnswer.PASS
|
||||
else:
|
||||
print(f"Frame {i + 1}: Image looks good and distortion free for slice #2. It is wrong.")
|
||||
logging.info(
|
||||
f"Frame {i + 1}: Image looks good and distortion free for slice #2. It is wrong.")
|
||||
else:
|
||||
print(
|
||||
logging.info(
|
||||
f"Frame {i + 1}: Image does not look good and distortion free for slice #4. It is wrong.")
|
||||
else:
|
||||
print(f"Frame {i + 1}: Image does not look good and distortion free for slice #3. It is wrong.")
|
||||
logging.info(
|
||||
f"Frame {i + 1}: Image does not look good and distortion free for slice #3. It is wrong.")
|
||||
else:
|
||||
print(f"Frame {i + 1}: Image does not look good and distortion free for slice #1. It is wrong.")
|
||||
logging.info(
|
||||
f"Frame {i + 1}: Image does not look good and distortion free for slice #1. It is wrong.")
|
||||
|
||||
return OPFDialogAnswer.FAIL
|
||||
|
||||
@@ -1043,6 +1159,66 @@ class OPFFunctions:
|
||||
def opf_107_handler(dptx, dprx, message: str) -> OPFDialogAnswer:
|
||||
return OPFFunctions.opf_pass_handler(dptx, dprx, message)
|
||||
|
||||
@staticmethod
|
||||
def opf_108_handler(dptx, dprx, *args) -> OPFDialogAnswer:
|
||||
if not OPFFunctions.check_dprx(dprx):
|
||||
return OPFDialogAnswer.ABORT
|
||||
|
||||
if len(args) == 4:
|
||||
audio_pattern, sample_rate, sample_size, ch_count = args
|
||||
else:
|
||||
audio_pattern, sample_rate, sample_size, ch_count = None, None, None, None
|
||||
|
||||
logging.info(audio_pattern, sample_rate, sample_size, ch_count)
|
||||
|
||||
try:
|
||||
dprx.audio_capturer.start(10)
|
||||
dprx.audio_capturer.stop()
|
||||
|
||||
if len(dprx.audio_capturer.capture_result.buffer) <= 0:
|
||||
logging.info("Audio buffer is empty")
|
||||
return UniTAP.OPFDialogAnswer.FAIL
|
||||
|
||||
if audio_pattern is None:
|
||||
logging.info("Default 108 OPF, check audio availability")
|
||||
return UniTAP.OPFDialogAnswer.PASS if len(dprx.audio_capturer.capture_result.buffer) > 0 else \
|
||||
UniTAP.OPFDialogAnswer.FAIL
|
||||
|
||||
def check_audio_frame(index, ch_count, sample_freq, sample_size) -> bool:
|
||||
|
||||
frame = dprx.audio_capturer.capture_result.buffer[index]
|
||||
_data = list(frame.data)
|
||||
length = len(_data)
|
||||
zero_count = _data.count(0)
|
||||
|
||||
is_only_zero = zero_count == length
|
||||
active_channel_count = OPFFunctions.get_active_channel_count(raw_data=frame.data,
|
||||
bit_depths=[frame.sample_size],
|
||||
channel_range=[frame.channel_count],
|
||||
expected_channels=ch_count
|
||||
)
|
||||
|
||||
logging.info(f"Frame index[{index}]: Len - {length}, Zero count - {zero_count}, "
|
||||
f"Reference channel count - {ch_count}, Captured active channel count - {active_channel_count}, "
|
||||
f"Reference sample rate - {sample_freq}, Captured sample rate - {frame.sample_rate}\n")
|
||||
return not is_only_zero and \
|
||||
active_channel_count == ch_count and \
|
||||
frame.sample_rate == sample_freq and \
|
||||
frame.sample_size == sample_size
|
||||
|
||||
buffer_len = len(dprx.audio_capturer.capture_result.buffer)
|
||||
|
||||
state = buffer_len > 0 and \
|
||||
(check_audio_frame(0, ch_count, sample_rate, sample_size) or
|
||||
check_audio_frame(int(buffer_len / 2), ch_count, sample_rate, sample_size) or
|
||||
check_audio_frame(buffer_len - 1, ch_count, sample_rate, sample_size))
|
||||
|
||||
return UniTAP.OPFDialogAnswer.PASS if state else UniTAP.OPFDialogAnswer.FAIL
|
||||
|
||||
except BaseException as e:
|
||||
logging.error(f"Cannot capture audio. Error: {e}")
|
||||
return UniTAP.OPFDialogAnswer.FAIL
|
||||
|
||||
@staticmethod
|
||||
def opf_120_handler(dptx, dprx, res_x, res_y, res_frate, res_bpc, color_format) -> OPFDialogAnswer:
|
||||
if not OPFFunctions.check_dprx(dprx):
|
||||
@@ -1256,6 +1432,38 @@ class OPFFunctions:
|
||||
else:
|
||||
return OPFDialogAnswer.ABORT
|
||||
|
||||
@staticmethod
|
||||
def opf_146_handler(dptx, dprx, res_x, res_y, rate):
|
||||
if not OPFFunctions.check_dptx(dptx):
|
||||
logging.error("DPTX is not initialized")
|
||||
return OPFDialogAnswer.ABORT
|
||||
|
||||
logging.info(res_x, res_y, rate)
|
||||
|
||||
return OPFDialogAnswer.PROCEED
|
||||
|
||||
@staticmethod
|
||||
def opf_147_handler(dptx, dprx, rate):
|
||||
if not OPFFunctions.check_dptx(dptx):
|
||||
logging.error("DPTX is not initialized")
|
||||
return OPFDialogAnswer.ABORT
|
||||
|
||||
logging.info(f"Refresh rate passed from OPF: {rate}")
|
||||
|
||||
dptx.pg.set_as_config(as_config=FixedASParams(refresh_rate=rate,
|
||||
divide_by_1_001=False,
|
||||
increase_lines=100,
|
||||
decrease_lines=100))
|
||||
res_pg = dptx.pg.apply()
|
||||
|
||||
if not res_pg:
|
||||
logging.info("DPTX pattern is not applied")
|
||||
return OPFDialogAnswer.ABORT
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
return OPFDialogAnswer.PROCEED
|
||||
|
||||
@staticmethod
|
||||
def opf_150_handler(dptx, dprx, message: str) -> OPFDialogAnswer:
|
||||
return OPFFunctions.opf_pass_handler(dptx, dprx, message)
|
||||
@@ -1342,7 +1550,7 @@ class OPFFunctions:
|
||||
@staticmethod
|
||||
def check_dsc_content_library(*args):
|
||||
width, height, color_mode, is_block_prediction_enabled, bpc, bpp, h_slice_number, v_slice_number, \
|
||||
compression_info, dsc_v_major, dsc_v_minor, data_info = args
|
||||
compression_info, dsc_v_major, dsc_v_minor, data_info = args
|
||||
if OPFFunctions.dsc_content_library_path != "":
|
||||
if not os.path.exists(OPFFunctions.dsc_content_library_path):
|
||||
os.makedirs(OPFFunctions.dsc_content_library_path)
|
||||
@@ -1375,7 +1583,7 @@ class OPFFunctions:
|
||||
@staticmethod
|
||||
def get_compression_info(*args):
|
||||
v_slice_size, h_slice_size, bpp, color_mode, buffer_bit_depth, is_block_prediction_enabled, dsc_v_major, \
|
||||
dsc_v_minor, is_simple_as_444 = args
|
||||
dsc_v_minor, is_simple_as_444 = args
|
||||
|
||||
compression_info = CompressionInfo()
|
||||
compression_info.v_slice_size = v_slice_size
|
||||
@@ -1388,3 +1596,94 @@ class OPFFunctions:
|
||||
compression_info.is_simple_as_444 = is_simple_as_444
|
||||
|
||||
return compression_info
|
||||
|
||||
@staticmethod
|
||||
def read_pcm(raw_data, sample_size):
|
||||
"""
|
||||
Read raw PCM data from file into a list of int32 values.
|
||||
Handles 16, 20, and 24-bit samples (little-endian).
|
||||
"""
|
||||
if sample_size == 16:
|
||||
if len(raw_data) % 2 != 0:
|
||||
raise ValueError("File length not divisible by 2 for 16-bit audio")
|
||||
result = []
|
||||
for i in range(0, len(raw_data), 2):
|
||||
sample = int.from_bytes(raw_data[i:i + 2], byteorder='little', signed=True)
|
||||
result.append(sample)
|
||||
return result, 32767
|
||||
elif sample_size == 20 or sample_size == 24:
|
||||
if len(raw_data) % 3 != 0:
|
||||
raise ValueError("File length not divisible by 3 for 24/20-bit audio")
|
||||
result = []
|
||||
for i in range(0, len(raw_data), 3):
|
||||
val = (raw_data[i] |
|
||||
(raw_data[i + 1] << 8) |
|
||||
(raw_data[i + 2] << 16))
|
||||
sign_mask = 1 << 23
|
||||
val = (val & (sign_mask - 1)) - (val & sign_mask)
|
||||
result.append(val)
|
||||
return result, (1 << 23) - 1
|
||||
else:
|
||||
raise ValueError(f"Unsupported bit depth: {sample_size}")
|
||||
|
||||
@staticmethod
|
||||
def get_active_channel_count(raw_data, bit_depths: list, channel_range: list, expected_channels: int,
|
||||
rms_threshold=1e-3, nonzero_frac_threshold=1e-4):
|
||||
"""
|
||||
Try multiple bit depths and channel counts, report active channels.
|
||||
"""
|
||||
active_count = 0
|
||||
for bd in bit_depths:
|
||||
try:
|
||||
data, dtype_max = OPFFunctions.read_pcm(raw_data, bd)
|
||||
except Exception as e:
|
||||
logging.info(f"[{bd}-bit] Error: {e}")
|
||||
continue
|
||||
|
||||
logging.info(f"\n=== Testing bit depth {bd} ===")
|
||||
|
||||
for nch in [expected_channels] + [ch for ch in channel_range if ch != expected_channels]:
|
||||
if len(data) % nch != 0:
|
||||
logging.info(f"Skipping {nch} channels: data length {len(data)} not divisible by {nch}")
|
||||
continue
|
||||
frames = len(data) // nch
|
||||
|
||||
arr = []
|
||||
for f in range(frames):
|
||||
frame = data[f * nch:(f + 1) * nch]
|
||||
arr.append(frame)
|
||||
|
||||
rms = []
|
||||
peak = []
|
||||
nonzero_frac = []
|
||||
for ch in range(nch):
|
||||
channel_data = [arr[f][ch] for f in range(frames)]
|
||||
|
||||
mean_square = sum(x * x for x in channel_data) / frames
|
||||
rms.append(mean_square ** 0.5)
|
||||
|
||||
peak.append(max(abs(x) for x in channel_data))
|
||||
|
||||
nonzero_count = sum(1 for x in channel_data if x != 0)
|
||||
nonzero_frac.append(nonzero_count / frames)
|
||||
|
||||
norm_rms = [r / dtype_max for r in rms]
|
||||
norm_peak = [p / dtype_max for p in peak]
|
||||
|
||||
logging.info(f"\nAssuming {nch} channel(s):")
|
||||
channel_active_count = 0
|
||||
for ch in range(nch):
|
||||
active = (norm_rms[ch] > rms_threshold or
|
||||
nonzero_frac[ch] > nonzero_frac_threshold)
|
||||
if active:
|
||||
channel_active_count += 1
|
||||
logging.info(f" Ch{ch}: normRMS={norm_rms[ch]:.6f}, "
|
||||
f"normPeak={norm_peak[ch]:.6f}, "
|
||||
f"nonzero={nonzero_frac[ch]:.6f}, ACTIVE={active}")
|
||||
logging.info(f" => Detected {channel_active_count}/{nch} active channels")
|
||||
|
||||
if channel_active_count == expected_channels:
|
||||
active_count = channel_active_count
|
||||
break
|
||||
|
||||
return active_count
|
||||
|
||||
Reference in New Issue
Block a user