You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

705 lines
25 KiB

// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2022 Intel Corporation. All Rights Reserved.
#include "unit-tests-common.h"
std::vector< profile >
configure_all_supported_streams( rs2::sensor & sensor, int width, int height, int fps )
{
std::vector<profile> all_profiles =
{
{ RS2_STREAM_DEPTH, RS2_FORMAT_Z16, width, height, 0, fps},
{ RS2_STREAM_COLOR, RS2_FORMAT_RGB8, width, height, 0, fps},
{ RS2_STREAM_INFRARED, RS2_FORMAT_Y8, width, height, 1, fps},
{ RS2_STREAM_INFRARED, RS2_FORMAT_Y8, width, height, 2, fps},
{ RS2_STREAM_INFRARED, RS2_FORMAT_Y8, width, height, 0, fps},
{ RS2_STREAM_CONFIDENCE,RS2_FORMAT_RAW8, width, height, 0, fps},
{ RS2_STREAM_FISHEYE, RS2_FORMAT_RAW8, width, height, 0, fps},
{ RS2_STREAM_ACCEL, RS2_FORMAT_MOTION_XYZ32F, 1, 1, 0, 200},
{ RS2_STREAM_GYRO, RS2_FORMAT_MOTION_XYZ32F, 1, 1, 0, 200},
};
std::vector< profile > profiles;
std::vector< rs2::stream_profile > modes;
auto all_modes = sensor.get_stream_profiles();
for( auto profile : all_profiles )
{
if( std::find_if( all_modes.begin(),
all_modes.end(),
[&]( rs2::stream_profile p ) {
if( auto video = p.as< rs2::video_stream_profile >() )
{
if( p.fps() == profile.fps && p.stream_index() == profile.index
&& p.stream_type() == profile.stream && p.format() == profile.format
&& video.width() == profile.width && video.height() == profile.height )
{
modes.push_back( p );
return true;
}
}
else
{
if( auto motion = p.as< rs2::motion_stream_profile >() )
{
if( ( ( profile.fps / ( p.fps() + 1 ) ) <= 2.f )
&& // Approximate IMU rates. No need for being exact
p.stream_index() == profile.index && p.stream_type() == profile.stream
&& p.format() == profile.format )
{
modes.push_back( p );
return true;
}
}
else
return false;
}
return false;
} )
!= all_modes.end() )
{
profiles.push_back( profile );
}
}
if( modes.size() > 0 )
REQUIRE_NOTHROW( sensor.open( modes ) );
return profiles;
}
std::pair< std::vector< rs2::sensor >, std::vector< profile > >
configure_all_supported_streams( rs2::device & dev, int width, int height, int fps )
{
std::vector< profile > profiles;
std::vector< rs2::sensor > sensors;
auto sens = dev.query_sensors();
for( auto s : sens )
{
auto res = configure_all_supported_streams( s, width, height, fps );
profiles.insert( profiles.end(), res.begin(), res.end() );
if( res.size() > 0 )
{
sensors.push_back( s );
}
}
return { sensors, profiles };
}
std::string space_to_underscore( const std::string & text )
{
const std::string from = " ";
const std::string to = "__";
auto temp = text;
size_t start_pos = 0;
while( ( start_pos = temp.find( from, start_pos ) ) != std::string::npos )
{
temp.replace( start_pos, from.size(), to );
start_pos += to.size();
}
return temp;
}
//inline long long current_time()
//{
// return (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count() % 10000);
//}
//// disable in one place options that are sensitive to frame content
//// this is done to make sure unit-tests are deterministic
void disable_sensitive_options_for( rs2::sensor & sen )
{
if( sen.supports( RS2_OPTION_ERROR_POLLING_ENABLED ) )
REQUIRE_NOTHROW( sen.set_option( RS2_OPTION_ERROR_POLLING_ENABLED, 0 ) );
if( sen.supports( RS2_OPTION_ENABLE_AUTO_EXPOSURE ) )
REQUIRE_NOTHROW( sen.set_option( RS2_OPTION_ENABLE_AUTO_EXPOSURE, 0 ) );
if( sen.supports( RS2_OPTION_GLOBAL_TIME_ENABLED ) )
REQUIRE_NOTHROW( sen.set_option( RS2_OPTION_GLOBAL_TIME_ENABLED, 0 ) );
if( sen.supports( RS2_OPTION_EXPOSURE ) )
{
rs2::option_range range;
REQUIRE_NOTHROW(
range = sen.get_option_range( RS2_OPTION_EXPOSURE ) ); // TODO: fails sometimes with "Element Not Found!"
// float val = (range.min + (range.def-range.min)/10.f); // TODO - generate new records using the modified
// exposure
REQUIRE_NOTHROW( sen.set_option( RS2_OPTION_EXPOSURE, range.def ) );
}
}
void disable_sensitive_options_for( rs2::device & dev )
{
for( auto && s : dev.query_sensors() )
disable_sensitive_options_for( s );
}
bool wait_for_reset( std::function< bool( void ) > func, std::shared_ptr< rs2::device > dev )
{
if( func() )
return true;
WARN( "Reset workaround" );
try
{
dev->hardware_reset();
}
catch( ... )
{
}
return func();
}
bool is_usb3( const rs2::device & dev )
{
bool usb3_device = true;
try
{
std::string usb_type( dev.get_info( RS2_CAMERA_INFO_USB_TYPE_DESCRIPTOR ) );
// Device types "3.x" and also "" (for legacy playback records) will be recognized as USB3
usb3_device = ( std::string::npos == usb_type.find( "2." ) );
}
catch( ... ) // In case the feature not provided assume USB3 device
{
}
return usb3_device;
}
dev_type get_PID( rs2::device & dev )
{
dev_type designator{ "", true };
std::string usb_type{};
bool usb_descriptor = false;
REQUIRE_NOTHROW( designator.first = dev.get_info( RS2_CAMERA_INFO_PRODUCT_ID ) );
REQUIRE_NOTHROW( usb_descriptor = dev.supports( RS2_CAMERA_INFO_USB_TYPE_DESCRIPTOR ) );
if( usb_descriptor )
{
REQUIRE_NOTHROW( usb_type = dev.get_info( RS2_CAMERA_INFO_USB_TYPE_DESCRIPTOR ) );
designator.second = ( std::string::npos == usb_type.find( "2." ) );
}
return designator;
}
void command_line_params::init( int argc, char const * const * const argv )
{
rs2::log_to_file( RS2_LOG_SEVERITY_DEBUG );
}
static bool g_found_any_section = false;
bool found_any_section()
{
return g_found_any_section;
}
rs2::context make_context( const char * /*id*/ )
{
rs2::context ctx( rs2::context::uninitialized );
try
{
ctx = rs2::context( "{\"dds\":false}" );
g_found_any_section = true;
}
catch( ... )
{
std::cerr << "UNKNOWN EXCEPTiON in make_context!" << std::endl;
}
return ctx;
}
// Require that matrix is an orthonormal 3x3 matrix
void require_rotation_matrix(const float(&matrix)[9])
{
const float row0[] = { matrix[0], matrix[3], matrix[6] };
const float row1[] = { matrix[1], matrix[4], matrix[7] };
const float row2[] = { matrix[2], matrix[5], matrix[8] };
CAPTURE( full_precision( row0[0] ));
CAPTURE( full_precision( row0[1] ));
CAPTURE( full_precision( row0[2] ));
CAPTURE( full_precision( row1[0] ));
CAPTURE( full_precision( row1[1] ));
CAPTURE( full_precision( row1[2] ));
CAPTURE( full_precision( row2[0] ));
CAPTURE( full_precision( row2[1] ));
CAPTURE( full_precision( row2[2] ));
CHECK(dot_product(row0, row0) == approx(1.f));
CAPTURE( full_precision( dot_product( row1, row1 )));
CHECK( dot_product( row1, row1 ) == approx( 1.f ) ); // this line is problematic, and needs higher epsilon!!
CHECK_THAT(dot_product(row1, row1), approx_equals(1.f));
CHECK(dot_product(row2, row2) == approx(1.f));
CHECK(dot_product(row0, row1) == approx(0.f));
CHECK(dot_product(row1, row2) == approx(0.f));
CHECK(dot_product(row2, row0) == approx(0.f));
require_cross_product(row0, row1, row2);
require_cross_product(row0, row1, row2);
require_cross_product(row0, row1, row2);
}
// Require that matrix is exactly the identity matrix
void require_identity_matrix( const float ( &matrix )[9] )
{
static const float identity_matrix_3x3[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
for( int i = 0; i < 9; ++i )
REQUIRE( matrix[i] == approx( identity_matrix_3x3[i] ) );
}
//inline void test_wait_for_frames(rs2_device * device, std::initializer_list<stream_mode>& modes, std::map<rs2_stream, test_duration>& duration_per_stream)
//{
// rs2_start_device(device, require_no_error());
// REQUIRE( rs2_is_device_streaming(device, require_no_error()) == 1 );
//
// rs2_wait_for_frames(device, require_no_error());
//
// int lowest_fps_mode = std::numeric_limits<int>::max();
// for(auto & mode : modes)
// {
// REQUIRE( rs2_is_stream_enabled(device, mode.stream, require_no_error()) == 1 );
// REQUIRE( rs2_get_frame_data(device, mode.stream, require_no_error()) != nullptr );
// REQUIRE( rs2_get_frame_timestamp(device, mode.stream, require_no_error()) >= 0 );
// if (mode.framerate < lowest_fps_mode) lowest_fps_mode = mode.framerate;
// }
//
// std::vector<unsigned long long> last_frame_number;
// std::vector<unsigned long long> number_of_frames;
// last_frame_number.resize(RS2_STREAM_COUNT);
// number_of_frames.resize(RS2_STREAM_COUNT);
//
// for (auto& elem : modes)
// {
// number_of_frames[elem.stream] = 0;
// last_frame_number[elem.stream] = 0;
// }
//
// const auto actual_frames_to_receive = std::min(max_frames_to_receive, (uint32_t)(max_capture_time_sec* lowest_fps_mode));
// const auto first_frame_to_capture = (actual_frames_to_receive*0.1); // Skip the first 10% of frames
// const auto frames_to_capture = (actual_frames_to_receive - first_frame_to_capture);
//
// for (auto i = 0u; i< actual_frames_to_receive; ++i)
// {
// rs2_wait_for_frames(device, require_no_error());
//
// if (i < first_frame_to_capture) continue; // Skip some frames at the beginning to stabilize the stream output
//
// for (auto & mode : modes)
// {
// if (rs2_get_frame_timestamp(device, mode.stream, require_no_error()) > 0)
// {
// REQUIRE( rs2_is_stream_enabled(device, mode.stream, require_no_error()) == 1);
// REQUIRE( rs2_get_frame_data(device, mode.stream, require_no_error()) != nullptr);
// REQUIRE( rs2_get_frame_timestamp(device, mode.stream, require_no_error()) >= 0);
//
// auto frame_number = rs2_get_frame_number(device, mode.stream, require_no_error());
// if (!duration_per_stream[mode.stream].is_end_time_initialized && last_frame_number[mode.stream] != frame_number)
// {
// last_frame_number[mode.stream] = frame_number;
// ++number_of_frames[mode.stream];
// }
//
// if (!duration_per_stream[mode.stream].is_start_time_initialized && number_of_frames[mode.stream] >= 1)
// {
// duration_per_stream[mode.stream].start_time = std::chrono::high_resolution_clock::now();
// duration_per_stream[mode.stream].is_start_time_initialized = true;
// }
//
// if (!duration_per_stream[mode.stream].is_end_time_initialized && (number_of_frames[mode.stream] > (0.9 * frames_to_capture))) // Requires additional work for streams with different fps
// {
// duration_per_stream[mode.stream].end_time = std::chrono::high_resolution_clock::now();
// auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(duration_per_stream[mode.stream].end_time - duration_per_stream[mode.stream].start_time).count();
// auto fps = ((double)number_of_frames[mode.stream] / duration) * 1000.;
// //check_fps(fps, mode.framerate); // requires additional work to configure UVC controls in order to achieve the required fps
// duration_per_stream[mode.stream].is_end_time_initialized = true;
// }
// }
// }
// }
//
// rs2_stop_device(device, require_no_error());
// REQUIRE( rs2_is_device_streaming(device, require_no_error()) == 0 );
//}
static std::mutex m;
static std::mutex cb_mtx;
static std::condition_variable cv;
static std::atomic<bool> stop_streaming;
static int done;
void frame_callback( rs2::device & dev, rs2::frame frame, void * user )
{
std::lock_guard< std::mutex > lock( cb_mtx );
if( stop_streaming || ( frame.get_timestamp() == 0 ) )
return;
auto data = (user_data *)user;
bool stop = true;
auto stream_type = frame.get_profile().stream_type();
for( auto & elem : data->number_of_frames_per_stream )
{
if( elem.second < data->duration_per_stream[stream_type].actual_frames_to_receive )
{
stop = false;
break;
}
}
if( stop )
{
stop_streaming = true;
{
std::lock_guard< std::mutex > lk( m );
done = true;
}
cv.notify_one();
return;
}
if( data->duration_per_stream[stream_type].is_end_time_initialized )
return;
unsigned num_of_frames = 0;
num_of_frames = ( ++data->number_of_frames_per_stream[stream_type] );
if( num_of_frames >= data->duration_per_stream[stream_type].actual_frames_to_receive )
{
if( ! data->duration_per_stream[stream_type].is_end_time_initialized )
{
data->duration_per_stream[stream_type].end_time = std::chrono::high_resolution_clock::now();
data->duration_per_stream[stream_type].is_end_time_initialized = true;
}
return;
}
if( ( num_of_frames == data->duration_per_stream[stream_type].first_frame_to_capture )
&& ( ! data->duration_per_stream[stream_type]
.is_start_time_initialized ) ) // Skip some frames at the beginning to stabilize the stream output
{
data->duration_per_stream[stream_type].start_time = std::chrono::high_resolution_clock::now();
data->duration_per_stream[stream_type].is_start_time_initialized = true;
}
REQUIRE( frame.get_data() != nullptr );
REQUIRE( frame.get_timestamp() >= 0 );
}
//inline void test_frame_callback(device &device, std::initializer_list<stream_profile>& modes, std::map<rs2_stream, test_duration>& duration_per_stream)
//{
// done = false;
// stop_streaming = false;
// user_data data;
// data.duration_per_stream = duration_per_stream;
//
// for(auto & mode : modes)
// {
// data.number_of_frames_per_stream[mode.stream] = 0;
// data.duration_per_stream[mode.stream].is_start_time_initialized = false;
// data.duration_per_stream[mode.stream].is_end_time_initialized = false;
// data.duration_per_stream[mode.stream].actual_frames_to_receive = std::min(max_frames_to_receive, (uint32_t)(max_capture_time_sec* mode.fps));
// data.duration_per_stream[mode.stream].first_frame_to_capture = (uint32_t)(data.duration_per_stream[mode.stream].actual_frames_to_receive*0.1); // Skip the first 10% of frames
// data.duration_per_stream[mode.stream].frames_to_capture = data.duration_per_stream[mode.stream].actual_frames_to_receive - data.duration_per_stream[mode.stream].first_frame_to_capture;
// // device doesn't know which subdevice is providing which stream. could loop over all subdevices? no, because subdevices don't expose this information
// REQUIRE( rs2_is_stream_enabled(device, mode.stream, require_no_error()) == 1 );
// rs2_start(device, mode.stream, frame_callback, &data, require_no_error());
// }
//
// rs2_start_device(device, require_no_error());
// REQUIRE( rs2_is_device_streaming(device, require_no_error()) == 1 );
//
// {
// std::unique_lock<std::mutex> lk(m);
// cv.wait(lk, [&]{return done;});
// lk.unlock();
// }
//
// rs2_stop_device(device, require_no_error());
// REQUIRE( rs2_is_device_streaming(device, require_no_error()) == 0 );
//
// for(auto & mode : modes)
// {
// auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(data.duration_per_stream[mode.stream].end_time - data.duration_per_stream[mode.stream].start_time).count();
// auto fps = ((float)data.number_of_frames_per_stream[mode.stream] / duration) * 1000.;
// //check_fps(fps, mode.framerate); / TODO is not suppuorted yet. See above message
// }
//}
//inline void motion_callback(rs2_device * , rs2_motion_data, void *)
//{
//}
//
//inline void timestamp_callback(rs2_device * , rs2_timestamp_data, void *)
//{
//}
//
//// Provide support for doing basic streaming tests on a set of specified modes
//inline void test_streaming(rs2_device * device, std::initializer_list<stream_profile> modes)
//{
// std::map<rs2_stream, test_duration> duration_per_stream;
// for(auto & mode : modes)
// {
// duration_per_stream.insert(std::pair<rs2_stream, test_duration>(mode.stream, test_duration()));
// rs2_enable_stream(device, mode.stream, mode.width, mode.height, mode.format, mode.framerate, require_no_error());
// REQUIRE( rs2_is_stream_enabled(device, mode.stream, require_no_error()) == 1 );
// }
//
// test_wait_for_frames(device, modes, duration_per_stream);
//
// test_frame_callback(device, modes, duration_per_stream);
//
// for(auto & mode : modes)
// {
// rs2_disable_stream(device, mode.stream, require_no_error());
// REQUIRE( rs2_is_stream_enabled(device, mode.stream, require_no_error()) == 0 );
// }
//}
void test_option( rs2::sensor & device,
rs2_option option,
std::initializer_list< int > good_values,
std::initializer_list< int > bad_values )
{
// Test reading the current value
float first_value;
REQUIRE_NOTHROW( first_value = device.get_option( option ) );
// check if first value is something sane (should be default?)
rs2::option_range range;
REQUIRE_NOTHROW( range = device.get_option_range( option ) );
REQUIRE( first_value >= range.min );
REQUIRE( first_value <= range.max );
CHECK( first_value == range.def );
// Test setting good values, and that each value set can be subsequently get
for( auto value : good_values )
{
REQUIRE_NOTHROW( device.set_option( option, (float)value ) );
REQUIRE( device.get_option( option ) == value );
}
// Test setting bad values, and verify that they do not change the value of the option
float last_good_value;
REQUIRE_NOTHROW( last_good_value = device.get_option( option ) );
for( auto value : bad_values )
{
REQUIRE_THROWS_AS( device.set_option( option, (float)value ), rs2::error );
REQUIRE( device.get_option( option ) == last_good_value );
}
// Test that we can reset the option to its original value
REQUIRE_NOTHROW( device.set_option( option, first_value ) );
REQUIRE( device.get_option( option ) == first_value );
}
rs2::stream_profile get_profile_by_resolution_type( rs2::sensor & s, res_type res )
{
auto sp = s.get_stream_profiles();
int width = 0, height = 0;
for( auto && p : sp )
{
if( auto video = p.as< rs2::video_stream_profile >() )
{
width = video.width();
height = video.height();
if( ( res = get_res_type( width, height ) ) )
return p;
}
}
std::stringstream ss;
ss << "stream profile for " << width << "," << height << " resolution is not supported!";
throw std::runtime_error( ss.str() );
}
std::shared_ptr< rs2::device > do_with_waiting_for_camera_connection( rs2::context ctx,
std::shared_ptr< rs2::device > dev,
std::string serial,
std::function< void() > operation )
{
std::mutex m;
bool disconnected = false;
bool connected = false;
std::shared_ptr< rs2::device > result;
std::condition_variable cv;
ctx.set_devices_changed_callback( [&]( rs2::event_information info ) mutable {
if( info.was_removed( *dev ) )
{
std::unique_lock< std::mutex > lock( m );
disconnected = true;
cv.notify_all();
}
auto list = info.get_new_devices();
if( list.size() > 0 )
{
for( auto cam : list )
{
if( serial == cam.get_info( RS2_CAMERA_INFO_SERIAL_NUMBER ) )
{
std::unique_lock< std::mutex > lock( m );
connected = true;
result = std::make_shared< rs2::device >( cam );
disable_sensitive_options_for( *result );
cv.notify_all();
break;
}
}
}
} );
operation();
std::unique_lock< std::mutex > lock( m );
REQUIRE( wait_for_reset(
[&]() { return cv.wait_for( lock, std::chrono::seconds( 20 ), [&]() { return disconnected; } ); },
dev ) );
REQUIRE( cv.wait_for( lock, std::chrono::seconds( 20 ), [&]() { return connected; } ) );
REQUIRE( result );
ctx.set_devices_changed_callback( []( rs2::event_information info ) {} ); // reset callback
return result;
}
rs2::depth_sensor restart_first_device_and_return_depth_sensor( const rs2::context & ctx,
const rs2::device_list & devices_list )
{
rs2::device dev = devices_list[0];
std::string serial;
REQUIRE_NOTHROW( serial = dev.get_info( RS2_CAMERA_INFO_SERIAL_NUMBER ) );
// forcing hardware reset to simulate device disconnection
auto shared_dev = std::make_shared< rs2::device >( devices_list.front() );
shared_dev = do_with_waiting_for_camera_connection( ctx, shared_dev, serial, [&]() { shared_dev->hardware_reset(); } );
REQUIRE( shared_dev );
rs2::depth_sensor depth_sensor = shared_dev->query_sensors().front();
return depth_sensor;
}
#ifdef _WIN32
#include <windows.h>
#include <wchar.h>
#include <KnownFolders.h>
#include <shlobj.h>
std::string get_folder_path( special_folder f )
{
std::string res;
if( f == temp_folder )
{
TCHAR buf[MAX_PATH];
if( GetTempPath( MAX_PATH, buf ) != 0 )
{
char str[1024];
wcstombs( str, buf, 1023 );
res = str;
}
}
else
{
GUID folder;
switch( f )
{
case user_desktop:
folder = FOLDERID_Desktop;
break;
case user_documents:
folder = FOLDERID_Documents;
break;
case user_pictures:
folder = FOLDERID_Pictures;
break;
case user_videos:
folder = FOLDERID_Videos;
break;
default:
throw std::invalid_argument( std::string( "Value of f (" ) + std::to_string( f )
+ std::string( ") is not supported" ) );
}
PWSTR folder_path = NULL;
HRESULT hr = SHGetKnownFolderPath( folder, KF_FLAG_DEFAULT_PATH, NULL, &folder_path );
if( SUCCEEDED( hr ) )
{
char str[1024];
wcstombs( str, folder_path, 1023 );
CoTaskMemFree( folder_path );
res = str;
}
}
return res;
}
#endif //_WIN32
#if defined __linux__ || defined __APPLE__
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
std::string get_folder_path( special_folder f )
{
std::string res;
if( f == special_folder::temp_folder )
{
const char * tmp_dir = getenv( "TMPDIR" );
res = tmp_dir ? tmp_dir : "/tmp/";
}
else
{
const char * home_dir = getenv( "HOME" );
if( ! home_dir )
{
struct passwd * pw = getpwuid( getuid() );
home_dir = ( pw && pw->pw_dir ) ? pw->pw_dir : "";
}
if( home_dir )
{
res = home_dir;
switch( f )
{
case user_desktop:
res += "/Desktop/";
break;
case user_documents:
res += "/Documents/";
break;
case user_pictures:
res += "/Pictures/";
break;
case user_videos:
res += "/Videos/";
break;
default:
throw std::invalid_argument( std::string( "Value of f (" ) + std::to_string( f )
+ std::string( ") is not supported" ) );
}
}
}
return res;
}
#endif // defined __linux__ || defined __APPLE__