diff --git a/Readme.md b/Readme.md index bc26669..45a46e1 100644 --- a/Readme.md +++ b/Readme.md @@ -9,10 +9,11 @@ - Optimized for only one purpose - to use HackRF as a spectrum analyzer - All changes in settings restart hackrf_sweep automatically - Easy retuning -- hackrf_sweep integrated as a shared library -- Peak display +- Peak / Persistent display +- Frequency allocation bands for EU - High resolution waterfall plot - Spur filter - removes spur artifacts from the spectrum +- hackrf_sweep integrated as a shared library ### Why? Other software is limited or hard to use diff --git a/release/hackrf_spectrum_analyzer.zip b/release/hackrf_spectrum_analyzer.zip index 9b4b71e..6d7df43 100644 Binary files a/release/hackrf_spectrum_analyzer.zip and b/release/hackrf_spectrum_analyzer.zip differ diff --git a/screenshot.gif b/screenshot.gif index eb97d17..d1a289d 100644 Binary files a/screenshot.gif and b/screenshot.gif differ diff --git a/src/hackrf-sweep/.classpath b/src/hackrf-sweep/.classpath index bf83daf..9ea6aee 100644 --- a/src/hackrf-sweep/.classpath +++ b/src/hackrf-sweep/.classpath @@ -8,7 +8,7 @@ - + diff --git a/src/hackrf-sweep/.depend b/src/hackrf-sweep/.depend new file mode 100644 index 0000000..882174a --- /dev/null +++ b/src/hackrf-sweep/.depend @@ -0,0 +1,2 @@ +hackrf_sweep.o: lib/hackrf/host/hackrf-tools/src/hackrf_sweep.c \ + lib/hackrf/host/hackrf-tools/src/hackrf_sweep.h diff --git a/src/hackrf-sweep/.gitignore b/src/hackrf-sweep/.gitignore index f40cdfc..978b009 100644 --- a/src/hackrf-sweep/.gitignore +++ b/src/hackrf-sweep/.gitignore @@ -3,4 +3,5 @@ /bin /build /obj -/res \ No newline at end of file +/res +**.jrf \ No newline at end of file diff --git a/src/hackrf-sweep/Makefile b/src/hackrf-sweep/Makefile index 2078f2e..70986d2 100644 --- a/src/hackrf-sweep/Makefile +++ b/src/hackrf-sweep/Makefile @@ -7,6 +7,12 @@ BUILD_PATH = obj #where the resulting release zip will be copied RELEASE_DIR = ../../release/ +JNA_LIB = lib/hackrf-sweep-jna.jar +JNA_SWEEP_HEADER = $(CURDIR)/lib/hackrf/host/hackrf-tools/src/hackrf_sweep.h +LIBUSB_DIR = lib/libusb-1.0.21 +MAIN_JAR = build/lib/hackrf_sweep_spectrum_analyzer.jar + + ARCH_DEFINED = n ifeq ($(ARCH), x86-64) ARCH_DEFINED = y @@ -18,7 +24,6 @@ IS_64BIT = n endif - #SOURCES = \ # lib/hackrf-2017.02.1/host/libhackrf/src/hackrf.c \ # lib/hackrf-2017.02.1/host/hackrf-tools/src/hackrf_sweep.c @@ -27,7 +32,7 @@ SOURCES = \ lib/hackrf/host/libhackrf/src/hackrf.c \ lib/hackrf/host/hackrf-tools/src/hackrf_sweep.c -INCLUDE_PATHS = -Ilib/hackrf/host/libhackrf/src -Ilib/libusb-1.0.20/include/libusb-1.0 +INCLUDE_PATHS = -Ilib/hackrf/host/libhackrf/src -I$(LIBUSB_DIR)/include/libusb-1.0 #-Ilib/hackrf-2017.02.1/host/libhackrf/src @@ -57,10 +62,10 @@ CC = gcc CXX = g++ ifeq ($(IS_64BIT), y) - LDPATHS = -L"lib/fftw-3.3.5-dll64" -L"lib/libusb-1.0.20/MinGW64/dll" + LDPATHS = -L"lib/fftw-3.3.5-dll64" -L"$(LIBUSB_DIR)/MinGW64/dll" INCLUDE_PATHS += -Ilib/fftw-3.3.5-dll64 else - LDPATHS = -L"lib/fftw-3.3.5-dll32" -L"lib/libusb-1.0.20/MinGW32/dll" + LDPATHS = -L"lib/fftw-3.3.5-dll32" -L"$(LIBUSB_DIR)/MinGW32/dll" INCLUDE_PATHS += -Ilib/fftw-3.3.5-dll32 endif @@ -78,27 +83,25 @@ endif #paths #path to build dlls -OUTPUT_DIR = build/lib/$(LIB_DIR) +OUTPUT_DLL_DIR = build/lib/$(LIB_DIR) -JNA_LIB = lib/hackrf-sweep-jna.jar -JNA_SWEEP_HEADER = $(CURDIR)/lib/hackrf/host/hackrf-tools/src/hackrf_sweep.h DLL_LIB_PTHREAD = lib/$(LIB_DIR)/libwinpthread-1.dll DLL_JNA = lib/$(LIB_DIR)/jnidispatch.dll -DLL_LIB = $(OUTPUT_DIR)/hackrf-sweep.dll +DLL_LIB = $(OUTPUT_DLL_DIR)/hackrf-sweep.dll ifeq ($(IS_64BIT), y) DLL_LIB_FFTW = lib/fftw-3.3.5-dll64/libfftw3f-3.dll - DLL_LIB_USB = lib/libusb-1.0.20/MinGW64/dll/libusb-1.0.dll + DLL_LIB_USB = $(LIBUSB_DIR)/MinGW64/dll/libusb-1.0.dll else DLL_LIB_FFTW = lib/fftw-3.3.5-dll32/libfftw3f-3.dll - DLL_LIB_USB = lib/libusb-1.0.20/MinGW32/dll/libusb-1.0.dll + DLL_LIB_USB = $(LIBUSB_DIR)/MinGW32/dll/libusb-1.0.dll endif JNAERATOR = $(CURDIR)/lib/jnaerator/jnaerator-0.13-20150328.111636-4-shaded.jar ZIP_FILE = hackrf_spectrum_analyzer.zip -ZIP_FILE_PATH = $(OUTPUT_DIR)/../../$(ZIP_FILE) +ZIP_FILE_PATH = $(OUTPUT_DLL_DIR)/../../$(ZIP_FILE) .NOTPARALLEL: all @@ -122,16 +125,16 @@ dirs: $(ZIP_FILE_PATH): $(DLL_LIB) mkdir -p $(RELEASE_DIR) - cd $(OUTPUT_DIR)/../../ && rm -rf $(ZIP_FILE) && zip -r $(ZIP_FILE) * + cd $(OUTPUT_DLL_DIR)/../../ && rm -rf $(ZIP_FILE) && zip -r $(ZIP_FILE) * yes | cp -rf $(ZIP_FILE_PATH) $(RELEASE_DIR) #cd $(CURDIR) .PHONY: prepare prepare: dirs $(OBJECTS) $(DLL_LIB_FFTW) - mkdir -p $(OUTPUT_DIR) - cp -f $(DLL_LIB_FFTW) $(DLL_LIB_PTHREAD) $(DLL_JNA) $(DLL_LIB_USB) $(OUTPUT_DIR) - cp -f lib/zadig_2.2.exe lib/program.ico lib/program.png $(OUTPUT_DIR)/../ + mkdir -p $(OUTPUT_DLL_DIR) + cp -f $(DLL_LIB_FFTW) $(DLL_LIB_PTHREAD) $(DLL_JNA) $(DLL_LIB_USB) $(OUTPUT_DLL_DIR) + cp -f lib/zadig_2.2.exe lib/program.ico lib/program.png $(OUTPUT_DLL_DIR)/../ $(DLL_LIB): $(OBJECTS) $(JNA_LIB) echo "building " $(DLL_LIB) @@ -153,6 +156,6 @@ $(BUILD_PATH)/%.c.$(OBJECT_SUFFIX): %.c .PHONY: clean clean: - rm -f $(OBJECTS) $(DLL_LIB) #$(JNA_LIB) - rm -rf $(OUTPUT_DIR) $(BUILD_PATH) + rm -f $(MAIN_JAR) $(OBJECTS) $(DLL_LIB) #$(JNA_LIB) + rm -rf $(OUTPUT_DLL_DIR) $(BUILD_PATH) diff --git a/src/hackrf-sweep/lib/libusb-1.0.20/MinGW32/dll/libusb-1.0.dll b/src/hackrf-sweep/lib/libusb-1.0.20/MinGW32/dll/libusb-1.0.dll deleted file mode 100644 index a2e99dd..0000000 Binary files a/src/hackrf-sweep/lib/libusb-1.0.20/MinGW32/dll/libusb-1.0.dll and /dev/null differ diff --git a/src/hackrf-sweep/lib/libusb-1.0.20/MinGW64/dll/libusb-1.0.dll b/src/hackrf-sweep/lib/libusb-1.0.20/MinGW64/dll/libusb-1.0.dll deleted file mode 100644 index 1330ae4..0000000 Binary files a/src/hackrf-sweep/lib/libusb-1.0.20/MinGW64/dll/libusb-1.0.dll and /dev/null differ diff --git a/src/hackrf-sweep/lib/libusb-1.0.20/MinGW64/static/libusb-1.0.a b/src/hackrf-sweep/lib/libusb-1.0.20/MinGW64/static/libusb-1.0.a deleted file mode 100644 index 7d3bb7b..0000000 Binary files a/src/hackrf-sweep/lib/libusb-1.0.20/MinGW64/static/libusb-1.0.a and /dev/null differ diff --git a/src/hackrf-sweep/lib/libusb-1.0.20/libusb-1.0.def b/src/hackrf-sweep/lib/libusb-1.0.20/libusb-1.0.def deleted file mode 100644 index c0b08a7..0000000 --- a/src/hackrf-sweep/lib/libusb-1.0.20/libusb-1.0.def +++ /dev/null @@ -1,168 +0,0 @@ -LIBRARY "libusb-1.0.dll" -EXPORTS - libusb_alloc_streams - libusb_alloc_streams@16 = libusb_alloc_streams - libusb_alloc_transfer - libusb_alloc_transfer@4 = libusb_alloc_transfer - libusb_attach_kernel_driver - libusb_attach_kernel_driver@8 = libusb_attach_kernel_driver - libusb_bulk_transfer - libusb_bulk_transfer@24 = libusb_bulk_transfer - libusb_cancel_transfer - libusb_cancel_transfer@4 = libusb_cancel_transfer - libusb_claim_interface - libusb_claim_interface@8 = libusb_claim_interface - libusb_clear_halt - libusb_clear_halt@8 = libusb_clear_halt - libusb_close - libusb_close@4 = libusb_close - libusb_control_transfer - libusb_control_transfer@32 = libusb_control_transfer - libusb_detach_kernel_driver - libusb_detach_kernel_driver@8 = libusb_detach_kernel_driver - libusb_error_name - libusb_error_name@4 = libusb_error_name - libusb_event_handler_active - libusb_event_handler_active@4 = libusb_event_handler_active - libusb_event_handling_ok - libusb_event_handling_ok@4 = libusb_event_handling_ok - libusb_exit - libusb_exit@4 = libusb_exit - libusb_free_bos_descriptor - libusb_free_bos_descriptor@4 = libusb_free_bos_descriptor - libusb_free_config_descriptor - libusb_free_config_descriptor@4 = libusb_free_config_descriptor - libusb_free_container_id_descriptor - libusb_free_container_id_descriptor@4 = libusb_free_container_id_descriptor - libusb_free_device_list - libusb_free_device_list@8 = libusb_free_device_list - libusb_free_ss_endpoint_companion_descriptor - libusb_free_ss_endpoint_companion_descriptor@4 = libusb_free_ss_endpoint_companion_descriptor - libusb_free_ss_usb_device_capability_descriptor - libusb_free_ss_usb_device_capability_descriptor@4 = libusb_free_ss_usb_device_capability_descriptor - libusb_free_streams - libusb_free_streams@12 = libusb_free_streams - libusb_free_transfer - libusb_free_transfer@4 = libusb_free_transfer - libusb_free_usb_2_0_extension_descriptor - libusb_free_usb_2_0_extension_descriptor@4 = libusb_free_usb_2_0_extension_descriptor - libusb_get_active_config_descriptor - libusb_get_active_config_descriptor@8 = libusb_get_active_config_descriptor - libusb_get_bos_descriptor - libusb_get_bos_descriptor@8 = libusb_get_bos_descriptor - libusb_get_bus_number - libusb_get_bus_number@4 = libusb_get_bus_number - libusb_get_config_descriptor - libusb_get_config_descriptor@12 = libusb_get_config_descriptor - libusb_get_config_descriptor_by_value - libusb_get_config_descriptor_by_value@12 = libusb_get_config_descriptor_by_value - libusb_get_configuration - libusb_get_configuration@8 = libusb_get_configuration - libusb_get_container_id_descriptor - libusb_get_container_id_descriptor@12 = libusb_get_container_id_descriptor - libusb_get_device - libusb_get_device@4 = libusb_get_device - libusb_get_device_address - libusb_get_device_address@4 = libusb_get_device_address - libusb_get_device_descriptor - libusb_get_device_descriptor@8 = libusb_get_device_descriptor - libusb_get_device_list - libusb_get_device_list@8 = libusb_get_device_list - libusb_get_device_speed - libusb_get_device_speed@4 = libusb_get_device_speed - libusb_get_max_iso_packet_size - libusb_get_max_iso_packet_size@8 = libusb_get_max_iso_packet_size - libusb_get_max_packet_size - libusb_get_max_packet_size@8 = libusb_get_max_packet_size - libusb_get_next_timeout - libusb_get_next_timeout@8 = libusb_get_next_timeout - libusb_get_parent - libusb_get_parent@4 = libusb_get_parent - libusb_get_pollfds - libusb_get_pollfds@4 = libusb_get_pollfds - libusb_free_pollfds - libusb_free_pollfds@4 = libusb_free_pollfds - libusb_get_port_number - libusb_get_port_number@4 = libusb_get_port_number - libusb_get_port_numbers - libusb_get_port_numbers@12 = libusb_get_port_numbers - libusb_get_port_path - libusb_get_port_path@16 = libusb_get_port_path - libusb_get_ss_endpoint_companion_descriptor - libusb_get_ss_endpoint_companion_descriptor@12 = libusb_get_ss_endpoint_companion_descriptor - libusb_get_ss_usb_device_capability_descriptor - libusb_get_ss_usb_device_capability_descriptor@12 = libusb_get_ss_usb_device_capability_descriptor - libusb_get_string_descriptor_ascii - libusb_get_string_descriptor_ascii@16 = libusb_get_string_descriptor_ascii - libusb_get_usb_2_0_extension_descriptor - libusb_get_usb_2_0_extension_descriptor@12 = libusb_get_usb_2_0_extension_descriptor - libusb_get_version - libusb_get_version@0 = libusb_get_version - libusb_handle_events - libusb_handle_events@4 = libusb_handle_events - libusb_handle_events_completed - libusb_handle_events_completed@8 = libusb_handle_events_completed - libusb_handle_events_locked - libusb_handle_events_locked@8 = libusb_handle_events_locked - libusb_handle_events_timeout - libusb_handle_events_timeout@8 = libusb_handle_events_timeout - libusb_handle_events_timeout_completed - libusb_handle_events_timeout_completed@12 = libusb_handle_events_timeout_completed - libusb_has_capability - libusb_has_capability@4 = libusb_has_capability - libusb_hotplug_deregister_callback - libusb_hotplug_deregister_callback@8 = libusb_hotplug_deregister_callback - libusb_hotplug_register_callback - libusb_hotplug_register_callback@36 = libusb_hotplug_register_callback - libusb_init - libusb_init@4 = libusb_init - libusb_interrupt_transfer - libusb_interrupt_transfer@24 = libusb_interrupt_transfer - libusb_kernel_driver_active - libusb_kernel_driver_active@8 = libusb_kernel_driver_active - libusb_lock_event_waiters - libusb_lock_event_waiters@4 = libusb_lock_event_waiters - libusb_lock_events - libusb_lock_events@4 = libusb_lock_events - libusb_open - libusb_open@8 = libusb_open - libusb_open_device_with_vid_pid - libusb_open_device_with_vid_pid@12 = libusb_open_device_with_vid_pid - libusb_pollfds_handle_timeouts - libusb_pollfds_handle_timeouts@4 = libusb_pollfds_handle_timeouts - libusb_ref_device - libusb_ref_device@4 = libusb_ref_device - libusb_release_interface - libusb_release_interface@8 = libusb_release_interface - libusb_reset_device - libusb_reset_device@4 = libusb_reset_device - libusb_set_auto_detach_kernel_driver - libusb_set_auto_detach_kernel_driver@8 = libusb_set_auto_detach_kernel_driver - libusb_set_configuration - libusb_set_configuration@8 = libusb_set_configuration - libusb_set_debug - libusb_set_debug@8 = libusb_set_debug - libusb_set_interface_alt_setting - libusb_set_interface_alt_setting@12 = libusb_set_interface_alt_setting - libusb_set_pollfd_notifiers - libusb_set_pollfd_notifiers@16 = libusb_set_pollfd_notifiers - libusb_setlocale - libusb_setlocale@4 = libusb_setlocale - libusb_strerror - libusb_strerror@4 = libusb_strerror - libusb_submit_transfer - libusb_submit_transfer@4 = libusb_submit_transfer - libusb_transfer_get_stream_id - libusb_transfer_get_stream_id@4 = libusb_transfer_get_stream_id - libusb_transfer_set_stream_id - libusb_transfer_set_stream_id@8 = libusb_transfer_set_stream_id - libusb_try_lock_events - libusb_try_lock_events@4 = libusb_try_lock_events - libusb_unlock_event_waiters - libusb_unlock_event_waiters@4 = libusb_unlock_event_waiters - libusb_unlock_events - libusb_unlock_events@4 = libusb_unlock_events - libusb_unref_device - libusb_unref_device@4 = libusb_unref_device - libusb_wait_for_event - libusb_wait_for_event@8 = libusb_wait_for_event diff --git a/src/hackrf-sweep/lib/libusb-1.0.21/MinGW32/dll/libusb-1.0.dll b/src/hackrf-sweep/lib/libusb-1.0.21/MinGW32/dll/libusb-1.0.dll new file mode 100644 index 0000000..a9803c1 Binary files /dev/null and b/src/hackrf-sweep/lib/libusb-1.0.21/MinGW32/dll/libusb-1.0.dll differ diff --git a/src/hackrf-sweep/lib/libusb-1.0.20/MinGW32/dll/libusb-1.0.dll.a b/src/hackrf-sweep/lib/libusb-1.0.21/MinGW32/dll/libusb-1.0.dll.a similarity index 71% rename from src/hackrf-sweep/lib/libusb-1.0.20/MinGW32/dll/libusb-1.0.dll.a rename to src/hackrf-sweep/lib/libusb-1.0.21/MinGW32/dll/libusb-1.0.dll.a index 5131b51..fc6c5eb 100644 Binary files a/src/hackrf-sweep/lib/libusb-1.0.20/MinGW32/dll/libusb-1.0.dll.a and b/src/hackrf-sweep/lib/libusb-1.0.21/MinGW32/dll/libusb-1.0.dll.a differ diff --git a/src/hackrf-sweep/lib/libusb-1.0.21/MinGW32/static/libusb-1.0.a b/src/hackrf-sweep/lib/libusb-1.0.21/MinGW32/static/libusb-1.0.a new file mode 100644 index 0000000..c6120a3 Binary files /dev/null and b/src/hackrf-sweep/lib/libusb-1.0.21/MinGW32/static/libusb-1.0.a differ diff --git a/src/hackrf-sweep/lib/libusb-1.0.21/MinGW64/dll/libusb-1.0.dll b/src/hackrf-sweep/lib/libusb-1.0.21/MinGW64/dll/libusb-1.0.dll new file mode 100644 index 0000000..f02d3b7 Binary files /dev/null and b/src/hackrf-sweep/lib/libusb-1.0.21/MinGW64/dll/libusb-1.0.dll differ diff --git a/src/hackrf-sweep/lib/libusb-1.0.20/MinGW64/dll/libusb-1.0.dll.a b/src/hackrf-sweep/lib/libusb-1.0.21/MinGW64/dll/libusb-1.0.dll.a similarity index 74% rename from src/hackrf-sweep/lib/libusb-1.0.20/MinGW64/dll/libusb-1.0.dll.a rename to src/hackrf-sweep/lib/libusb-1.0.21/MinGW64/dll/libusb-1.0.dll.a index 66bae34..86b67a5 100644 Binary files a/src/hackrf-sweep/lib/libusb-1.0.20/MinGW64/dll/libusb-1.0.dll.a and b/src/hackrf-sweep/lib/libusb-1.0.21/MinGW64/dll/libusb-1.0.dll.a differ diff --git a/src/hackrf-sweep/lib/libusb-1.0.21/MinGW64/static/libusb-1.0.a b/src/hackrf-sweep/lib/libusb-1.0.21/MinGW64/static/libusb-1.0.a new file mode 100644 index 0000000..847c130 Binary files /dev/null and b/src/hackrf-sweep/lib/libusb-1.0.21/MinGW64/static/libusb-1.0.a differ diff --git a/src/hackrf-sweep/lib/libusb-1.0.20/README.txt b/src/hackrf-sweep/lib/libusb-1.0.21/README.txt similarity index 98% rename from src/hackrf-sweep/lib/libusb-1.0.20/README.txt rename to src/hackrf-sweep/lib/libusb-1.0.21/README.txt index 6231f6b..2ad8395 100644 --- a/src/hackrf-sweep/lib/libusb-1.0.20/README.txt +++ b/src/hackrf-sweep/lib/libusb-1.0.21/README.txt @@ -1,61 +1,61 @@ - libusb 1.0 Windows binary snapshot - README - - ********************************************************************* - * The latest version of this snapshot can always be downloaded at: * - * https://sourceforge.net/projects/libusb/files/ * - ********************************************************************* - -o Visual Studio: - - Open existing or create a new project for your application - - Copy libusb.h, from the include\libusb-1.0\ directory, into your project and - make sure that the location where the file reside appears in the 'Additional - Include Directories' section (Configuration Properties -> C/C++ -> General). - - Copy the relevant .lib file from MS32\ or MS64\ and add 'libusb-1.0.lib' to - your 'Additional Dependencies' (Configuration Properties -> Linker -> Input) - Also make sure that the directory where libusb-1.0.lib resides is added to - 'Additional Library Directories' (Configuration Properties -> Linker - -> General) - - If you use the static version of the libusb library, make sure that - 'Runtime Library' is set to 'Multi-threaded DLL (/MD)' (Configuration - Properties -> C/C++ -> Code Generation). - NB: If your application requires /MT (Multi-threaded/libCMT), you need to - recompile a static libusb 1.0 library from source. - - Compile and run your application. If you use the DLL version of libusb-1.0, - remember that you need to have a copy of the DLL either in the runtime - directory or in system32 - -o WDK/DDK: - - The following is an example of a sources files that you can use to compile - a libusb 1.0 based console application. In this sample ..\libusb\ is the - directory where you would have copied libusb.h as well as the relevant - libusb-1.0.lib - - TARGETNAME=your_app - TARGETTYPE=PROGRAM - USE_MSVCRT=1 - UMTYPE=console - INCLUDES=..\libusb;$(DDK_INC_PATH) - TARGETLIBS=..\libusb\libusb-1.0.lib - SOURCES=your_app.c - - - Note that if you plan to use libCMT instead of MSVCRT (USE_LIBCMT=1 instead - of USE_MSVCRT=1), you will need to recompile libusb to use libCMT. This can - easily be achieved, in the DDK environment, by running 'ddk_build /MT' - -o MinGW/cygwin - - Copy libusb.h, from include/libusb-1.0/ to your default include directory, - and copy the MinGW32/ or MinGW64/ .a files to your default library directory. - Or, if you don't want to use the default locations, make sure that you feed - the relevant -I and -L options to the compiler. - - Add the '-lusb-1.0' linker option when compiling. - -o Additional information: - - The libusb 1.0 API documentation can be accessed at: - http://api.libusb.info - - For some libusb samples (including source), please have a look in examples/ - - For additional information on the libusb 1.0 Windows backend please visit: - http://windows.libusb.info - - The MinGW and MS generated DLLs are fully interchangeable, provided that you - use the import libs provided or generate one from the .def also provided. - - If you find any issue, please visit http://libusb.info/ and check the - Support section + libusb 1.0 Windows binary snapshot - README + + ********************************************************************* + * The latest version of this snapshot can always be downloaded at: * + * https://sourceforge.net/projects/libusb/files/ * + ********************************************************************* + +o Visual Studio: + - Open existing or create a new project for your application + - Copy libusb.h, from the include\libusb-1.0\ directory, into your project and + make sure that the location where the file reside appears in the 'Additional + Include Directories' section (Configuration Properties -> C/C++ -> General). + - Copy the relevant .lib file from MS32\ or MS64\ and add 'libusb-1.0.lib' to + your 'Additional Dependencies' (Configuration Properties -> Linker -> Input) + Also make sure that the directory where libusb-1.0.lib resides is added to + 'Additional Library Directories' (Configuration Properties -> Linker + -> General) + - If you use the static version of the libusb library, make sure that + 'Runtime Library' is set to 'Multi-threaded DLL (/MD)' (Configuration + Properties -> C/C++ -> Code Generation). + NB: If your application requires /MT (Multi-threaded/libCMT), you need to + recompile a static libusb 1.0 library from source. + - Compile and run your application. If you use the DLL version of libusb-1.0, + remember that you need to have a copy of the DLL either in the runtime + directory or in system32 + +o WDK/DDK: + - The following is an example of a sources files that you can use to compile + a libusb 1.0 based console application. In this sample ..\libusb\ is the + directory where you would have copied libusb.h as well as the relevant + libusb-1.0.lib + + TARGETNAME=your_app + TARGETTYPE=PROGRAM + USE_MSVCRT=1 + UMTYPE=console + INCLUDES=..\libusb;$(DDK_INC_PATH) + TARGETLIBS=..\libusb\libusb-1.0.lib + SOURCES=your_app.c + + - Note that if you plan to use libCMT instead of MSVCRT (USE_LIBCMT=1 instead + of USE_MSVCRT=1), you will need to recompile libusb to use libCMT. This can + easily be achieved, in the DDK environment, by running 'ddk_build /MT' + +o MinGW/cygwin + - Copy libusb.h, from include/libusb-1.0/ to your default include directory, + and copy the MinGW32/ or MinGW64/ .a files to your default library directory. + Or, if you don't want to use the default locations, make sure that you feed + the relevant -I and -L options to the compiler. + - Add the '-lusb-1.0' linker option when compiling. + +o Additional information: + - The libusb 1.0 API documentation can be accessed at: + http://api.libusb.info + - For some libusb samples (including source), please have a look in examples/ + - For additional information on the libusb 1.0 Windows backend please visit: + http://windows.libusb.info + - The MinGW and MS generated DLLs are fully interchangeable, provided that you + use the import libs provided or generate one from the .def also provided. + - If you find any issue, please visit http://libusb.info/ and check the + Support section diff --git a/src/hackrf-sweep/lib/libusb-1.0.20/include/libusb-1.0/libusb.h b/src/hackrf-sweep/lib/libusb-1.0.21/include/libusb-1.0/libusb.h similarity index 94% rename from src/hackrf-sweep/lib/libusb-1.0.20/include/libusb-1.0/libusb.h rename to src/hackrf-sweep/lib/libusb-1.0.21/include/libusb-1.0/libusb.h index c165a76..f73e31c 100644 --- a/src/hackrf-sweep/lib/libusb-1.0.20/include/libusb-1.0/libusb.h +++ b/src/hackrf-sweep/lib/libusb-1.0.21/include/libusb-1.0/libusb.h @@ -1,1999 +1,2008 @@ -/* - * Public libusb header file - * Copyright © 2001 Johannes Erdfelt - * Copyright © 2007-2008 Daniel Drake - * Copyright © 2012 Pete Batard - * Copyright © 2012 Nathan Hjelm - * For more information, please visit: http://libusb.info - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef LIBUSB_H -#define LIBUSB_H - -#ifdef _MSC_VER -/* on MS environments, the inline keyword is available in C++ only */ -#if !defined(__cplusplus) -#define inline __inline -#endif -/* ssize_t is also not available (copy/paste from MinGW) */ -#ifndef _SSIZE_T_DEFINED -#define _SSIZE_T_DEFINED -#undef ssize_t -#ifdef _WIN64 - typedef __int64 ssize_t; -#else - typedef int ssize_t; -#endif /* _WIN64 */ -#endif /* _SSIZE_T_DEFINED */ -#endif /* _MSC_VER */ - -/* stdint.h is not available on older MSVC */ -#if defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H)) -typedef unsigned __int8 uint8_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int32 uint32_t; -#else -#include -#endif - -#if !defined(_WIN32_WCE) -#include -#endif - -#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__) -#include -#endif - -#include -#include - -/* 'interface' might be defined as a macro on Windows, so we need to - * undefine it so as not to break the current libusb API, because - * libusb_config_descriptor has an 'interface' member - * As this can be problematic if you include windows.h after libusb.h - * in your sources, we force windows.h to be included first. */ -#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) -#include -#if defined(interface) -#undef interface -#endif -#if !defined(__CYGWIN__) -#include -#endif -#endif - -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) -#define LIBUSB_DEPRECATED_FOR(f) \ - __attribute__((deprecated("Use " #f " instead"))) -#else -#define LIBUSB_DEPRECATED_FOR(f) -#endif /* __GNUC__ */ - -/** \def LIBUSB_CALL - * \ingroup misc - * libusb's Windows calling convention. - * - * Under Windows, the selection of available compilers and configurations - * means that, unlike other platforms, there is not one true calling - * convention (calling convention: the manner in which parameters are - * passed to functions in the generated assembly code). - * - * Matching the Windows API itself, libusb uses the WINAPI convention (which - * translates to the stdcall convention) and guarantees that the - * library is compiled in this way. The public header file also includes - * appropriate annotations so that your own software will use the right - * convention, even if another convention is being used by default within - * your codebase. - * - * The one consideration that you must apply in your software is to mark - * all functions which you use as libusb callbacks with this LIBUSB_CALL - * annotation, so that they too get compiled for the correct calling - * convention. - * - * On non-Windows operating systems, this macro is defined as nothing. This - * means that you can apply it to your code without worrying about - * cross-platform compatibility. - */ -/* LIBUSB_CALL must be defined on both definition and declaration of libusb - * functions. You'd think that declaration would be enough, but cygwin will - * complain about conflicting types unless both are marked this way. - * The placement of this macro is important too; it must appear after the - * return type, before the function name. See internal documentation for - * API_EXPORTED. - */ -#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) -#define LIBUSB_CALL WINAPI -#else -#define LIBUSB_CALL -#endif - -/** \def LIBUSB_API_VERSION - * \ingroup misc - * libusb's API version. - * - * Since version 1.0.13, to help with feature detection, libusb defines - * a LIBUSB_API_VERSION macro that gets increased every time there is a - * significant change to the API, such as the introduction of a new call, - * the definition of a new macro/enum member, or any other element that - * libusb applications may want to detect at compilation time. - * - * The macro is typically used in an application as follows: - * \code - * #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01001234) - * // Use one of the newer features from the libusb API - * #endif - * \endcode - * - * Internally, LIBUSB_API_VERSION is defined as follows: - * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental) - */ -#define LIBUSB_API_VERSION 0x01000104 - -/* The following is kept for compatibility, but will be deprecated in the future */ -#define LIBUSBX_API_VERSION LIBUSB_API_VERSION - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \ingroup misc - * Convert a 16-bit value from host-endian to little-endian format. On - * little endian systems, this function does nothing. On big endian systems, - * the bytes are swapped. - * \param x the host-endian value to convert - * \returns the value in little-endian byte order - */ -static inline uint16_t libusb_cpu_to_le16(const uint16_t x) -{ - union { - uint8_t b8[2]; - uint16_t b16; - } _tmp; - _tmp.b8[1] = (uint8_t) (x >> 8); - _tmp.b8[0] = (uint8_t) (x & 0xff); - return _tmp.b16; -} - -/** \def libusb_le16_to_cpu - * \ingroup misc - * Convert a 16-bit value from little-endian to host-endian format. On - * little endian systems, this function does nothing. On big endian systems, - * the bytes are swapped. - * \param x the little-endian value to convert - * \returns the value in host-endian byte order - */ -#define libusb_le16_to_cpu libusb_cpu_to_le16 - -/* standard USB stuff */ - -/** \ingroup desc - * Device and/or Interface Class codes */ -enum libusb_class_code { - /** In the context of a \ref libusb_device_descriptor "device descriptor", - * this bDeviceClass value indicates that each interface specifies its - * own class information and all interfaces operate independently. - */ - LIBUSB_CLASS_PER_INTERFACE = 0, - - /** Audio class */ - LIBUSB_CLASS_AUDIO = 1, - - /** Communications class */ - LIBUSB_CLASS_COMM = 2, - - /** Human Interface Device class */ - LIBUSB_CLASS_HID = 3, - - /** Physical */ - LIBUSB_CLASS_PHYSICAL = 5, - - /** Printer class */ - LIBUSB_CLASS_PRINTER = 7, - - /** Image class */ - LIBUSB_CLASS_PTP = 6, /* legacy name from libusb-0.1 usb.h */ - LIBUSB_CLASS_IMAGE = 6, - - /** Mass storage class */ - LIBUSB_CLASS_MASS_STORAGE = 8, - - /** Hub class */ - LIBUSB_CLASS_HUB = 9, - - /** Data class */ - LIBUSB_CLASS_DATA = 10, - - /** Smart Card */ - LIBUSB_CLASS_SMART_CARD = 0x0b, - - /** Content Security */ - LIBUSB_CLASS_CONTENT_SECURITY = 0x0d, - - /** Video */ - LIBUSB_CLASS_VIDEO = 0x0e, - - /** Personal Healthcare */ - LIBUSB_CLASS_PERSONAL_HEALTHCARE = 0x0f, - - /** Diagnostic Device */ - LIBUSB_CLASS_DIAGNOSTIC_DEVICE = 0xdc, - - /** Wireless class */ - LIBUSB_CLASS_WIRELESS = 0xe0, - - /** Application class */ - LIBUSB_CLASS_APPLICATION = 0xfe, - - /** Class is vendor-specific */ - LIBUSB_CLASS_VENDOR_SPEC = 0xff -}; - -/** \ingroup desc - * Descriptor types as defined by the USB specification. */ -enum libusb_descriptor_type { - /** Device descriptor. See libusb_device_descriptor. */ - LIBUSB_DT_DEVICE = 0x01, - - /** Configuration descriptor. See libusb_config_descriptor. */ - LIBUSB_DT_CONFIG = 0x02, - - /** String descriptor */ - LIBUSB_DT_STRING = 0x03, - - /** Interface descriptor. See libusb_interface_descriptor. */ - LIBUSB_DT_INTERFACE = 0x04, - - /** Endpoint descriptor. See libusb_endpoint_descriptor. */ - LIBUSB_DT_ENDPOINT = 0x05, - - /** BOS descriptor */ - LIBUSB_DT_BOS = 0x0f, - - /** Device Capability descriptor */ - LIBUSB_DT_DEVICE_CAPABILITY = 0x10, - - /** HID descriptor */ - LIBUSB_DT_HID = 0x21, - - /** HID report descriptor */ - LIBUSB_DT_REPORT = 0x22, - - /** Physical descriptor */ - LIBUSB_DT_PHYSICAL = 0x23, - - /** Hub descriptor */ - LIBUSB_DT_HUB = 0x29, - - /** SuperSpeed Hub descriptor */ - LIBUSB_DT_SUPERSPEED_HUB = 0x2a, - - /** SuperSpeed Endpoint Companion descriptor */ - LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30 -}; - -/* Descriptor sizes per descriptor type */ -#define LIBUSB_DT_DEVICE_SIZE 18 -#define LIBUSB_DT_CONFIG_SIZE 9 -#define LIBUSB_DT_INTERFACE_SIZE 9 -#define LIBUSB_DT_ENDPOINT_SIZE 7 -#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ -#define LIBUSB_DT_HUB_NONVAR_SIZE 7 -#define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE 6 -#define LIBUSB_DT_BOS_SIZE 5 -#define LIBUSB_DT_DEVICE_CAPABILITY_SIZE 3 - -/* BOS descriptor sizes */ -#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7 -#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10 -#define LIBUSB_BT_CONTAINER_ID_SIZE 20 - -/* We unwrap the BOS => define its max size */ -#define LIBUSB_DT_BOS_MAX_SIZE ((LIBUSB_DT_BOS_SIZE) +\ - (LIBUSB_BT_USB_2_0_EXTENSION_SIZE) +\ - (LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) +\ - (LIBUSB_BT_CONTAINER_ID_SIZE)) - -#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ -#define LIBUSB_ENDPOINT_DIR_MASK 0x80 - -/** \ingroup desc - * Endpoint direction. Values for bit 7 of the - * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. - */ -enum libusb_endpoint_direction { - /** In: device-to-host */ - LIBUSB_ENDPOINT_IN = 0x80, - - /** Out: host-to-device */ - LIBUSB_ENDPOINT_OUT = 0x00 -}; - -#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ - -/** \ingroup desc - * Endpoint transfer type. Values for bits 0:1 of the - * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. - */ -enum libusb_transfer_type { - /** Control endpoint */ - LIBUSB_TRANSFER_TYPE_CONTROL = 0, - - /** Isochronous endpoint */ - LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, - - /** Bulk endpoint */ - LIBUSB_TRANSFER_TYPE_BULK = 2, - - /** Interrupt endpoint */ - LIBUSB_TRANSFER_TYPE_INTERRUPT = 3, - - /** Stream endpoint */ - LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4, -}; - -/** \ingroup misc - * Standard requests, as defined in table 9-5 of the USB 3.0 specifications */ -enum libusb_standard_request { - /** Request status of the specific recipient */ - LIBUSB_REQUEST_GET_STATUS = 0x00, - - /** Clear or disable a specific feature */ - LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, - - /* 0x02 is reserved */ - - /** Set or enable a specific feature */ - LIBUSB_REQUEST_SET_FEATURE = 0x03, - - /* 0x04 is reserved */ - - /** Set device address for all future accesses */ - LIBUSB_REQUEST_SET_ADDRESS = 0x05, - - /** Get the specified descriptor */ - LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, - - /** Used to update existing descriptors or add new descriptors */ - LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, - - /** Get the current device configuration value */ - LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, - - /** Set device configuration */ - LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, - - /** Return the selected alternate setting for the specified interface */ - LIBUSB_REQUEST_GET_INTERFACE = 0x0A, - - /** Select an alternate interface for the specified interface */ - LIBUSB_REQUEST_SET_INTERFACE = 0x0B, - - /** Set then report an endpoint's synchronization frame */ - LIBUSB_REQUEST_SYNCH_FRAME = 0x0C, - - /** Sets both the U1 and U2 Exit Latency */ - LIBUSB_REQUEST_SET_SEL = 0x30, - - /** Delay from the time a host transmits a packet to the time it is - * received by the device. */ - LIBUSB_SET_ISOCH_DELAY = 0x31, -}; - -/** \ingroup misc - * Request type bits of the - * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control - * transfers. */ -enum libusb_request_type { - /** Standard */ - LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), - - /** Class */ - LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), - - /** Vendor */ - LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), - - /** Reserved */ - LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5) -}; - -/** \ingroup misc - * Recipient bits of the - * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control - * transfers. Values 4 through 31 are reserved. */ -enum libusb_request_recipient { - /** Device */ - LIBUSB_RECIPIENT_DEVICE = 0x00, - - /** Interface */ - LIBUSB_RECIPIENT_INTERFACE = 0x01, - - /** Endpoint */ - LIBUSB_RECIPIENT_ENDPOINT = 0x02, - - /** Other */ - LIBUSB_RECIPIENT_OTHER = 0x03, -}; - -#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C - -/** \ingroup desc - * Synchronization type for isochronous endpoints. Values for bits 2:3 of the - * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in - * libusb_endpoint_descriptor. - */ -enum libusb_iso_sync_type { - /** No synchronization */ - LIBUSB_ISO_SYNC_TYPE_NONE = 0, - - /** Asynchronous */ - LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, - - /** Adaptive */ - LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, - - /** Synchronous */ - LIBUSB_ISO_SYNC_TYPE_SYNC = 3 -}; - -#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 - -/** \ingroup desc - * Usage type for isochronous endpoints. Values for bits 4:5 of the - * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in - * libusb_endpoint_descriptor. - */ -enum libusb_iso_usage_type { - /** Data endpoint */ - LIBUSB_ISO_USAGE_TYPE_DATA = 0, - - /** Feedback endpoint */ - LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, - - /** Implicit feedback Data endpoint */ - LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2, -}; - -/** \ingroup desc - * A structure representing the standard USB device descriptor. This - * descriptor is documented in section 9.6.1 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -struct libusb_device_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this - * context. */ - uint8_t bDescriptorType; - - /** USB specification release number in binary-coded decimal. A value of - * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ - uint16_t bcdUSB; - - /** USB-IF class code for the device. See \ref libusb_class_code. */ - uint8_t bDeviceClass; - - /** USB-IF subclass code for the device, qualified by the bDeviceClass - * value */ - uint8_t bDeviceSubClass; - - /** USB-IF protocol code for the device, qualified by the bDeviceClass and - * bDeviceSubClass values */ - uint8_t bDeviceProtocol; - - /** Maximum packet size for endpoint 0 */ - uint8_t bMaxPacketSize0; - - /** USB-IF vendor ID */ - uint16_t idVendor; - - /** USB-IF product ID */ - uint16_t idProduct; - - /** Device release number in binary-coded decimal */ - uint16_t bcdDevice; - - /** Index of string descriptor describing manufacturer */ - uint8_t iManufacturer; - - /** Index of string descriptor describing product */ - uint8_t iProduct; - - /** Index of string descriptor containing device serial number */ - uint8_t iSerialNumber; - - /** Number of possible configurations */ - uint8_t bNumConfigurations; -}; - -/** \ingroup desc - * A structure representing the standard USB endpoint descriptor. This - * descriptor is documented in section 9.6.6 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -struct libusb_endpoint_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in - * this context. */ - uint8_t bDescriptorType; - - /** The address of the endpoint described by this descriptor. Bits 0:3 are - * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, - * see \ref libusb_endpoint_direction. - */ - uint8_t bEndpointAddress; - - /** Attributes which apply to the endpoint when it is configured using - * the bConfigurationValue. Bits 0:1 determine the transfer type and - * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for - * isochronous endpoints and correspond to \ref libusb_iso_sync_type. - * Bits 4:5 are also only used for isochronous endpoints and correspond to - * \ref libusb_iso_usage_type. Bits 6:7 are reserved. - */ - uint8_t bmAttributes; - - /** Maximum packet size this endpoint is capable of sending/receiving. */ - uint16_t wMaxPacketSize; - - /** Interval for polling endpoint for data transfers. */ - uint8_t bInterval; - - /** For audio devices only: the rate at which synchronization feedback - * is provided. */ - uint8_t bRefresh; - - /** For audio devices only: the address if the synch endpoint */ - uint8_t bSynchAddress; - - /** Extra descriptors. If libusb encounters unknown endpoint descriptors, - * it will store them here, should you wish to parse them. */ - const unsigned char *extra; - - /** Length of the extra descriptors, in bytes. */ - int extra_length; -}; - -/** \ingroup desc - * A structure representing the standard USB interface descriptor. This - * descriptor is documented in section 9.6.5 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -struct libusb_interface_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE - * in this context. */ - uint8_t bDescriptorType; - - /** Number of this interface */ - uint8_t bInterfaceNumber; - - /** Value used to select this alternate setting for this interface */ - uint8_t bAlternateSetting; - - /** Number of endpoints used by this interface (excluding the control - * endpoint). */ - uint8_t bNumEndpoints; - - /** USB-IF class code for this interface. See \ref libusb_class_code. */ - uint8_t bInterfaceClass; - - /** USB-IF subclass code for this interface, qualified by the - * bInterfaceClass value */ - uint8_t bInterfaceSubClass; - - /** USB-IF protocol code for this interface, qualified by the - * bInterfaceClass and bInterfaceSubClass values */ - uint8_t bInterfaceProtocol; - - /** Index of string descriptor describing this interface */ - uint8_t iInterface; - - /** Array of endpoint descriptors. This length of this array is determined - * by the bNumEndpoints field. */ - const struct libusb_endpoint_descriptor *endpoint; - - /** Extra descriptors. If libusb encounters unknown interface descriptors, - * it will store them here, should you wish to parse them. */ - const unsigned char *extra; - - /** Length of the extra descriptors, in bytes. */ - int extra_length; -}; - -/** \ingroup desc - * A collection of alternate settings for a particular USB interface. - */ -struct libusb_interface { - /** Array of interface descriptors. The length of this array is determined - * by the num_altsetting field. */ - const struct libusb_interface_descriptor *altsetting; - - /** The number of alternate settings that belong to this interface */ - int num_altsetting; -}; - -/** \ingroup desc - * A structure representing the standard USB configuration descriptor. This - * descriptor is documented in section 9.6.3 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -struct libusb_config_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG - * in this context. */ - uint8_t bDescriptorType; - - /** Total length of data returned for this configuration */ - uint16_t wTotalLength; - - /** Number of interfaces supported by this configuration */ - uint8_t bNumInterfaces; - - /** Identifier value for this configuration */ - uint8_t bConfigurationValue; - - /** Index of string descriptor describing this configuration */ - uint8_t iConfiguration; - - /** Configuration characteristics */ - uint8_t bmAttributes; - - /** Maximum power consumption of the USB device from this bus in this - * configuration when the device is fully operation. Expressed in units - * of 2 mA when the device is operating in high-speed mode and in units - * of 8 mA when the device is operating in super-speed mode. */ - uint8_t MaxPower; - - /** Array of interfaces supported by this configuration. The length of - * this array is determined by the bNumInterfaces field. */ - const struct libusb_interface *interface; - - /** Extra descriptors. If libusb encounters unknown configuration - * descriptors, it will store them here, should you wish to parse them. */ - const unsigned char *extra; - - /** Length of the extra descriptors, in bytes. */ - int extra_length; -}; - -/** \ingroup desc - * A structure representing the superspeed endpoint companion - * descriptor. This descriptor is documented in section 9.6.7 of - * the USB 3.0 specification. All multiple-byte fields are represented in - * host-endian format. - */ -struct libusb_ss_endpoint_companion_descriptor { - - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in - * this context. */ - uint8_t bDescriptorType; - - - /** The maximum number of packets the endpoint can send or - * recieve as part of a burst. */ - uint8_t bMaxBurst; - - /** In bulk EP: bits 4:0 represents the maximum number of - * streams the EP supports. In isochronous EP: bits 1:0 - * represents the Mult - a zero based value that determines - * the maximum number of packets within a service interval */ - uint8_t bmAttributes; - - /** The total number of bytes this EP will transfer every - * service interval. valid only for periodic EPs. */ - uint16_t wBytesPerInterval; -}; - -/** \ingroup desc - * A generic representation of a BOS Device Capability descriptor. It is - * advised to check bDevCapabilityType and call the matching - * libusb_get_*_descriptor function to get a structure fully matching the type. - */ -struct libusb_bos_dev_capability_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY - * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ - uint8_t bDescriptorType; - /** Device Capability type */ - uint8_t bDevCapabilityType; - /** Device Capability data (bLength - 3 bytes) */ - uint8_t dev_capability_data -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - [] /* valid C99 code */ -#else - [0] /* non-standard, but usually working code */ -#endif - ; -}; - -/** \ingroup desc - * A structure representing the Binary Device Object Store (BOS) descriptor. - * This descriptor is documented in section 9.6.2 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -struct libusb_bos_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_BOS LIBUSB_DT_BOS - * in this context. */ - uint8_t bDescriptorType; - - /** Length of this descriptor and all of its sub descriptors */ - uint16_t wTotalLength; - - /** The number of separate device capability descriptors in - * the BOS */ - uint8_t bNumDeviceCaps; - - /** bNumDeviceCap Device Capability Descriptors */ - struct libusb_bos_dev_capability_descriptor *dev_capability -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - [] /* valid C99 code */ -#else - [0] /* non-standard, but usually working code */ -#endif - ; -}; - -/** \ingroup desc - * A structure representing the USB 2.0 Extension descriptor - * This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -struct libusb_usb_2_0_extension_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY - * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ - uint8_t bDescriptorType; - - /** Capability type. Will have value - * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION - * LIBUSB_BT_USB_2_0_EXTENSION in this context. */ - uint8_t bDevCapabilityType; - - /** Bitmap encoding of supported device level features. - * A value of one in a bit location indicates a feature is - * supported; a value of zero indicates it is not supported. - * See \ref libusb_usb_2_0_extension_attributes. */ - uint32_t bmAttributes; -}; - -/** \ingroup desc - * A structure representing the SuperSpeed USB Device Capability descriptor - * This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification. - * All multiple-byte fields are represented in host-endian format. - */ -struct libusb_ss_usb_device_capability_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY - * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ - uint8_t bDescriptorType; - - /** Capability type. Will have value - * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY - * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY in this context. */ - uint8_t bDevCapabilityType; - - /** Bitmap encoding of supported device level features. - * A value of one in a bit location indicates a feature is - * supported; a value of zero indicates it is not supported. - * See \ref libusb_ss_usb_device_capability_attributes. */ - uint8_t bmAttributes; - - /** Bitmap encoding of the speed supported by this device when - * operating in SuperSpeed mode. See \ref libusb_supported_speed. */ - uint16_t wSpeedSupported; - - /** The lowest speed at which all the functionality supported - * by the device is available to the user. For example if the - * device supports all its functionality when connected at - * full speed and above then it sets this value to 1. */ - uint8_t bFunctionalitySupport; - - /** U1 Device Exit Latency. */ - uint8_t bU1DevExitLat; - - /** U2 Device Exit Latency. */ - uint16_t bU2DevExitLat; -}; - -/** \ingroup desc - * A structure representing the Container ID descriptor. - * This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification. - * All multiple-byte fields, except UUIDs, are represented in host-endian format. - */ -struct libusb_container_id_descriptor { - /** Size of this descriptor (in bytes) */ - uint8_t bLength; - - /** Descriptor type. Will have value - * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY - * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ - uint8_t bDescriptorType; - - /** Capability type. Will have value - * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID - * LIBUSB_BT_CONTAINER_ID in this context. */ - uint8_t bDevCapabilityType; - - /** Reserved field */ - uint8_t bReserved; - - /** 128 bit UUID */ - uint8_t ContainerID[16]; -}; - -/** \ingroup asyncio - * Setup packet for control transfers. */ -struct libusb_control_setup { - /** Request type. Bits 0:4 determine recipient, see - * \ref libusb_request_recipient. Bits 5:6 determine type, see - * \ref libusb_request_type. Bit 7 determines data transfer direction, see - * \ref libusb_endpoint_direction. - */ - uint8_t bmRequestType; - - /** Request. If the type bits of bmRequestType are equal to - * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD - * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to - * \ref libusb_standard_request. For other cases, use of this field is - * application-specific. */ - uint8_t bRequest; - - /** Value. Varies according to request */ - uint16_t wValue; - - /** Index. Varies according to request, typically used to pass an index - * or offset */ - uint16_t wIndex; - - /** Number of bytes to transfer */ - uint16_t wLength; -}; - -#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) - -/* libusb */ - -struct libusb_context; -struct libusb_device; -struct libusb_device_handle; - -/** \ingroup lib - * Structure providing the version of the libusb runtime - */ -struct libusb_version { - /** Library major version. */ - const uint16_t major; - - /** Library minor version. */ - const uint16_t minor; - - /** Library micro version. */ - const uint16_t micro; - - /** Library nano version. */ - const uint16_t nano; - - /** Library release candidate suffix string, e.g. "-rc4". */ - const char *rc; - - /** For ABI compatibility only. */ - const char* describe; -}; - -/** \ingroup lib - * Structure representing a libusb session. The concept of individual libusb - * sessions allows for your program to use two libraries (or dynamically - * load two modules) which both independently use libusb. This will prevent - * interference between the individual libusb users - for example - * libusb_set_debug() will not affect the other user of the library, and - * libusb_exit() will not destroy resources that the other user is still - * using. - * - * Sessions are created by libusb_init() and destroyed through libusb_exit(). - * If your application is guaranteed to only ever include a single libusb - * user (i.e. you), you do not have to worry about contexts: pass NULL in - * every function call where a context is required. The default context - * will be used. - * - * For more information, see \ref contexts. - */ -typedef struct libusb_context libusb_context; - -/** \ingroup dev - * Structure representing a USB device detected on the system. This is an - * opaque type for which you are only ever provided with a pointer, usually - * originating from libusb_get_device_list(). - * - * Certain operations can be performed on a device, but in order to do any - * I/O you will have to first obtain a device handle using libusb_open(). - * - * Devices are reference counted with libusb_ref_device() and - * libusb_unref_device(), and are freed when the reference count reaches 0. - * New devices presented by libusb_get_device_list() have a reference count of - * 1, and libusb_free_device_list() can optionally decrease the reference count - * on all devices in the list. libusb_open() adds another reference which is - * later destroyed by libusb_close(). - */ -typedef struct libusb_device libusb_device; - - -/** \ingroup dev - * Structure representing a handle on a USB device. This is an opaque type for - * which you are only ever provided with a pointer, usually originating from - * libusb_open(). - * - * A device handle is used to perform I/O and other operations. When finished - * with a device handle, you should call libusb_close(). - */ -typedef struct libusb_device_handle libusb_device_handle; - -/** \ingroup dev - * Speed codes. Indicates the speed at which the device is operating. - */ -enum libusb_speed { - /** The OS doesn't report or know the device speed. */ - LIBUSB_SPEED_UNKNOWN = 0, - - /** The device is operating at low speed (1.5MBit/s). */ - LIBUSB_SPEED_LOW = 1, - - /** The device is operating at full speed (12MBit/s). */ - LIBUSB_SPEED_FULL = 2, - - /** The device is operating at high speed (480MBit/s). */ - LIBUSB_SPEED_HIGH = 3, - - /** The device is operating at super speed (5000MBit/s). */ - LIBUSB_SPEED_SUPER = 4, -}; - -/** \ingroup dev - * Supported speeds (wSpeedSupported) bitfield. Indicates what - * speeds the device supports. - */ -enum libusb_supported_speed { - /** Low speed operation supported (1.5MBit/s). */ - LIBUSB_LOW_SPEED_OPERATION = 1, - - /** Full speed operation supported (12MBit/s). */ - LIBUSB_FULL_SPEED_OPERATION = 2, - - /** High speed operation supported (480MBit/s). */ - LIBUSB_HIGH_SPEED_OPERATION = 4, - - /** Superspeed operation supported (5000MBit/s). */ - LIBUSB_SUPER_SPEED_OPERATION = 8, -}; - -/** \ingroup dev - * Masks for the bits of the - * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field - * of the USB 2.0 Extension descriptor. - */ -enum libusb_usb_2_0_extension_attributes { - /** Supports Link Power Management (LPM) */ - LIBUSB_BM_LPM_SUPPORT = 2, -}; - -/** \ingroup dev - * Masks for the bits of the - * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field - * field of the SuperSpeed USB Device Capability descriptor. - */ -enum libusb_ss_usb_device_capability_attributes { - /** Supports Latency Tolerance Messages (LTM) */ - LIBUSB_BM_LTM_SUPPORT = 2, -}; - -/** \ingroup dev - * USB capability types - */ -enum libusb_bos_type { - /** Wireless USB device capability */ - LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1, - - /** USB 2.0 extensions */ - LIBUSB_BT_USB_2_0_EXTENSION = 2, - - /** SuperSpeed USB device capability */ - LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 3, - - /** Container ID type */ - LIBUSB_BT_CONTAINER_ID = 4, -}; - -/** \ingroup misc - * Error codes. Most libusb functions return 0 on success or one of these - * codes on failure. - * You can call libusb_error_name() to retrieve a string representation of an - * error code or libusb_strerror() to get an end-user suitable description of - * an error code. - */ -enum libusb_error { - /** Success (no error) */ - LIBUSB_SUCCESS = 0, - - /** Input/output error */ - LIBUSB_ERROR_IO = -1, - - /** Invalid parameter */ - LIBUSB_ERROR_INVALID_PARAM = -2, - - /** Access denied (insufficient permissions) */ - LIBUSB_ERROR_ACCESS = -3, - - /** No such device (it may have been disconnected) */ - LIBUSB_ERROR_NO_DEVICE = -4, - - /** Entity not found */ - LIBUSB_ERROR_NOT_FOUND = -5, - - /** Resource busy */ - LIBUSB_ERROR_BUSY = -6, - - /** Operation timed out */ - LIBUSB_ERROR_TIMEOUT = -7, - - /** Overflow */ - LIBUSB_ERROR_OVERFLOW = -8, - - /** Pipe error */ - LIBUSB_ERROR_PIPE = -9, - - /** System call interrupted (perhaps due to signal) */ - LIBUSB_ERROR_INTERRUPTED = -10, - - /** Insufficient memory */ - LIBUSB_ERROR_NO_MEM = -11, - - /** Operation not supported or unimplemented on this platform */ - LIBUSB_ERROR_NOT_SUPPORTED = -12, - - /* NB: Remember to update LIBUSB_ERROR_COUNT below as well as the - message strings in strerror.c when adding new error codes here. */ - - /** Other error */ - LIBUSB_ERROR_OTHER = -99, -}; - -/* Total number of error codes in enum libusb_error */ -#define LIBUSB_ERROR_COUNT 14 - -/** \ingroup asyncio - * Transfer status codes */ -enum libusb_transfer_status { - /** Transfer completed without error. Note that this does not indicate - * that the entire amount of requested data was transferred. */ - LIBUSB_TRANSFER_COMPLETED, - - /** Transfer failed */ - LIBUSB_TRANSFER_ERROR, - - /** Transfer timed out */ - LIBUSB_TRANSFER_TIMED_OUT, - - /** Transfer was cancelled */ - LIBUSB_TRANSFER_CANCELLED, - - /** For bulk/interrupt endpoints: halt condition detected (endpoint - * stalled). For control endpoints: control request not supported. */ - LIBUSB_TRANSFER_STALL, - - /** Device was disconnected */ - LIBUSB_TRANSFER_NO_DEVICE, - - /** Device sent more data than requested */ - LIBUSB_TRANSFER_OVERFLOW, - - /* NB! Remember to update libusb_error_name() - when adding new status codes here. */ -}; - -/** \ingroup asyncio - * libusb_transfer.flags values */ -enum libusb_transfer_flags { - /** Report short frames as errors */ - LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0, - - /** Automatically free() transfer buffer during libusb_free_transfer() */ - LIBUSB_TRANSFER_FREE_BUFFER = 1<<1, - - /** Automatically call libusb_free_transfer() after callback returns. - * If this flag is set, it is illegal to call libusb_free_transfer() - * from your transfer callback, as this will result in a double-free - * when this flag is acted upon. */ - LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2, - - /** Terminate transfers that are a multiple of the endpoint's - * wMaxPacketSize with an extra zero length packet. This is useful - * when a device protocol mandates that each logical request is - * terminated by an incomplete packet (i.e. the logical requests are - * not separated by other means). - * - * This flag only affects host-to-device transfers to bulk and interrupt - * endpoints. In other situations, it is ignored. - * - * This flag only affects transfers with a length that is a multiple of - * the endpoint's wMaxPacketSize. On transfers of other lengths, this - * flag has no effect. Therefore, if you are working with a device that - * needs a ZLP whenever the end of the logical request falls on a packet - * boundary, then it is sensible to set this flag on every - * transfer (you do not have to worry about only setting it on transfers - * that end on the boundary). - * - * This flag is currently only supported on Linux. - * On other systems, libusb_submit_transfer() will return - * LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set. - * - * Available since libusb-1.0.9. - */ - LIBUSB_TRANSFER_ADD_ZERO_PACKET = 1 << 3, -}; - -/** \ingroup asyncio - * Isochronous packet descriptor. */ -struct libusb_iso_packet_descriptor { - /** Length of data to request in this packet */ - unsigned int length; - - /** Amount of data that was actually transferred */ - unsigned int actual_length; - - /** Status code for this packet */ - enum libusb_transfer_status status; -}; - -struct libusb_transfer; - -/** \ingroup asyncio - * Asynchronous transfer callback function type. When submitting asynchronous - * transfers, you pass a pointer to a callback function of this type via the - * \ref libusb_transfer::callback "callback" member of the libusb_transfer - * structure. libusb will call this function later, when the transfer has - * completed or failed. See \ref asyncio for more information. - * \param transfer The libusb_transfer struct the callback function is being - * notified about. - */ -typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transfer); - -/** \ingroup asyncio - * The generic USB transfer structure. The user populates this structure and - * then submits it in order to request a transfer. After the transfer has - * completed, the library populates the transfer with the results and passes - * it back to the user. - */ -struct libusb_transfer { - /** Handle of the device that this transfer will be submitted to */ - libusb_device_handle *dev_handle; - - /** A bitwise OR combination of \ref libusb_transfer_flags. */ - uint8_t flags; - - /** Address of the endpoint where this transfer will be sent. */ - unsigned char endpoint; - - /** Type of the endpoint from \ref libusb_transfer_type */ - unsigned char type; - - /** Timeout for this transfer in millseconds. A value of 0 indicates no - * timeout. */ - unsigned int timeout; - - /** The status of the transfer. Read-only, and only for use within - * transfer callback function. - * - * If this is an isochronous transfer, this field may read COMPLETED even - * if there were errors in the frames. Use the - * \ref libusb_iso_packet_descriptor::status "status" field in each packet - * to determine if errors occurred. */ - enum libusb_transfer_status status; - - /** Length of the data buffer */ - int length; - - /** Actual length of data that was transferred. Read-only, and only for - * use within transfer callback function. Not valid for isochronous - * endpoint transfers. */ - int actual_length; - - /** Callback function. This will be invoked when the transfer completes, - * fails, or is cancelled. */ - libusb_transfer_cb_fn callback; - - /** User context data to pass to the callback function. */ - void *user_data; - - /** Data buffer */ - unsigned char *buffer; - - /** Number of isochronous packets. Only used for I/O with isochronous - * endpoints. */ - int num_iso_packets; - - /** Isochronous packet descriptors, for isochronous transfers only. */ - struct libusb_iso_packet_descriptor iso_packet_desc -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) - [] /* valid C99 code */ -#else - [0] /* non-standard, but usually working code */ -#endif - ; -}; - -/** \ingroup misc - * Capabilities supported by an instance of libusb on the current running - * platform. Test if the loaded library supports a given capability by calling - * \ref libusb_has_capability(). - */ -enum libusb_capability { - /** The libusb_has_capability() API is available. */ - LIBUSB_CAP_HAS_CAPABILITY = 0x0000, - /** Hotplug support is available on this platform. */ - LIBUSB_CAP_HAS_HOTPLUG = 0x0001, - /** The library can access HID devices without requiring user intervention. - * Note that before being able to actually access an HID device, you may - * still have to call additional libusb functions such as - * \ref libusb_detach_kernel_driver(). */ - LIBUSB_CAP_HAS_HID_ACCESS = 0x0100, - /** The library supports detaching of the default USB driver, using - * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */ - LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101 -}; - -/** \ingroup lib - * Log message levels. - * - LIBUSB_LOG_LEVEL_NONE (0) : no messages ever printed by the library (default) - * - LIBUSB_LOG_LEVEL_ERROR (1) : error messages are printed to stderr - * - LIBUSB_LOG_LEVEL_WARNING (2) : warning and error messages are printed to stderr - * - LIBUSB_LOG_LEVEL_INFO (3) : informational messages are printed to stdout, warning - * and error messages are printed to stderr - * - LIBUSB_LOG_LEVEL_DEBUG (4) : debug and informational messages are printed to stdout, - * warnings and errors to stderr - */ -enum libusb_log_level { - LIBUSB_LOG_LEVEL_NONE = 0, - LIBUSB_LOG_LEVEL_ERROR, - LIBUSB_LOG_LEVEL_WARNING, - LIBUSB_LOG_LEVEL_INFO, - LIBUSB_LOG_LEVEL_DEBUG, -}; - -int LIBUSB_CALL libusb_init(libusb_context **ctx); -void LIBUSB_CALL libusb_exit(libusb_context *ctx); -void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); -const struct libusb_version * LIBUSB_CALL libusb_get_version(void); -int LIBUSB_CALL libusb_has_capability(uint32_t capability); -const char * LIBUSB_CALL libusb_error_name(int errcode); -int LIBUSB_CALL libusb_setlocale(const char *locale); -const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode); - -ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, - libusb_device ***list); -void LIBUSB_CALL libusb_free_device_list(libusb_device **list, - int unref_devices); -libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev); -void LIBUSB_CALL libusb_unref_device(libusb_device *dev); - -int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev, - int *config); -int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev, - struct libusb_device_descriptor *desc); -int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev, - struct libusb_config_descriptor **config); -int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev, - uint8_t config_index, struct libusb_config_descriptor **config); -int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, - uint8_t bConfigurationValue, struct libusb_config_descriptor **config); -void LIBUSB_CALL libusb_free_config_descriptor( - struct libusb_config_descriptor *config); -int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor( - struct libusb_context *ctx, - const struct libusb_endpoint_descriptor *endpoint, - struct libusb_ss_endpoint_companion_descriptor **ep_comp); -void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor( - struct libusb_ss_endpoint_companion_descriptor *ep_comp); -int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device_handle *handle, - struct libusb_bos_descriptor **bos); -void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos); -int LIBUSB_CALL libusb_get_usb_2_0_extension_descriptor( - struct libusb_context *ctx, - struct libusb_bos_dev_capability_descriptor *dev_cap, - struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension); -void LIBUSB_CALL libusb_free_usb_2_0_extension_descriptor( - struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension); -int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor( - struct libusb_context *ctx, - struct libusb_bos_dev_capability_descriptor *dev_cap, - struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap); -void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor( - struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap); -int LIBUSB_CALL libusb_get_container_id_descriptor(struct libusb_context *ctx, - struct libusb_bos_dev_capability_descriptor *dev_cap, - struct libusb_container_id_descriptor **container_id); -void LIBUSB_CALL libusb_free_container_id_descriptor( - struct libusb_container_id_descriptor *container_id); -uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); -uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev); -int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t* port_numbers, int port_numbers_len); -LIBUSB_DEPRECATED_FOR(libusb_get_port_numbers) -int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t* path, uint8_t path_length); -libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev); -uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); -int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev); -int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, - unsigned char endpoint); -int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, - unsigned char endpoint); - -int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **handle); -void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); -libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); - -int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev, - int configuration); -int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev, - int interface_number); -int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev, - int interface_number); - -libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( - libusb_context *ctx, uint16_t vendor_id, uint16_t product_id); - -int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev, - int interface_number, int alternate_setting); -int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev, - unsigned char endpoint); -int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev); - -int LIBUSB_CALL libusb_alloc_streams(libusb_device_handle *dev, - uint32_t num_streams, unsigned char *endpoints, int num_endpoints); -int LIBUSB_CALL libusb_free_streams(libusb_device_handle *dev, - unsigned char *endpoints, int num_endpoints); - -int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev, - int interface_number); -int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev, - int interface_number); -int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev, - int interface_number); -int LIBUSB_CALL libusb_set_auto_detach_kernel_driver( - libusb_device_handle *dev, int enable); - -/* async I/O */ - -/** \ingroup asyncio - * Get the data section of a control transfer. This convenience function is here - * to remind you that the data does not start until 8 bytes into the actual - * buffer, as the setup packet comes first. - * - * Calling this function only makes sense from a transfer callback function, - * or situations where you have already allocated a suitably sized buffer at - * transfer->buffer. - * - * \param transfer a transfer - * \returns pointer to the first byte of the data section - */ -static inline unsigned char *libusb_control_transfer_get_data( - struct libusb_transfer *transfer) -{ - return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; -} - -/** \ingroup asyncio - * Get the control setup packet of a control transfer. This convenience - * function is here to remind you that the control setup occupies the first - * 8 bytes of the transfer data buffer. - * - * Calling this function only makes sense from a transfer callback function, - * or situations where you have already allocated a suitably sized buffer at - * transfer->buffer. - * - * \param transfer a transfer - * \returns a casted pointer to the start of the transfer data buffer - */ -static inline struct libusb_control_setup *libusb_control_transfer_get_setup( - struct libusb_transfer *transfer) -{ - return (struct libusb_control_setup *)(void *) transfer->buffer; -} - -/** \ingroup asyncio - * Helper function to populate the setup packet (first 8 bytes of the data - * buffer) for a control transfer. The wIndex, wValue and wLength values should - * be given in host-endian byte order. - * - * \param buffer buffer to output the setup packet into - * This pointer must be aligned to at least 2 bytes boundary. - * \param bmRequestType see the - * \ref libusb_control_setup::bmRequestType "bmRequestType" field of - * \ref libusb_control_setup - * \param bRequest see the - * \ref libusb_control_setup::bRequest "bRequest" field of - * \ref libusb_control_setup - * \param wValue see the - * \ref libusb_control_setup::wValue "wValue" field of - * \ref libusb_control_setup - * \param wIndex see the - * \ref libusb_control_setup::wIndex "wIndex" field of - * \ref libusb_control_setup - * \param wLength see the - * \ref libusb_control_setup::wLength "wLength" field of - * \ref libusb_control_setup - */ -static inline void libusb_fill_control_setup(unsigned char *buffer, - uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, - uint16_t wLength) -{ - struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer; - setup->bmRequestType = bmRequestType; - setup->bRequest = bRequest; - setup->wValue = libusb_cpu_to_le16(wValue); - setup->wIndex = libusb_cpu_to_le16(wIndex); - setup->wLength = libusb_cpu_to_le16(wLength); -} - -struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets); -int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer); -int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer); -void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer); -void LIBUSB_CALL libusb_transfer_set_stream_id( - struct libusb_transfer *transfer, uint32_t stream_id); -uint32_t LIBUSB_CALL libusb_transfer_get_stream_id( - struct libusb_transfer *transfer); - -/** \ingroup asyncio - * Helper function to populate the required \ref libusb_transfer fields - * for a control transfer. - * - * If you pass a transfer buffer to this function, the first 8 bytes will - * be interpreted as a control setup packet, and the wLength field will be - * used to automatically populate the \ref libusb_transfer::length "length" - * field of the transfer. Therefore the recommended approach is: - * -# Allocate a suitably sized data buffer (including space for control setup) - * -# Call libusb_fill_control_setup() - * -# If this is a host-to-device transfer with a data stage, put the data - * in place after the setup packet - * -# Call this function - * -# Call libusb_submit_transfer() - * - * It is also legal to pass a NULL buffer to this function, in which case this - * function will not attempt to populate the length field. Remember that you - * must then populate the buffer and length fields later. - * - * \param transfer the transfer to populate - * \param dev_handle handle of the device that will handle the transfer - * \param buffer data buffer. If provided, this function will interpret the - * first 8 bytes as a setup packet and infer the transfer length from that. - * This pointer must be aligned to at least 2 bytes boundary. - * \param callback callback function to be invoked on transfer completion - * \param user_data user data to pass to callback function - * \param timeout timeout for the transfer in milliseconds - */ -static inline void libusb_fill_control_transfer( - struct libusb_transfer *transfer, libusb_device_handle *dev_handle, - unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, - unsigned int timeout) -{ - struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer; - transfer->dev_handle = dev_handle; - transfer->endpoint = 0; - transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; - transfer->timeout = timeout; - transfer->buffer = buffer; - if (setup) - transfer->length = (int) (LIBUSB_CONTROL_SETUP_SIZE - + libusb_le16_to_cpu(setup->wLength)); - transfer->user_data = user_data; - transfer->callback = callback; -} - -/** \ingroup asyncio - * Helper function to populate the required \ref libusb_transfer fields - * for a bulk transfer. - * - * \param transfer the transfer to populate - * \param dev_handle handle of the device that will handle the transfer - * \param endpoint address of the endpoint where this transfer will be sent - * \param buffer data buffer - * \param length length of data buffer - * \param callback callback function to be invoked on transfer completion - * \param user_data user data to pass to callback function - * \param timeout timeout for the transfer in milliseconds - */ -static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, - libusb_device_handle *dev_handle, unsigned char endpoint, - unsigned char *buffer, int length, libusb_transfer_cb_fn callback, - void *user_data, unsigned int timeout) -{ - transfer->dev_handle = dev_handle; - transfer->endpoint = endpoint; - transfer->type = LIBUSB_TRANSFER_TYPE_BULK; - transfer->timeout = timeout; - transfer->buffer = buffer; - transfer->length = length; - transfer->user_data = user_data; - transfer->callback = callback; -} - -/** \ingroup asyncio - * Helper function to populate the required \ref libusb_transfer fields - * for a bulk transfer using bulk streams. - * - * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 - * - * \param transfer the transfer to populate - * \param dev_handle handle of the device that will handle the transfer - * \param endpoint address of the endpoint where this transfer will be sent - * \param stream_id bulk stream id for this transfer - * \param buffer data buffer - * \param length length of data buffer - * \param callback callback function to be invoked on transfer completion - * \param user_data user data to pass to callback function - * \param timeout timeout for the transfer in milliseconds - */ -static inline void libusb_fill_bulk_stream_transfer( - struct libusb_transfer *transfer, libusb_device_handle *dev_handle, - unsigned char endpoint, uint32_t stream_id, - unsigned char *buffer, int length, libusb_transfer_cb_fn callback, - void *user_data, unsigned int timeout) -{ - libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, - length, callback, user_data, timeout); - transfer->type = LIBUSB_TRANSFER_TYPE_BULK_STREAM; - libusb_transfer_set_stream_id(transfer, stream_id); -} - -/** \ingroup asyncio - * Helper function to populate the required \ref libusb_transfer fields - * for an interrupt transfer. - * - * \param transfer the transfer to populate - * \param dev_handle handle of the device that will handle the transfer - * \param endpoint address of the endpoint where this transfer will be sent - * \param buffer data buffer - * \param length length of data buffer - * \param callback callback function to be invoked on transfer completion - * \param user_data user data to pass to callback function - * \param timeout timeout for the transfer in milliseconds - */ -static inline void libusb_fill_interrupt_transfer( - struct libusb_transfer *transfer, libusb_device_handle *dev_handle, - unsigned char endpoint, unsigned char *buffer, int length, - libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) -{ - transfer->dev_handle = dev_handle; - transfer->endpoint = endpoint; - transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; - transfer->timeout = timeout; - transfer->buffer = buffer; - transfer->length = length; - transfer->user_data = user_data; - transfer->callback = callback; -} - -/** \ingroup asyncio - * Helper function to populate the required \ref libusb_transfer fields - * for an isochronous transfer. - * - * \param transfer the transfer to populate - * \param dev_handle handle of the device that will handle the transfer - * \param endpoint address of the endpoint where this transfer will be sent - * \param buffer data buffer - * \param length length of data buffer - * \param num_iso_packets the number of isochronous packets - * \param callback callback function to be invoked on transfer completion - * \param user_data user data to pass to callback function - * \param timeout timeout for the transfer in milliseconds - */ -static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, - libusb_device_handle *dev_handle, unsigned char endpoint, - unsigned char *buffer, int length, int num_iso_packets, - libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) -{ - transfer->dev_handle = dev_handle; - transfer->endpoint = endpoint; - transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; - transfer->timeout = timeout; - transfer->buffer = buffer; - transfer->length = length; - transfer->num_iso_packets = num_iso_packets; - transfer->user_data = user_data; - transfer->callback = callback; -} - -/** \ingroup asyncio - * Convenience function to set the length of all packets in an isochronous - * transfer, based on the num_iso_packets field in the transfer structure. - * - * \param transfer a transfer - * \param length the length to set in each isochronous packet descriptor - * \see libusb_get_max_packet_size() - */ -static inline void libusb_set_iso_packet_lengths( - struct libusb_transfer *transfer, unsigned int length) -{ - int i; - for (i = 0; i < transfer->num_iso_packets; i++) - transfer->iso_packet_desc[i].length = length; -} - -/** \ingroup asyncio - * Convenience function to locate the position of an isochronous packet - * within the buffer of an isochronous transfer. - * - * This is a thorough function which loops through all preceding packets, - * accumulating their lengths to find the position of the specified packet. - * Typically you will assign equal lengths to each packet in the transfer, - * and hence the above method is sub-optimal. You may wish to use - * libusb_get_iso_packet_buffer_simple() instead. - * - * \param transfer a transfer - * \param packet the packet to return the address of - * \returns the base address of the packet buffer inside the transfer buffer, - * or NULL if the packet does not exist. - * \see libusb_get_iso_packet_buffer_simple() - */ -static inline unsigned char *libusb_get_iso_packet_buffer( - struct libusb_transfer *transfer, unsigned int packet) -{ - int i; - size_t offset = 0; - int _packet; - - /* oops..slight bug in the API. packet is an unsigned int, but we use - * signed integers almost everywhere else. range-check and convert to - * signed to avoid compiler warnings. FIXME for libusb-2. */ - if (packet > INT_MAX) - return NULL; - _packet = (int) packet; - - if (_packet >= transfer->num_iso_packets) - return NULL; - - for (i = 0; i < _packet; i++) - offset += transfer->iso_packet_desc[i].length; - - return transfer->buffer + offset; -} - -/** \ingroup asyncio - * Convenience function to locate the position of an isochronous packet - * within the buffer of an isochronous transfer, for transfers where each - * packet is of identical size. - * - * This function relies on the assumption that every packet within the transfer - * is of identical size to the first packet. Calculating the location of - * the packet buffer is then just a simple calculation: - * buffer + (packet_size * packet) - * - * Do not use this function on transfers other than those that have identical - * packet lengths for each packet. - * - * \param transfer a transfer - * \param packet the packet to return the address of - * \returns the base address of the packet buffer inside the transfer buffer, - * or NULL if the packet does not exist. - * \see libusb_get_iso_packet_buffer() - */ -static inline unsigned char *libusb_get_iso_packet_buffer_simple( - struct libusb_transfer *transfer, unsigned int packet) -{ - int _packet; - - /* oops..slight bug in the API. packet is an unsigned int, but we use - * signed integers almost everywhere else. range-check and convert to - * signed to avoid compiler warnings. FIXME for libusb-2. */ - if (packet > INT_MAX) - return NULL; - _packet = (int) packet; - - if (_packet >= transfer->num_iso_packets) - return NULL; - - return transfer->buffer + ((int) transfer->iso_packet_desc[0].length * _packet); -} - -/* sync I/O */ - -int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle, - uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, - unsigned char *data, uint16_t wLength, unsigned int timeout); - -int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle, - unsigned char endpoint, unsigned char *data, int length, - int *actual_length, unsigned int timeout); - -int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, - unsigned char endpoint, unsigned char *data, int length, - int *actual_length, unsigned int timeout); - -/** \ingroup desc - * Retrieve a descriptor from the default control pipe. - * This is a convenience function which formulates the appropriate control - * message to retrieve the descriptor. - * - * \param dev a device handle - * \param desc_type the descriptor type, see \ref libusb_descriptor_type - * \param desc_index the index of the descriptor to retrieve - * \param data output buffer for descriptor - * \param length size of data buffer - * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure - */ -static inline int libusb_get_descriptor(libusb_device_handle *dev, - uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) -{ - return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, - LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t) ((desc_type << 8) | desc_index), - 0, data, (uint16_t) length, 1000); -} - -/** \ingroup desc - * Retrieve a descriptor from a device. - * This is a convenience function which formulates the appropriate control - * message to retrieve the descriptor. The string returned is Unicode, as - * detailed in the USB specifications. - * - * \param dev a device handle - * \param desc_index the index of the descriptor to retrieve - * \param langid the language ID for the string descriptor - * \param data output buffer for descriptor - * \param length size of data buffer - * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure - * \see libusb_get_string_descriptor_ascii() - */ -static inline int libusb_get_string_descriptor(libusb_device_handle *dev, - uint8_t desc_index, uint16_t langid, unsigned char *data, int length) -{ - return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, - LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t)((LIBUSB_DT_STRING << 8) | desc_index), - langid, data, (uint16_t) length, 1000); -} - -int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev, - uint8_t desc_index, unsigned char *data, int length); - -/* polling and timeouts */ - -int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx); -void LIBUSB_CALL libusb_lock_events(libusb_context *ctx); -void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx); -int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx); -int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx); -void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx); -void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx); -int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); - -int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx, - struct timeval *tv); -int LIBUSB_CALL libusb_handle_events_timeout_completed(libusb_context *ctx, - struct timeval *tv, int *completed); -int LIBUSB_CALL libusb_handle_events(libusb_context *ctx); -int LIBUSB_CALL libusb_handle_events_completed(libusb_context *ctx, int *completed); -int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx, - struct timeval *tv); -int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx); -int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx, - struct timeval *tv); - -/** \ingroup poll - * File descriptor for polling - */ -struct libusb_pollfd { - /** Numeric file descriptor */ - int fd; - - /** Event flags to poll for from . POLLIN indicates that you - * should monitor this file descriptor for becoming ready to read from, - * and POLLOUT indicates that you should monitor this file descriptor for - * nonblocking write readiness. */ - short events; -}; - -/** \ingroup poll - * Callback function, invoked when a new file descriptor should be added - * to the set of file descriptors monitored for events. - * \param fd the new file descriptor - * \param events events to monitor for, see \ref libusb_pollfd for a - * description - * \param user_data User data pointer specified in - * libusb_set_pollfd_notifiers() call - * \see libusb_set_pollfd_notifiers() - */ -typedef void (LIBUSB_CALL *libusb_pollfd_added_cb)(int fd, short events, - void *user_data); - -/** \ingroup poll - * Callback function, invoked when a file descriptor should be removed from - * the set of file descriptors being monitored for events. After returning - * from this callback, do not use that file descriptor again. - * \param fd the file descriptor to stop monitoring - * \param user_data User data pointer specified in - * libusb_set_pollfd_notifiers() call - * \see libusb_set_pollfd_notifiers() - */ -typedef void (LIBUSB_CALL *libusb_pollfd_removed_cb)(int fd, void *user_data); - -const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( - libusb_context *ctx); -void LIBUSB_CALL libusb_free_pollfds(const struct libusb_pollfd **pollfds); -void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, - libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, - void *user_data); - -/** \ingroup hotplug - * Callback handle. - * - * Callbacks handles are generated by libusb_hotplug_register_callback() - * and can be used to deregister callbacks. Callback handles are unique - * per libusb_context and it is safe to call libusb_hotplug_deregister_callback() - * on an already deregisted callback. - * - * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 - * - * For more information, see \ref hotplug. - */ -typedef int libusb_hotplug_callback_handle; - -/** \ingroup hotplug - * - * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 - * - * Flags for hotplug events */ -typedef enum { - /** Default value when not using any flags. */ - LIBUSB_HOTPLUG_NO_FLAGS = 0, - - /** Arm the callback and fire it for all matching currently attached devices. */ - LIBUSB_HOTPLUG_ENUMERATE = 1<<0, -} libusb_hotplug_flag; - -/** \ingroup hotplug - * - * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 - * - * Hotplug events */ -typedef enum { - /** A device has been plugged in and is ready to use */ - LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01, - - /** A device has left and is no longer available. - * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device. - * It is safe to call libusb_get_device_descriptor on a device that has left */ - LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 0x02, -} libusb_hotplug_event; - -/** \ingroup hotplug - * Wildcard matching for hotplug events */ -#define LIBUSB_HOTPLUG_MATCH_ANY -1 - -/** \ingroup hotplug - * Hotplug callback function type. When requesting hotplug event notifications, - * you pass a pointer to a callback function of this type. - * - * This callback may be called by an internal event thread and as such it is - * recommended the callback do minimal processing before returning. - * - * libusb will call this function later, when a matching event had happened on - * a matching device. See \ref hotplug for more information. - * - * It is safe to call either libusb_hotplug_register_callback() or - * libusb_hotplug_deregister_callback() from within a callback function. - * - * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 - * - * \param ctx context of this notification - * \param device libusb_device this event occurred on - * \param event event that occurred - * \param user_data user data provided when this callback was registered - * \returns bool whether this callback is finished processing events. - * returning 1 will cause this callback to be deregistered - */ -typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, - libusb_device *device, - libusb_hotplug_event event, - void *user_data); - -/** \ingroup hotplug - * Register a hotplug callback function - * - * Register a callback with the libusb_context. The callback will fire - * when a matching event occurs on a matching device. The callback is - * armed until either it is deregistered with libusb_hotplug_deregister_callback() - * or the supplied callback returns 1 to indicate it is finished processing events. - * - * If the \ref LIBUSB_HOTPLUG_ENUMERATE is passed the callback will be - * called with a \ref LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED for all devices - * already plugged into the machine. Note that libusb modifies its internal - * device list from a separate thread, while calling hotplug callbacks from - * libusb_handle_events(), so it is possible for a device to already be present - * on, or removed from, its internal device list, while the hotplug callbacks - * still need to be dispatched. This means that when using \ref - * LIBUSB_HOTPLUG_ENUMERATE, your callback may be called twice for the arrival - * of the same device, once from libusb_hotplug_register_callback() and once - * from libusb_handle_events(); and/or your callback may be called for the - * removal of a device for which an arrived call was never made. - * - * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 - * - * \param[in] ctx context to register this callback with - * \param[in] events bitwise or of events that will trigger this callback. See \ref - * libusb_hotplug_event - * \param[in] flags hotplug callback flags. See \ref libusb_hotplug_flag - * \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY - * \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY - * \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY - * \param[in] cb_fn the function to be invoked on a matching event/device - * \param[in] user_data user data to pass to the callback function - * \param[out] handle pointer to store the handle of the allocated callback (can be NULL) - * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure - */ -int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx, - libusb_hotplug_event events, - libusb_hotplug_flag flags, - int vendor_id, int product_id, - int dev_class, - libusb_hotplug_callback_fn cb_fn, - void *user_data, - libusb_hotplug_callback_handle *handle); - -/** \ingroup hotplug - * Deregisters a hotplug callback. - * - * Deregister a callback from a libusb_context. This function is safe to call from within - * a hotplug callback. - * - * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 - * - * \param[in] ctx context this callback is registered with - * \param[in] handle the handle of the callback to deregister - */ -void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx, - libusb_hotplug_callback_handle handle); - -#ifdef __cplusplus -} -#endif - -#endif +/* + * Public libusb header file + * Copyright © 2001 Johannes Erdfelt + * Copyright © 2007-2008 Daniel Drake + * Copyright © 2012 Pete Batard + * Copyright © 2012 Nathan Hjelm + * For more information, please visit: http://libusb.info + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_H +#define LIBUSB_H + +#ifdef _MSC_VER +/* on MS environments, the inline keyword is available in C++ only */ +#if !defined(__cplusplus) +#define inline __inline +#endif +/* ssize_t is also not available (copy/paste from MinGW) */ +#ifndef _SSIZE_T_DEFINED +#define _SSIZE_T_DEFINED +#undef ssize_t +#ifdef _WIN64 + typedef __int64 ssize_t; +#else + typedef int ssize_t; +#endif /* _WIN64 */ +#endif /* _SSIZE_T_DEFINED */ +#endif /* _MSC_VER */ + +/* stdint.h is not available on older MSVC */ +#if defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H)) +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +#else +#include +#endif + +#if !defined(_WIN32_WCE) +#include +#endif + +#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__) +#include +#endif + +#include +#include + +/* 'interface' might be defined as a macro on Windows, so we need to + * undefine it so as not to break the current libusb API, because + * libusb_config_descriptor has an 'interface' member + * As this can be problematic if you include windows.h after libusb.h + * in your sources, we force windows.h to be included first. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +#include +#if defined(interface) +#undef interface +#endif +#if !defined(__CYGWIN__) +#include +#endif +#endif + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +#define LIBUSB_DEPRECATED_FOR(f) \ + __attribute__((deprecated("Use " #f " instead"))) +#else +#define LIBUSB_DEPRECATED_FOR(f) +#endif /* __GNUC__ */ + +/** \def LIBUSB_CALL + * \ingroup libusb_misc + * libusb's Windows calling convention. + * + * Under Windows, the selection of available compilers and configurations + * means that, unlike other platforms, there is not one true calling + * convention (calling convention: the manner in which parameters are + * passed to functions in the generated assembly code). + * + * Matching the Windows API itself, libusb uses the WINAPI convention (which + * translates to the stdcall convention) and guarantees that the + * library is compiled in this way. The public header file also includes + * appropriate annotations so that your own software will use the right + * convention, even if another convention is being used by default within + * your codebase. + * + * The one consideration that you must apply in your software is to mark + * all functions which you use as libusb callbacks with this LIBUSB_CALL + * annotation, so that they too get compiled for the correct calling + * convention. + * + * On non-Windows operating systems, this macro is defined as nothing. This + * means that you can apply it to your code without worrying about + * cross-platform compatibility. + */ +/* LIBUSB_CALL must be defined on both definition and declaration of libusb + * functions. You'd think that declaration would be enough, but cygwin will + * complain about conflicting types unless both are marked this way. + * The placement of this macro is important too; it must appear after the + * return type, before the function name. See internal documentation for + * API_EXPORTED. + */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +#define LIBUSB_CALL WINAPI +#else +#define LIBUSB_CALL +#endif + +/** \def LIBUSB_API_VERSION + * \ingroup libusb_misc + * libusb's API version. + * + * Since version 1.0.13, to help with feature detection, libusb defines + * a LIBUSB_API_VERSION macro that gets increased every time there is a + * significant change to the API, such as the introduction of a new call, + * the definition of a new macro/enum member, or any other element that + * libusb applications may want to detect at compilation time. + * + * The macro is typically used in an application as follows: + * \code + * #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01001234) + * // Use one of the newer features from the libusb API + * #endif + * \endcode + * + * Internally, LIBUSB_API_VERSION is defined as follows: + * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental) + */ +#define LIBUSB_API_VERSION 0x01000105 + +/* The following is kept for compatibility, but will be deprecated in the future */ +#define LIBUSBX_API_VERSION LIBUSB_API_VERSION + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup libusb_misc + * Convert a 16-bit value from host-endian to little-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the host-endian value to convert + * \returns the value in little-endian byte order + */ +static inline uint16_t libusb_cpu_to_le16(const uint16_t x) +{ + union { + uint8_t b8[2]; + uint16_t b16; + } _tmp; + _tmp.b8[1] = (uint8_t) (x >> 8); + _tmp.b8[0] = (uint8_t) (x & 0xff); + return _tmp.b16; +} + +/** \def libusb_le16_to_cpu + * \ingroup libusb_misc + * Convert a 16-bit value from little-endian to host-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the little-endian value to convert + * \returns the value in host-endian byte order + */ +#define libusb_le16_to_cpu libusb_cpu_to_le16 + +/* standard USB stuff */ + +/** \ingroup libusb_desc + * Device and/or Interface Class codes */ +enum libusb_class_code { + /** In the context of a \ref libusb_device_descriptor "device descriptor", + * this bDeviceClass value indicates that each interface specifies its + * own class information and all interfaces operate independently. + */ + LIBUSB_CLASS_PER_INTERFACE = 0, + + /** Audio class */ + LIBUSB_CLASS_AUDIO = 1, + + /** Communications class */ + LIBUSB_CLASS_COMM = 2, + + /** Human Interface Device class */ + LIBUSB_CLASS_HID = 3, + + /** Physical */ + LIBUSB_CLASS_PHYSICAL = 5, + + /** Printer class */ + LIBUSB_CLASS_PRINTER = 7, + + /** Image class */ + LIBUSB_CLASS_PTP = 6, /* legacy name from libusb-0.1 usb.h */ + LIBUSB_CLASS_IMAGE = 6, + + /** Mass storage class */ + LIBUSB_CLASS_MASS_STORAGE = 8, + + /** Hub class */ + LIBUSB_CLASS_HUB = 9, + + /** Data class */ + LIBUSB_CLASS_DATA = 10, + + /** Smart Card */ + LIBUSB_CLASS_SMART_CARD = 0x0b, + + /** Content Security */ + LIBUSB_CLASS_CONTENT_SECURITY = 0x0d, + + /** Video */ + LIBUSB_CLASS_VIDEO = 0x0e, + + /** Personal Healthcare */ + LIBUSB_CLASS_PERSONAL_HEALTHCARE = 0x0f, + + /** Diagnostic Device */ + LIBUSB_CLASS_DIAGNOSTIC_DEVICE = 0xdc, + + /** Wireless class */ + LIBUSB_CLASS_WIRELESS = 0xe0, + + /** Application class */ + LIBUSB_CLASS_APPLICATION = 0xfe, + + /** Class is vendor-specific */ + LIBUSB_CLASS_VENDOR_SPEC = 0xff +}; + +/** \ingroup libusb_desc + * Descriptor types as defined by the USB specification. */ +enum libusb_descriptor_type { + /** Device descriptor. See libusb_device_descriptor. */ + LIBUSB_DT_DEVICE = 0x01, + + /** Configuration descriptor. See libusb_config_descriptor. */ + LIBUSB_DT_CONFIG = 0x02, + + /** String descriptor */ + LIBUSB_DT_STRING = 0x03, + + /** Interface descriptor. See libusb_interface_descriptor. */ + LIBUSB_DT_INTERFACE = 0x04, + + /** Endpoint descriptor. See libusb_endpoint_descriptor. */ + LIBUSB_DT_ENDPOINT = 0x05, + + /** BOS descriptor */ + LIBUSB_DT_BOS = 0x0f, + + /** Device Capability descriptor */ + LIBUSB_DT_DEVICE_CAPABILITY = 0x10, + + /** HID descriptor */ + LIBUSB_DT_HID = 0x21, + + /** HID report descriptor */ + LIBUSB_DT_REPORT = 0x22, + + /** Physical descriptor */ + LIBUSB_DT_PHYSICAL = 0x23, + + /** Hub descriptor */ + LIBUSB_DT_HUB = 0x29, + + /** SuperSpeed Hub descriptor */ + LIBUSB_DT_SUPERSPEED_HUB = 0x2a, + + /** SuperSpeed Endpoint Companion descriptor */ + LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30 +}; + +/* Descriptor sizes per descriptor type */ +#define LIBUSB_DT_DEVICE_SIZE 18 +#define LIBUSB_DT_CONFIG_SIZE 9 +#define LIBUSB_DT_INTERFACE_SIZE 9 +#define LIBUSB_DT_ENDPOINT_SIZE 7 +#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB_DT_HUB_NONVAR_SIZE 7 +#define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE 6 +#define LIBUSB_DT_BOS_SIZE 5 +#define LIBUSB_DT_DEVICE_CAPABILITY_SIZE 3 + +/* BOS descriptor sizes */ +#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7 +#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10 +#define LIBUSB_BT_CONTAINER_ID_SIZE 20 + +/* We unwrap the BOS => define its max size */ +#define LIBUSB_DT_BOS_MAX_SIZE ((LIBUSB_DT_BOS_SIZE) +\ + (LIBUSB_BT_USB_2_0_EXTENSION_SIZE) +\ + (LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) +\ + (LIBUSB_BT_CONTAINER_ID_SIZE)) + +#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB_ENDPOINT_DIR_MASK 0x80 + +/** \ingroup libusb_desc + * Endpoint direction. Values for bit 7 of the + * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. + */ +enum libusb_endpoint_direction { + /** In: device-to-host */ + LIBUSB_ENDPOINT_IN = 0x80, + + /** Out: host-to-device */ + LIBUSB_ENDPOINT_OUT = 0x00 +}; + +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ + +/** \ingroup libusb_desc + * Endpoint transfer type. Values for bits 0:1 of the + * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. + */ +enum libusb_transfer_type { + /** Control endpoint */ + LIBUSB_TRANSFER_TYPE_CONTROL = 0, + + /** Isochronous endpoint */ + LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, + + /** Bulk endpoint */ + LIBUSB_TRANSFER_TYPE_BULK = 2, + + /** Interrupt endpoint */ + LIBUSB_TRANSFER_TYPE_INTERRUPT = 3, + + /** Stream endpoint */ + LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4, +}; + +/** \ingroup libusb_misc + * Standard requests, as defined in table 9-5 of the USB 3.0 specifications */ +enum libusb_standard_request { + /** Request status of the specific recipient */ + LIBUSB_REQUEST_GET_STATUS = 0x00, + + /** Clear or disable a specific feature */ + LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, + + /* 0x02 is reserved */ + + /** Set or enable a specific feature */ + LIBUSB_REQUEST_SET_FEATURE = 0x03, + + /* 0x04 is reserved */ + + /** Set device address for all future accesses */ + LIBUSB_REQUEST_SET_ADDRESS = 0x05, + + /** Get the specified descriptor */ + LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, + + /** Used to update existing descriptors or add new descriptors */ + LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, + + /** Get the current device configuration value */ + LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, + + /** Set device configuration */ + LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, + + /** Return the selected alternate setting for the specified interface */ + LIBUSB_REQUEST_GET_INTERFACE = 0x0A, + + /** Select an alternate interface for the specified interface */ + LIBUSB_REQUEST_SET_INTERFACE = 0x0B, + + /** Set then report an endpoint's synchronization frame */ + LIBUSB_REQUEST_SYNCH_FRAME = 0x0C, + + /** Sets both the U1 and U2 Exit Latency */ + LIBUSB_REQUEST_SET_SEL = 0x30, + + /** Delay from the time a host transmits a packet to the time it is + * received by the device. */ + LIBUSB_SET_ISOCH_DELAY = 0x31, +}; + +/** \ingroup libusb_misc + * Request type bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. */ +enum libusb_request_type { + /** Standard */ + LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), + + /** Class */ + LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), + + /** Vendor */ + LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), + + /** Reserved */ + LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5) +}; + +/** \ingroup libusb_misc + * Recipient bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. Values 4 through 31 are reserved. */ +enum libusb_request_recipient { + /** Device */ + LIBUSB_RECIPIENT_DEVICE = 0x00, + + /** Interface */ + LIBUSB_RECIPIENT_INTERFACE = 0x01, + + /** Endpoint */ + LIBUSB_RECIPIENT_ENDPOINT = 0x02, + + /** Other */ + LIBUSB_RECIPIENT_OTHER = 0x03, +}; + +#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C + +/** \ingroup libusb_desc + * Synchronization type for isochronous endpoints. Values for bits 2:3 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_sync_type { + /** No synchronization */ + LIBUSB_ISO_SYNC_TYPE_NONE = 0, + + /** Asynchronous */ + LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, + + /** Adaptive */ + LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, + + /** Synchronous */ + LIBUSB_ISO_SYNC_TYPE_SYNC = 3 +}; + +#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 + +/** \ingroup libusb_desc + * Usage type for isochronous endpoints. Values for bits 4:5 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_usage_type { + /** Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_DATA = 0, + + /** Feedback endpoint */ + LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, + + /** Implicit feedback Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2, +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB device descriptor. This + * descriptor is documented in section 9.6.1 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_device_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this + * context. */ + uint8_t bDescriptorType; + + /** USB specification release number in binary-coded decimal. A value of + * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ + uint16_t bcdUSB; + + /** USB-IF class code for the device. See \ref libusb_class_code. */ + uint8_t bDeviceClass; + + /** USB-IF subclass code for the device, qualified by the bDeviceClass + * value */ + uint8_t bDeviceSubClass; + + /** USB-IF protocol code for the device, qualified by the bDeviceClass and + * bDeviceSubClass values */ + uint8_t bDeviceProtocol; + + /** Maximum packet size for endpoint 0 */ + uint8_t bMaxPacketSize0; + + /** USB-IF vendor ID */ + uint16_t idVendor; + + /** USB-IF product ID */ + uint16_t idProduct; + + /** Device release number in binary-coded decimal */ + uint16_t bcdDevice; + + /** Index of string descriptor describing manufacturer */ + uint8_t iManufacturer; + + /** Index of string descriptor describing product */ + uint8_t iProduct; + + /** Index of string descriptor containing device serial number */ + uint8_t iSerialNumber; + + /** Number of possible configurations */ + uint8_t bNumConfigurations; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB endpoint descriptor. This + * descriptor is documented in section 9.6.6 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_endpoint_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in + * this context. */ + uint8_t bDescriptorType; + + /** The address of the endpoint described by this descriptor. Bits 0:3 are + * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, + * see \ref libusb_endpoint_direction. + */ + uint8_t bEndpointAddress; + + /** Attributes which apply to the endpoint when it is configured using + * the bConfigurationValue. Bits 0:1 determine the transfer type and + * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for + * isochronous endpoints and correspond to \ref libusb_iso_sync_type. + * Bits 4:5 are also only used for isochronous endpoints and correspond to + * \ref libusb_iso_usage_type. Bits 6:7 are reserved. + */ + uint8_t bmAttributes; + + /** Maximum packet size this endpoint is capable of sending/receiving. */ + uint16_t wMaxPacketSize; + + /** Interval for polling endpoint for data transfers. */ + uint8_t bInterval; + + /** For audio devices only: the rate at which synchronization feedback + * is provided. */ + uint8_t bRefresh; + + /** For audio devices only: the address if the synch endpoint */ + uint8_t bSynchAddress; + + /** Extra descriptors. If libusb encounters unknown endpoint descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB interface descriptor. This + * descriptor is documented in section 9.6.5 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE + * in this context. */ + uint8_t bDescriptorType; + + /** Number of this interface */ + uint8_t bInterfaceNumber; + + /** Value used to select this alternate setting for this interface */ + uint8_t bAlternateSetting; + + /** Number of endpoints used by this interface (excluding the control + * endpoint). */ + uint8_t bNumEndpoints; + + /** USB-IF class code for this interface. See \ref libusb_class_code. */ + uint8_t bInterfaceClass; + + /** USB-IF subclass code for this interface, qualified by the + * bInterfaceClass value */ + uint8_t bInterfaceSubClass; + + /** USB-IF protocol code for this interface, qualified by the + * bInterfaceClass and bInterfaceSubClass values */ + uint8_t bInterfaceProtocol; + + /** Index of string descriptor describing this interface */ + uint8_t iInterface; + + /** Array of endpoint descriptors. This length of this array is determined + * by the bNumEndpoints field. */ + const struct libusb_endpoint_descriptor *endpoint; + + /** Extra descriptors. If libusb encounters unknown interface descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A collection of alternate settings for a particular USB interface. + */ +struct libusb_interface { + /** Array of interface descriptors. The length of this array is determined + * by the num_altsetting field. */ + const struct libusb_interface_descriptor *altsetting; + + /** The number of alternate settings that belong to this interface */ + int num_altsetting; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB configuration descriptor. This + * descriptor is documented in section 9.6.3 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_config_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG + * in this context. */ + uint8_t bDescriptorType; + + /** Total length of data returned for this configuration */ + uint16_t wTotalLength; + + /** Number of interfaces supported by this configuration */ + uint8_t bNumInterfaces; + + /** Identifier value for this configuration */ + uint8_t bConfigurationValue; + + /** Index of string descriptor describing this configuration */ + uint8_t iConfiguration; + + /** Configuration characteristics */ + uint8_t bmAttributes; + + /** Maximum power consumption of the USB device from this bus in this + * configuration when the device is fully operation. Expressed in units + * of 2 mA when the device is operating in high-speed mode and in units + * of 8 mA when the device is operating in super-speed mode. */ + uint8_t MaxPower; + + /** Array of interfaces supported by this configuration. The length of + * this array is determined by the bNumInterfaces field. */ + const struct libusb_interface *interface; + + /** Extra descriptors. If libusb encounters unknown configuration + * descriptors, it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A structure representing the superspeed endpoint companion + * descriptor. This descriptor is documented in section 9.6.7 of + * the USB 3.0 specification. All multiple-byte fields are represented in + * host-endian format. + */ +struct libusb_ss_endpoint_companion_descriptor { + + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in + * this context. */ + uint8_t bDescriptorType; + + + /** The maximum number of packets the endpoint can send or + * receive as part of a burst. */ + uint8_t bMaxBurst; + + /** In bulk EP: bits 4:0 represents the maximum number of + * streams the EP supports. In isochronous EP: bits 1:0 + * represents the Mult - a zero based value that determines + * the maximum number of packets within a service interval */ + uint8_t bmAttributes; + + /** The total number of bytes this EP will transfer every + * service interval. valid only for periodic EPs. */ + uint16_t wBytesPerInterval; +}; + +/** \ingroup libusb_desc + * A generic representation of a BOS Device Capability descriptor. It is + * advised to check bDevCapabilityType and call the matching + * libusb_get_*_descriptor function to get a structure fully matching the type. + */ +struct libusb_bos_dev_capability_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + /** Device Capability type */ + uint8_t bDevCapabilityType; + /** Device Capability data (bLength - 3 bytes) */ + uint8_t dev_capability_data +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +/** \ingroup libusb_desc + * A structure representing the Binary Device Object Store (BOS) descriptor. + * This descriptor is documented in section 9.6.2 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_bos_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_BOS LIBUSB_DT_BOS + * in this context. */ + uint8_t bDescriptorType; + + /** Length of this descriptor and all of its sub descriptors */ + uint16_t wTotalLength; + + /** The number of separate device capability descriptors in + * the BOS */ + uint8_t bNumDeviceCaps; + + /** bNumDeviceCap Device Capability Descriptors */ + struct libusb_bos_dev_capability_descriptor *dev_capability +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +/** \ingroup libusb_desc + * A structure representing the USB 2.0 Extension descriptor + * This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_usb_2_0_extension_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION + * LIBUSB_BT_USB_2_0_EXTENSION in this context. */ + uint8_t bDevCapabilityType; + + /** Bitmap encoding of supported device level features. + * A value of one in a bit location indicates a feature is + * supported; a value of zero indicates it is not supported. + * See \ref libusb_usb_2_0_extension_attributes. */ + uint32_t bmAttributes; +}; + +/** \ingroup libusb_desc + * A structure representing the SuperSpeed USB Device Capability descriptor + * This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_ss_usb_device_capability_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY + * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY in this context. */ + uint8_t bDevCapabilityType; + + /** Bitmap encoding of supported device level features. + * A value of one in a bit location indicates a feature is + * supported; a value of zero indicates it is not supported. + * See \ref libusb_ss_usb_device_capability_attributes. */ + uint8_t bmAttributes; + + /** Bitmap encoding of the speed supported by this device when + * operating in SuperSpeed mode. See \ref libusb_supported_speed. */ + uint16_t wSpeedSupported; + + /** The lowest speed at which all the functionality supported + * by the device is available to the user. For example if the + * device supports all its functionality when connected at + * full speed and above then it sets this value to 1. */ + uint8_t bFunctionalitySupport; + + /** U1 Device Exit Latency. */ + uint8_t bU1DevExitLat; + + /** U2 Device Exit Latency. */ + uint16_t bU2DevExitLat; +}; + +/** \ingroup libusb_desc + * A structure representing the Container ID descriptor. + * This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification. + * All multiple-byte fields, except UUIDs, are represented in host-endian format. + */ +struct libusb_container_id_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID + * LIBUSB_BT_CONTAINER_ID in this context. */ + uint8_t bDevCapabilityType; + + /** Reserved field */ + uint8_t bReserved; + + /** 128 bit UUID */ + uint8_t ContainerID[16]; +}; + +/** \ingroup libusb_asyncio + * Setup packet for control transfers. */ +struct libusb_control_setup { + /** Request type. Bits 0:4 determine recipient, see + * \ref libusb_request_recipient. Bits 5:6 determine type, see + * \ref libusb_request_type. Bit 7 determines data transfer direction, see + * \ref libusb_endpoint_direction. + */ + uint8_t bmRequestType; + + /** Request. If the type bits of bmRequestType are equal to + * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD + * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to + * \ref libusb_standard_request. For other cases, use of this field is + * application-specific. */ + uint8_t bRequest; + + /** Value. Varies according to request */ + uint16_t wValue; + + /** Index. Varies according to request, typically used to pass an index + * or offset */ + uint16_t wIndex; + + /** Number of bytes to transfer */ + uint16_t wLength; +}; + +#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) + +/* libusb */ + +struct libusb_context; +struct libusb_device; +struct libusb_device_handle; + +/** \ingroup libusb_lib + * Structure providing the version of the libusb runtime + */ +struct libusb_version { + /** Library major version. */ + const uint16_t major; + + /** Library minor version. */ + const uint16_t minor; + + /** Library micro version. */ + const uint16_t micro; + + /** Library nano version. */ + const uint16_t nano; + + /** Library release candidate suffix string, e.g. "-rc4". */ + const char *rc; + + /** For ABI compatibility only. */ + const char* describe; +}; + +/** \ingroup libusb_lib + * Structure representing a libusb session. The concept of individual libusb + * sessions allows for your program to use two libraries (or dynamically + * load two modules) which both independently use libusb. This will prevent + * interference between the individual libusb users - for example + * libusb_set_debug() will not affect the other user of the library, and + * libusb_exit() will not destroy resources that the other user is still + * using. + * + * Sessions are created by libusb_init() and destroyed through libusb_exit(). + * If your application is guaranteed to only ever include a single libusb + * user (i.e. you), you do not have to worry about contexts: pass NULL in + * every function call where a context is required. The default context + * will be used. + * + * For more information, see \ref libusb_contexts. + */ +typedef struct libusb_context libusb_context; + +/** \ingroup libusb_dev + * Structure representing a USB device detected on the system. This is an + * opaque type for which you are only ever provided with a pointer, usually + * originating from libusb_get_device_list(). + * + * Certain operations can be performed on a device, but in order to do any + * I/O you will have to first obtain a device handle using libusb_open(). + * + * Devices are reference counted with libusb_ref_device() and + * libusb_unref_device(), and are freed when the reference count reaches 0. + * New devices presented by libusb_get_device_list() have a reference count of + * 1, and libusb_free_device_list() can optionally decrease the reference count + * on all devices in the list. libusb_open() adds another reference which is + * later destroyed by libusb_close(). + */ +typedef struct libusb_device libusb_device; + + +/** \ingroup libusb_dev + * Structure representing a handle on a USB device. This is an opaque type for + * which you are only ever provided with a pointer, usually originating from + * libusb_open(). + * + * A device handle is used to perform I/O and other operations. When finished + * with a device handle, you should call libusb_close(). + */ +typedef struct libusb_device_handle libusb_device_handle; + +/** \ingroup libusb_dev + * Speed codes. Indicates the speed at which the device is operating. + */ +enum libusb_speed { + /** The OS doesn't report or know the device speed. */ + LIBUSB_SPEED_UNKNOWN = 0, + + /** The device is operating at low speed (1.5MBit/s). */ + LIBUSB_SPEED_LOW = 1, + + /** The device is operating at full speed (12MBit/s). */ + LIBUSB_SPEED_FULL = 2, + + /** The device is operating at high speed (480MBit/s). */ + LIBUSB_SPEED_HIGH = 3, + + /** The device is operating at super speed (5000MBit/s). */ + LIBUSB_SPEED_SUPER = 4, +}; + +/** \ingroup libusb_dev + * Supported speeds (wSpeedSupported) bitfield. Indicates what + * speeds the device supports. + */ +enum libusb_supported_speed { + /** Low speed operation supported (1.5MBit/s). */ + LIBUSB_LOW_SPEED_OPERATION = 1, + + /** Full speed operation supported (12MBit/s). */ + LIBUSB_FULL_SPEED_OPERATION = 2, + + /** High speed operation supported (480MBit/s). */ + LIBUSB_HIGH_SPEED_OPERATION = 4, + + /** Superspeed operation supported (5000MBit/s). */ + LIBUSB_SUPER_SPEED_OPERATION = 8, +}; + +/** \ingroup libusb_dev + * Masks for the bits of the + * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field + * of the USB 2.0 Extension descriptor. + */ +enum libusb_usb_2_0_extension_attributes { + /** Supports Link Power Management (LPM) */ + LIBUSB_BM_LPM_SUPPORT = 2, +}; + +/** \ingroup libusb_dev + * Masks for the bits of the + * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field + * field of the SuperSpeed USB Device Capability descriptor. + */ +enum libusb_ss_usb_device_capability_attributes { + /** Supports Latency Tolerance Messages (LTM) */ + LIBUSB_BM_LTM_SUPPORT = 2, +}; + +/** \ingroup libusb_dev + * USB capability types + */ +enum libusb_bos_type { + /** Wireless USB device capability */ + LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1, + + /** USB 2.0 extensions */ + LIBUSB_BT_USB_2_0_EXTENSION = 2, + + /** SuperSpeed USB device capability */ + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 3, + + /** Container ID type */ + LIBUSB_BT_CONTAINER_ID = 4, +}; + +/** \ingroup libusb_misc + * Error codes. Most libusb functions return 0 on success or one of these + * codes on failure. + * You can call libusb_error_name() to retrieve a string representation of an + * error code or libusb_strerror() to get an end-user suitable description of + * an error code. + */ +enum libusb_error { + /** Success (no error) */ + LIBUSB_SUCCESS = 0, + + /** Input/output error */ + LIBUSB_ERROR_IO = -1, + + /** Invalid parameter */ + LIBUSB_ERROR_INVALID_PARAM = -2, + + /** Access denied (insufficient permissions) */ + LIBUSB_ERROR_ACCESS = -3, + + /** No such device (it may have been disconnected) */ + LIBUSB_ERROR_NO_DEVICE = -4, + + /** Entity not found */ + LIBUSB_ERROR_NOT_FOUND = -5, + + /** Resource busy */ + LIBUSB_ERROR_BUSY = -6, + + /** Operation timed out */ + LIBUSB_ERROR_TIMEOUT = -7, + + /** Overflow */ + LIBUSB_ERROR_OVERFLOW = -8, + + /** Pipe error */ + LIBUSB_ERROR_PIPE = -9, + + /** System call interrupted (perhaps due to signal) */ + LIBUSB_ERROR_INTERRUPTED = -10, + + /** Insufficient memory */ + LIBUSB_ERROR_NO_MEM = -11, + + /** Operation not supported or unimplemented on this platform */ + LIBUSB_ERROR_NOT_SUPPORTED = -12, + + /* NB: Remember to update LIBUSB_ERROR_COUNT below as well as the + message strings in strerror.c when adding new error codes here. */ + + /** Other error */ + LIBUSB_ERROR_OTHER = -99, +}; + +/* Total number of error codes in enum libusb_error */ +#define LIBUSB_ERROR_COUNT 14 + +/** \ingroup libusb_asyncio + * Transfer status codes */ +enum libusb_transfer_status { + /** Transfer completed without error. Note that this does not indicate + * that the entire amount of requested data was transferred. */ + LIBUSB_TRANSFER_COMPLETED, + + /** Transfer failed */ + LIBUSB_TRANSFER_ERROR, + + /** Transfer timed out */ + LIBUSB_TRANSFER_TIMED_OUT, + + /** Transfer was cancelled */ + LIBUSB_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected (endpoint + * stalled). For control endpoints: control request not supported. */ + LIBUSB_TRANSFER_STALL, + + /** Device was disconnected */ + LIBUSB_TRANSFER_NO_DEVICE, + + /** Device sent more data than requested */ + LIBUSB_TRANSFER_OVERFLOW, + + /* NB! Remember to update libusb_error_name() + when adding new status codes here. */ +}; + +/** \ingroup libusb_asyncio + * libusb_transfer.flags values */ +enum libusb_transfer_flags { + /** Report short frames as errors */ + LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0, + + /** Automatically free() transfer buffer during libusb_free_transfer(). + * Note that buffers allocated with libusb_dev_mem_alloc() should not + * be attempted freed in this way, since free() is not an appropriate + * way to release such memory. */ + LIBUSB_TRANSFER_FREE_BUFFER = 1<<1, + + /** Automatically call libusb_free_transfer() after callback returns. + * If this flag is set, it is illegal to call libusb_free_transfer() + * from your transfer callback, as this will result in a double-free + * when this flag is acted upon. */ + LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2, + + /** Terminate transfers that are a multiple of the endpoint's + * wMaxPacketSize with an extra zero length packet. This is useful + * when a device protocol mandates that each logical request is + * terminated by an incomplete packet (i.e. the logical requests are + * not separated by other means). + * + * This flag only affects host-to-device transfers to bulk and interrupt + * endpoints. In other situations, it is ignored. + * + * This flag only affects transfers with a length that is a multiple of + * the endpoint's wMaxPacketSize. On transfers of other lengths, this + * flag has no effect. Therefore, if you are working with a device that + * needs a ZLP whenever the end of the logical request falls on a packet + * boundary, then it is sensible to set this flag on every + * transfer (you do not have to worry about only setting it on transfers + * that end on the boundary). + * + * This flag is currently only supported on Linux. + * On other systems, libusb_submit_transfer() will return + * LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set. + * + * Available since libusb-1.0.9. + */ + LIBUSB_TRANSFER_ADD_ZERO_PACKET = 1 << 3, +}; + +/** \ingroup libusb_asyncio + * Isochronous packet descriptor. */ +struct libusb_iso_packet_descriptor { + /** Length of data to request in this packet */ + unsigned int length; + + /** Amount of data that was actually transferred */ + unsigned int actual_length; + + /** Status code for this packet */ + enum libusb_transfer_status status; +}; + +struct libusb_transfer; + +/** \ingroup libusb_asyncio + * Asynchronous transfer callback function type. When submitting asynchronous + * transfers, you pass a pointer to a callback function of this type via the + * \ref libusb_transfer::callback "callback" member of the libusb_transfer + * structure. libusb will call this function later, when the transfer has + * completed or failed. See \ref libusb_asyncio for more information. + * \param transfer The libusb_transfer struct the callback function is being + * notified about. + */ +typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transfer); + +/** \ingroup libusb_asyncio + * The generic USB transfer structure. The user populates this structure and + * then submits it in order to request a transfer. After the transfer has + * completed, the library populates the transfer with the results and passes + * it back to the user. + */ +struct libusb_transfer { + /** Handle of the device that this transfer will be submitted to */ + libusb_device_handle *dev_handle; + + /** A bitwise OR combination of \ref libusb_transfer_flags. */ + uint8_t flags; + + /** Address of the endpoint where this transfer will be sent. */ + unsigned char endpoint; + + /** Type of the endpoint from \ref libusb_transfer_type */ + unsigned char type; + + /** Timeout for this transfer in millseconds. A value of 0 indicates no + * timeout. */ + unsigned int timeout; + + /** The status of the transfer. Read-only, and only for use within + * transfer callback function. + * + * If this is an isochronous transfer, this field may read COMPLETED even + * if there were errors in the frames. Use the + * \ref libusb_iso_packet_descriptor::status "status" field in each packet + * to determine if errors occurred. */ + enum libusb_transfer_status status; + + /** Length of the data buffer */ + int length; + + /** Actual length of data that was transferred. Read-only, and only for + * use within transfer callback function. Not valid for isochronous + * endpoint transfers. */ + int actual_length; + + /** Callback function. This will be invoked when the transfer completes, + * fails, or is cancelled. */ + libusb_transfer_cb_fn callback; + + /** User context data to pass to the callback function. */ + void *user_data; + + /** Data buffer */ + unsigned char *buffer; + + /** Number of isochronous packets. Only used for I/O with isochronous + * endpoints. */ + int num_iso_packets; + + /** Isochronous packet descriptors, for isochronous transfers only. */ + struct libusb_iso_packet_descriptor iso_packet_desc +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +/** \ingroup libusb_misc + * Capabilities supported by an instance of libusb on the current running + * platform. Test if the loaded library supports a given capability by calling + * \ref libusb_has_capability(). + */ +enum libusb_capability { + /** The libusb_has_capability() API is available. */ + LIBUSB_CAP_HAS_CAPABILITY = 0x0000, + /** Hotplug support is available on this platform. */ + LIBUSB_CAP_HAS_HOTPLUG = 0x0001, + /** The library can access HID devices without requiring user intervention. + * Note that before being able to actually access an HID device, you may + * still have to call additional libusb functions such as + * \ref libusb_detach_kernel_driver(). */ + LIBUSB_CAP_HAS_HID_ACCESS = 0x0100, + /** The library supports detaching of the default USB driver, using + * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */ + LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101 +}; + +/** \ingroup libusb_lib + * Log message levels. + * - LIBUSB_LOG_LEVEL_NONE (0) : no messages ever printed by the library (default) + * - LIBUSB_LOG_LEVEL_ERROR (1) : error messages are printed to stderr + * - LIBUSB_LOG_LEVEL_WARNING (2) : warning and error messages are printed to stderr + * - LIBUSB_LOG_LEVEL_INFO (3) : informational messages are printed to stdout, warning + * and error messages are printed to stderr + * - LIBUSB_LOG_LEVEL_DEBUG (4) : debug and informational messages are printed to stdout, + * warnings and errors to stderr + */ +enum libusb_log_level { + LIBUSB_LOG_LEVEL_NONE = 0, + LIBUSB_LOG_LEVEL_ERROR, + LIBUSB_LOG_LEVEL_WARNING, + LIBUSB_LOG_LEVEL_INFO, + LIBUSB_LOG_LEVEL_DEBUG, +}; + +int LIBUSB_CALL libusb_init(libusb_context **ctx); +void LIBUSB_CALL libusb_exit(libusb_context *ctx); +void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); +const struct libusb_version * LIBUSB_CALL libusb_get_version(void); +int LIBUSB_CALL libusb_has_capability(uint32_t capability); +const char * LIBUSB_CALL libusb_error_name(int errcode); +int LIBUSB_CALL libusb_setlocale(const char *locale); +const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode); + +ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, + libusb_device ***list); +void LIBUSB_CALL libusb_free_device_list(libusb_device **list, + int unref_devices); +libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev); +void LIBUSB_CALL libusb_unref_device(libusb_device *dev); + +int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev, + int *config); +int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc); +int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config); +void LIBUSB_CALL libusb_free_config_descriptor( + struct libusb_config_descriptor *config); +int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor( + struct libusb_context *ctx, + const struct libusb_endpoint_descriptor *endpoint, + struct libusb_ss_endpoint_companion_descriptor **ep_comp); +void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor( + struct libusb_ss_endpoint_companion_descriptor *ep_comp); +int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device_handle *dev_handle, + struct libusb_bos_descriptor **bos); +void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos); +int LIBUSB_CALL libusb_get_usb_2_0_extension_descriptor( + struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension); +void LIBUSB_CALL libusb_free_usb_2_0_extension_descriptor( + struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension); +int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor( + struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap); +void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor( + struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap); +int LIBUSB_CALL libusb_get_container_id_descriptor(struct libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_container_id_descriptor **container_id); +void LIBUSB_CALL libusb_free_container_id_descriptor( + struct libusb_container_id_descriptor *container_id); +uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev); +int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t* port_numbers, int port_numbers_len); +LIBUSB_DEPRECATED_FOR(libusb_get_port_numbers) +int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t* path, uint8_t path_length); +libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); +int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev); +int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, + unsigned char endpoint); + +int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle); +void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); +libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev_handle, + int configuration); +int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev_handle, + int interface_number); + +libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id); + +int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, + int interface_number, int alternate_setting); +int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev_handle, + unsigned char endpoint); +int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_alloc_streams(libusb_device_handle *dev_handle, + uint32_t num_streams, unsigned char *endpoints, int num_endpoints); +int LIBUSB_CALL libusb_free_streams(libusb_device_handle *dev_handle, + unsigned char *endpoints, int num_endpoints); + +unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle, + size_t length); +int LIBUSB_CALL libusb_dev_mem_free(libusb_device_handle *dev_handle, + unsigned char *buffer, size_t length); + +int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_set_auto_detach_kernel_driver( + libusb_device_handle *dev_handle, int enable); + +/* async I/O */ + +/** \ingroup libusb_asyncio + * Get the data section of a control transfer. This convenience function is here + * to remind you that the data does not start until 8 bytes into the actual + * buffer, as the setup packet comes first. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns pointer to the first byte of the data section + */ +static inline unsigned char *libusb_control_transfer_get_data( + struct libusb_transfer *transfer) +{ + return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; +} + +/** \ingroup libusb_asyncio + * Get the control setup packet of a control transfer. This convenience + * function is here to remind you that the control setup occupies the first + * 8 bytes of the transfer data buffer. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns a casted pointer to the start of the transfer data buffer + */ +static inline struct libusb_control_setup *libusb_control_transfer_get_setup( + struct libusb_transfer *transfer) +{ + return (struct libusb_control_setup *)(void *) transfer->buffer; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the setup packet (first 8 bytes of the data + * buffer) for a control transfer. The wIndex, wValue and wLength values should + * be given in host-endian byte order. + * + * \param buffer buffer to output the setup packet into + * This pointer must be aligned to at least 2 bytes boundary. + * \param bmRequestType see the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field of + * \ref libusb_control_setup + * \param bRequest see the + * \ref libusb_control_setup::bRequest "bRequest" field of + * \ref libusb_control_setup + * \param wValue see the + * \ref libusb_control_setup::wValue "wValue" field of + * \ref libusb_control_setup + * \param wIndex see the + * \ref libusb_control_setup::wIndex "wIndex" field of + * \ref libusb_control_setup + * \param wLength see the + * \ref libusb_control_setup::wLength "wLength" field of + * \ref libusb_control_setup + */ +static inline void libusb_fill_control_setup(unsigned char *buffer, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint16_t wLength) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer; + setup->bmRequestType = bmRequestType; + setup->bRequest = bRequest; + setup->wValue = libusb_cpu_to_le16(wValue); + setup->wIndex = libusb_cpu_to_le16(wIndex); + setup->wLength = libusb_cpu_to_le16(wLength); +} + +struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets); +int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer); +int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_transfer_set_stream_id( + struct libusb_transfer *transfer, uint32_t stream_id); +uint32_t LIBUSB_CALL libusb_transfer_get_stream_id( + struct libusb_transfer *transfer); + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a control transfer. + * + * If you pass a transfer buffer to this function, the first 8 bytes will + * be interpreted as a control setup packet, and the wLength field will be + * used to automatically populate the \ref libusb_transfer::length "length" + * field of the transfer. Therefore the recommended approach is: + * -# Allocate a suitably sized data buffer (including space for control setup) + * -# Call libusb_fill_control_setup() + * -# If this is a host-to-device transfer with a data stage, put the data + * in place after the setup packet + * -# Call this function + * -# Call libusb_submit_transfer() + * + * It is also legal to pass a NULL buffer to this function, in which case this + * function will not attempt to populate the length field. Remember that you + * must then populate the buffer and length fields later. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param buffer data buffer. If provided, this function will interpret the + * first 8 bytes as a setup packet and infer the transfer length from that. + * This pointer must be aligned to at least 2 bytes boundary. + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_control_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, + unsigned int timeout) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer; + transfer->dev_handle = dev_handle; + transfer->endpoint = 0; + transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buffer; + if (setup) + transfer->length = (int) (LIBUSB_CONTROL_SETUP_SIZE + + libusb_le16_to_cpu(setup->wLength)); + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer using bulk streams. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param stream_id bulk stream id for this transfer + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_stream_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, uint32_t stream_id, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, + length, callback, user_data, timeout); + transfer->type = LIBUSB_TRANSFER_TYPE_BULK_STREAM; + libusb_transfer_set_stream_id(transfer, stream_id); +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an interrupt transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_interrupt_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an isochronous transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param num_iso_packets the number of isochronous packets + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, int num_iso_packets, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->num_iso_packets = num_iso_packets; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Convenience function to set the length of all packets in an isochronous + * transfer, based on the num_iso_packets field in the transfer structure. + * + * \param transfer a transfer + * \param length the length to set in each isochronous packet descriptor + * \see libusb_get_max_packet_size() + */ +static inline void libusb_set_iso_packet_lengths( + struct libusb_transfer *transfer, unsigned int length) +{ + int i; + for (i = 0; i < transfer->num_iso_packets; i++) + transfer->iso_packet_desc[i].length = length; +} + +/** \ingroup libusb_asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer. + * + * This is a thorough function which loops through all preceding packets, + * accumulating their lengths to find the position of the specified packet. + * Typically you will assign equal lengths to each packet in the transfer, + * and hence the above method is sub-optimal. You may wish to use + * libusb_get_iso_packet_buffer_simple() instead. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer_simple() + */ +static inline unsigned char *libusb_get_iso_packet_buffer( + struct libusb_transfer *transfer, unsigned int packet) +{ + int i; + size_t offset = 0; + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = (int) packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + for (i = 0; i < _packet; i++) + offset += transfer->iso_packet_desc[i].length; + + return transfer->buffer + offset; +} + +/** \ingroup libusb_asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer, for transfers where each + * packet is of identical size. + * + * This function relies on the assumption that every packet within the transfer + * is of identical size to the first packet. Calculating the location of + * the packet buffer is then just a simple calculation: + * buffer + (packet_size * packet) + * + * Do not use this function on transfers other than those that have identical + * packet lengths for each packet. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer() + */ +static inline unsigned char *libusb_get_iso_packet_buffer_simple( + struct libusb_transfer *transfer, unsigned int packet) +{ + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = (int) packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + return transfer->buffer + ((int) transfer->iso_packet_desc[0].length * _packet); +} + +/* sync I/O */ + +int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout); + +int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +/** \ingroup libusb_desc + * Retrieve a descriptor from the default control pipe. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. + * + * \param dev_handle a device handle + * \param desc_type the descriptor type, see \ref libusb_descriptor_type + * \param desc_index the index of the descriptor to retrieve + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + */ +static inline int libusb_get_descriptor(libusb_device_handle *dev_handle, + uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) +{ + return libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t) ((desc_type << 8) | desc_index), + 0, data, (uint16_t) length, 1000); +} + +/** \ingroup libusb_desc + * Retrieve a descriptor from a device. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. The string returned is Unicode, as + * detailed in the USB specifications. + * + * \param dev_handle a device handle + * \param desc_index the index of the descriptor to retrieve + * \param langid the language ID for the string descriptor + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + * \see libusb_get_string_descriptor_ascii() + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev_handle, + uint8_t desc_index, uint16_t langid, unsigned char *data, int length) +{ + return libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t)((LIBUSB_DT_STRING << 8) | desc_index), + langid, data, (uint16_t) length, 1000); +} + +int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle, + uint8_t desc_index, unsigned char *data, int length); + +/* polling and timeouts */ + +int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx); +void LIBUSB_CALL libusb_interrupt_event_handler(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx); +int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); + +int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_handle_events_timeout_completed(libusb_context *ctx, + struct timeval *tv, int *completed); +int LIBUSB_CALL libusb_handle_events(libusb_context *ctx); +int LIBUSB_CALL libusb_handle_events_completed(libusb_context *ctx, int *completed); +int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx); +int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx, + struct timeval *tv); + +/** \ingroup libusb_poll + * File descriptor for polling + */ +struct libusb_pollfd { + /** Numeric file descriptor */ + int fd; + + /** Event flags to poll for from . POLLIN indicates that you + * should monitor this file descriptor for becoming ready to read from, + * and POLLOUT indicates that you should monitor this file descriptor for + * nonblocking write readiness. */ + short events; +}; + +/** \ingroup libusb_poll + * Callback function, invoked when a new file descriptor should be added + * to the set of file descriptors monitored for events. + * \param fd the new file descriptor + * \param events events to monitor for, see \ref libusb_pollfd for a + * description + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_added_cb)(int fd, short events, + void *user_data); + +/** \ingroup libusb_poll + * Callback function, invoked when a file descriptor should be removed from + * the set of file descriptors being monitored for events. After returning + * from this callback, do not use that file descriptor again. + * \param fd the file descriptor to stop monitoring + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_removed_cb)(int fd, void *user_data); + +const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( + libusb_context *ctx); +void LIBUSB_CALL libusb_free_pollfds(const struct libusb_pollfd **pollfds); +void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data); + +/** \ingroup libusb_hotplug + * Callback handle. + * + * Callbacks handles are generated by libusb_hotplug_register_callback() + * and can be used to deregister callbacks. Callback handles are unique + * per libusb_context and it is safe to call libusb_hotplug_deregister_callback() + * on an already deregisted callback. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * For more information, see \ref libusb_hotplug. + */ +typedef int libusb_hotplug_callback_handle; + +/** \ingroup libusb_hotplug + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * Flags for hotplug events */ +typedef enum { + /** Default value when not using any flags. */ + LIBUSB_HOTPLUG_NO_FLAGS = 0, + + /** Arm the callback and fire it for all matching currently attached devices. */ + LIBUSB_HOTPLUG_ENUMERATE = 1<<0, +} libusb_hotplug_flag; + +/** \ingroup libusb_hotplug + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * Hotplug events */ +typedef enum { + /** A device has been plugged in and is ready to use */ + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01, + + /** A device has left and is no longer available. + * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device. + * It is safe to call libusb_get_device_descriptor on a device that has left */ + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 0x02, +} libusb_hotplug_event; + +/** \ingroup libusb_hotplug + * Wildcard matching for hotplug events */ +#define LIBUSB_HOTPLUG_MATCH_ANY -1 + +/** \ingroup libusb_hotplug + * Hotplug callback function type. When requesting hotplug event notifications, + * you pass a pointer to a callback function of this type. + * + * This callback may be called by an internal event thread and as such it is + * recommended the callback do minimal processing before returning. + * + * libusb will call this function later, when a matching event had happened on + * a matching device. See \ref libusb_hotplug for more information. + * + * It is safe to call either libusb_hotplug_register_callback() or + * libusb_hotplug_deregister_callback() from within a callback function. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param ctx context of this notification + * \param device libusb_device this event occurred on + * \param event event that occurred + * \param user_data user data provided when this callback was registered + * \returns bool whether this callback is finished processing events. + * returning 1 will cause this callback to be deregistered + */ +typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, + libusb_device *device, + libusb_hotplug_event event, + void *user_data); + +/** \ingroup libusb_hotplug + * Register a hotplug callback function + * + * Register a callback with the libusb_context. The callback will fire + * when a matching event occurs on a matching device. The callback is + * armed until either it is deregistered with libusb_hotplug_deregister_callback() + * or the supplied callback returns 1 to indicate it is finished processing events. + * + * If the \ref LIBUSB_HOTPLUG_ENUMERATE is passed the callback will be + * called with a \ref LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED for all devices + * already plugged into the machine. Note that libusb modifies its internal + * device list from a separate thread, while calling hotplug callbacks from + * libusb_handle_events(), so it is possible for a device to already be present + * on, or removed from, its internal device list, while the hotplug callbacks + * still need to be dispatched. This means that when using \ref + * LIBUSB_HOTPLUG_ENUMERATE, your callback may be called twice for the arrival + * of the same device, once from libusb_hotplug_register_callback() and once + * from libusb_handle_events(); and/or your callback may be called for the + * removal of a device for which an arrived call was never made. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param[in] ctx context to register this callback with + * \param[in] events bitwise or of events that will trigger this callback. See \ref + * libusb_hotplug_event + * \param[in] flags hotplug callback flags. See \ref libusb_hotplug_flag + * \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] cb_fn the function to be invoked on a matching event/device + * \param[in] user_data user data to pass to the callback function + * \param[out] callback_handle pointer to store the handle of the allocated callback (can be NULL) + * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure + */ +int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx, + libusb_hotplug_event events, + libusb_hotplug_flag flags, + int vendor_id, int product_id, + int dev_class, + libusb_hotplug_callback_fn cb_fn, + void *user_data, + libusb_hotplug_callback_handle *callback_handle); + +/** \ingroup libusb_hotplug + * Deregisters a hotplug callback. + * + * Deregister a callback from a libusb_context. This function is safe to call from within + * a hotplug callback. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param[in] ctx context this callback is registered with + * \param[in] callback_handle the handle of the callback to deregister + */ +void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/hackrf-sweep/lib/libusb-1.0.21/libusb-1.0.def b/src/hackrf-sweep/lib/libusb-1.0.21/libusb-1.0.def new file mode 100644 index 0000000..a38a6a6 --- /dev/null +++ b/src/hackrf-sweep/lib/libusb-1.0.21/libusb-1.0.def @@ -0,0 +1,346 @@ +LIBRARY "libusb-1.0.dll" +EXPORTS + _libusb_alloc_streams + _libusb_alloc_streams@16 = _libusb_alloc_streams + _libusb_alloc_transfer + _libusb_alloc_transfer@4 = _libusb_alloc_transfer + _libusb_attach_kernel_driver + _libusb_attach_kernel_driver@8 = _libusb_attach_kernel_driver + _libusb_bulk_transfer + _libusb_bulk_transfer@24 = _libusb_bulk_transfer + _libusb_cancel_transfer + _libusb_cancel_transfer@4 = _libusb_cancel_transfer + _libusb_claim_interface + _libusb_claim_interface@8 = _libusb_claim_interface + _libusb_clear_halt + _libusb_clear_halt@8 = _libusb_clear_halt + _libusb_close + _libusb_close@4 = _libusb_close + _libusb_control_transfer + _libusb_control_transfer@32 = _libusb_control_transfer + _libusb_detach_kernel_driver + _libusb_detach_kernel_driver@8 = _libusb_detach_kernel_driver + _libusb_dev_mem_alloc + _libusb_dev_mem_alloc@8 = _libusb_dev_mem_alloc + _libusb_dev_mem_free + _libusb_dev_mem_free@12 = _libusb_dev_mem_free + _libusb_error_name + _libusb_error_name@4 = _libusb_error_name + _libusb_event_handler_active + _libusb_event_handler_active@4 = _libusb_event_handler_active + _libusb_event_handling_ok + _libusb_event_handling_ok@4 = _libusb_event_handling_ok + _libusb_exit + _libusb_exit@4 = _libusb_exit + _libusb_free_bos_descriptor + _libusb_free_bos_descriptor@4 = _libusb_free_bos_descriptor + _libusb_free_config_descriptor + _libusb_free_config_descriptor@4 = _libusb_free_config_descriptor + _libusb_free_container_id_descriptor + _libusb_free_container_id_descriptor@4 = _libusb_free_container_id_descriptor + _libusb_free_device_list + _libusb_free_device_list@8 = _libusb_free_device_list + _libusb_free_pollfds + _libusb_free_pollfds@4 = _libusb_free_pollfds + _libusb_free_ss_endpoint_companion_descriptor + _libusb_free_ss_endpoint_companion_descriptor@4 = _libusb_free_ss_endpoint_companion_descriptor + _libusb_free_ss_usb_device_capability_descriptor + _libusb_free_ss_usb_device_capability_descriptor@4 = _libusb_free_ss_usb_device_capability_descriptor + _libusb_free_streams + _libusb_free_streams@12 = _libusb_free_streams + _libusb_free_transfer + _libusb_free_transfer@4 = _libusb_free_transfer + _libusb_free_usb_2_0_extension_descriptor + _libusb_free_usb_2_0_extension_descriptor@4 = _libusb_free_usb_2_0_extension_descriptor + _libusb_get_active_config_descriptor + _libusb_get_active_config_descriptor@8 = _libusb_get_active_config_descriptor + _libusb_get_bos_descriptor + _libusb_get_bos_descriptor@8 = _libusb_get_bos_descriptor + _libusb_get_bus_number + _libusb_get_bus_number@4 = _libusb_get_bus_number + _libusb_get_config_descriptor + _libusb_get_config_descriptor@12 = _libusb_get_config_descriptor + _libusb_get_config_descriptor_by_value + _libusb_get_config_descriptor_by_value@12 = _libusb_get_config_descriptor_by_value + _libusb_get_configuration + _libusb_get_configuration@8 = _libusb_get_configuration + _libusb_get_container_id_descriptor + _libusb_get_container_id_descriptor@12 = _libusb_get_container_id_descriptor + _libusb_get_device + _libusb_get_device@4 = _libusb_get_device + _libusb_get_device_address + _libusb_get_device_address@4 = _libusb_get_device_address + _libusb_get_device_descriptor + _libusb_get_device_descriptor@8 = _libusb_get_device_descriptor + _libusb_get_device_list + _libusb_get_device_list@8 = _libusb_get_device_list + _libusb_get_device_speed + _libusb_get_device_speed@4 = _libusb_get_device_speed + _libusb_get_max_iso_packet_size + _libusb_get_max_iso_packet_size@8 = _libusb_get_max_iso_packet_size + _libusb_get_max_packet_size + _libusb_get_max_packet_size@8 = _libusb_get_max_packet_size + _libusb_get_next_timeout + _libusb_get_next_timeout@8 = _libusb_get_next_timeout + _libusb_get_parent + _libusb_get_parent@4 = _libusb_get_parent + _libusb_get_pollfds + _libusb_get_pollfds@4 = _libusb_get_pollfds + _libusb_get_port_number + _libusb_get_port_number@4 = _libusb_get_port_number + _libusb_get_port_numbers + _libusb_get_port_numbers@12 = _libusb_get_port_numbers + _libusb_get_port_path + _libusb_get_port_path@16 = _libusb_get_port_path + _libusb_get_ss_endpoint_companion_descriptor + _libusb_get_ss_endpoint_companion_descriptor@12 = _libusb_get_ss_endpoint_companion_descriptor + _libusb_get_ss_usb_device_capability_descriptor + _libusb_get_ss_usb_device_capability_descriptor@12 = _libusb_get_ss_usb_device_capability_descriptor + _libusb_get_string_descriptor_ascii + _libusb_get_string_descriptor_ascii@16 = _libusb_get_string_descriptor_ascii + _libusb_get_usb_2_0_extension_descriptor + _libusb_get_usb_2_0_extension_descriptor@12 = _libusb_get_usb_2_0_extension_descriptor + _libusb_get_version + _libusb_get_version@0 = _libusb_get_version + _libusb_handle_events + _libusb_handle_events@4 = _libusb_handle_events + _libusb_handle_events_completed + _libusb_handle_events_completed@8 = _libusb_handle_events_completed + _libusb_handle_events_locked + _libusb_handle_events_locked@8 = _libusb_handle_events_locked + _libusb_handle_events_timeout + _libusb_handle_events_timeout@8 = _libusb_handle_events_timeout + _libusb_handle_events_timeout_completed + _libusb_handle_events_timeout_completed@12 = _libusb_handle_events_timeout_completed + _libusb_has_capability + _libusb_has_capability@4 = _libusb_has_capability + _libusb_hotplug_deregister_callback + _libusb_hotplug_deregister_callback@8 = _libusb_hotplug_deregister_callback + _libusb_hotplug_register_callback + _libusb_hotplug_register_callback@36 = _libusb_hotplug_register_callback + _libusb_init + _libusb_init@4 = _libusb_init + _libusb_interrupt_event_handler + _libusb_interrupt_event_handler@4 = _libusb_interrupt_event_handler + _libusb_interrupt_transfer + _libusb_interrupt_transfer@24 = _libusb_interrupt_transfer + _libusb_kernel_driver_active + _libusb_kernel_driver_active@8 = _libusb_kernel_driver_active + _libusb_lock_event_waiters + _libusb_lock_event_waiters@4 = _libusb_lock_event_waiters + _libusb_lock_events + _libusb_lock_events@4 = _libusb_lock_events + _libusb_open + _libusb_open@8 = _libusb_open + _libusb_open_device_with_vid_pid + _libusb_open_device_with_vid_pid@12 = _libusb_open_device_with_vid_pid + _libusb_pollfds_handle_timeouts + _libusb_pollfds_handle_timeouts@4 = _libusb_pollfds_handle_timeouts + _libusb_ref_device + _libusb_ref_device@4 = _libusb_ref_device + _libusb_release_interface + _libusb_release_interface@8 = _libusb_release_interface + _libusb_reset_device + _libusb_reset_device@4 = _libusb_reset_device + _libusb_set_auto_detach_kernel_driver + _libusb_set_auto_detach_kernel_driver@8 = _libusb_set_auto_detach_kernel_driver + _libusb_set_configuration + _libusb_set_configuration@8 = _libusb_set_configuration + _libusb_set_debug + _libusb_set_debug@8 = _libusb_set_debug + _libusb_set_interface_alt_setting + _libusb_set_interface_alt_setting@12 = _libusb_set_interface_alt_setting + _libusb_set_pollfd_notifiers + _libusb_set_pollfd_notifiers@16 = _libusb_set_pollfd_notifiers + _libusb_setlocale + _libusb_setlocale@4 = _libusb_setlocale + _libusb_strerror + _libusb_strerror@4 = _libusb_strerror + _libusb_submit_transfer + _libusb_submit_transfer@4 = _libusb_submit_transfer + _libusb_transfer_get_stream_id + _libusb_transfer_get_stream_id@4 = _libusb_transfer_get_stream_id + _libusb_transfer_set_stream_id + _libusb_transfer_set_stream_id@8 = _libusb_transfer_set_stream_id + _libusb_try_lock_events + _libusb_try_lock_events@4 = _libusb_try_lock_events + _libusb_unlock_event_waiters + _libusb_unlock_event_waiters@4 = _libusb_unlock_event_waiters + _libusb_unlock_events + _libusb_unlock_events@4 = _libusb_unlock_events + _libusb_unref_device + _libusb_unref_device@4 = _libusb_unref_device + _libusb_wait_for_event + _libusb_wait_for_event@8 = _libusb_wait_for_event + libusb_alloc_streams + libusb_alloc_streams@16 = libusb_alloc_streams + libusb_alloc_transfer + libusb_alloc_transfer@4 = libusb_alloc_transfer + libusb_attach_kernel_driver + libusb_attach_kernel_driver@8 = libusb_attach_kernel_driver + libusb_bulk_transfer + libusb_bulk_transfer@24 = libusb_bulk_transfer + libusb_cancel_transfer + libusb_cancel_transfer@4 = libusb_cancel_transfer + libusb_claim_interface + libusb_claim_interface@8 = libusb_claim_interface + libusb_clear_halt + libusb_clear_halt@8 = libusb_clear_halt + libusb_close + libusb_close@4 = libusb_close + libusb_control_transfer + libusb_control_transfer@32 = libusb_control_transfer + libusb_detach_kernel_driver + libusb_detach_kernel_driver@8 = libusb_detach_kernel_driver + libusb_dev_mem_alloc + libusb_dev_mem_alloc@8 = libusb_dev_mem_alloc + libusb_dev_mem_free + libusb_dev_mem_free@12 = libusb_dev_mem_free + libusb_error_name + libusb_error_name@4 = libusb_error_name + libusb_event_handler_active + libusb_event_handler_active@4 = libusb_event_handler_active + libusb_event_handling_ok + libusb_event_handling_ok@4 = libusb_event_handling_ok + libusb_exit + libusb_exit@4 = libusb_exit + libusb_free_bos_descriptor + libusb_free_bos_descriptor@4 = libusb_free_bos_descriptor + libusb_free_config_descriptor + libusb_free_config_descriptor@4 = libusb_free_config_descriptor + libusb_free_container_id_descriptor + libusb_free_container_id_descriptor@4 = libusb_free_container_id_descriptor + libusb_free_device_list + libusb_free_device_list@8 = libusb_free_device_list + libusb_free_pollfds + libusb_free_pollfds@4 = libusb_free_pollfds + libusb_free_ss_endpoint_companion_descriptor + libusb_free_ss_endpoint_companion_descriptor@4 = libusb_free_ss_endpoint_companion_descriptor + libusb_free_ss_usb_device_capability_descriptor + libusb_free_ss_usb_device_capability_descriptor@4 = libusb_free_ss_usb_device_capability_descriptor + libusb_free_streams + libusb_free_streams@12 = libusb_free_streams + libusb_free_transfer + libusb_free_transfer@4 = libusb_free_transfer + libusb_free_usb_2_0_extension_descriptor + libusb_free_usb_2_0_extension_descriptor@4 = libusb_free_usb_2_0_extension_descriptor + libusb_get_active_config_descriptor + libusb_get_active_config_descriptor@8 = libusb_get_active_config_descriptor + libusb_get_bos_descriptor + libusb_get_bos_descriptor@8 = libusb_get_bos_descriptor + libusb_get_bus_number + libusb_get_bus_number@4 = libusb_get_bus_number + libusb_get_config_descriptor + libusb_get_config_descriptor@12 = libusb_get_config_descriptor + libusb_get_config_descriptor_by_value + libusb_get_config_descriptor_by_value@12 = libusb_get_config_descriptor_by_value + libusb_get_configuration + libusb_get_configuration@8 = libusb_get_configuration + libusb_get_container_id_descriptor + libusb_get_container_id_descriptor@12 = libusb_get_container_id_descriptor + libusb_get_device + libusb_get_device@4 = libusb_get_device + libusb_get_device_address + libusb_get_device_address@4 = libusb_get_device_address + libusb_get_device_descriptor + libusb_get_device_descriptor@8 = libusb_get_device_descriptor + libusb_get_device_list + libusb_get_device_list@8 = libusb_get_device_list + libusb_get_device_speed + libusb_get_device_speed@4 = libusb_get_device_speed + libusb_get_max_iso_packet_size + libusb_get_max_iso_packet_size@8 = libusb_get_max_iso_packet_size + libusb_get_max_packet_size + libusb_get_max_packet_size@8 = libusb_get_max_packet_size + libusb_get_next_timeout + libusb_get_next_timeout@8 = libusb_get_next_timeout + libusb_get_parent + libusb_get_parent@4 = libusb_get_parent + libusb_get_pollfds + libusb_get_pollfds@4 = libusb_get_pollfds + libusb_get_port_number + libusb_get_port_number@4 = libusb_get_port_number + libusb_get_port_numbers + libusb_get_port_numbers@12 = libusb_get_port_numbers + libusb_get_port_path + libusb_get_port_path@16 = libusb_get_port_path + libusb_get_ss_endpoint_companion_descriptor + libusb_get_ss_endpoint_companion_descriptor@12 = libusb_get_ss_endpoint_companion_descriptor + libusb_get_ss_usb_device_capability_descriptor + libusb_get_ss_usb_device_capability_descriptor@12 = libusb_get_ss_usb_device_capability_descriptor + libusb_get_string_descriptor_ascii + libusb_get_string_descriptor_ascii@16 = libusb_get_string_descriptor_ascii + libusb_get_usb_2_0_extension_descriptor + libusb_get_usb_2_0_extension_descriptor@12 = libusb_get_usb_2_0_extension_descriptor + libusb_get_version + libusb_get_version@0 = libusb_get_version + libusb_handle_events + libusb_handle_events@4 = libusb_handle_events + libusb_handle_events_completed + libusb_handle_events_completed@8 = libusb_handle_events_completed + libusb_handle_events_locked + libusb_handle_events_locked@8 = libusb_handle_events_locked + libusb_handle_events_timeout + libusb_handle_events_timeout@8 = libusb_handle_events_timeout + libusb_handle_events_timeout_completed + libusb_handle_events_timeout_completed@12 = libusb_handle_events_timeout_completed + libusb_has_capability + libusb_has_capability@4 = libusb_has_capability + libusb_hotplug_deregister_callback + libusb_hotplug_deregister_callback@8 = libusb_hotplug_deregister_callback + libusb_hotplug_register_callback + libusb_hotplug_register_callback@36 = libusb_hotplug_register_callback + libusb_init + libusb_init@4 = libusb_init + libusb_interrupt_event_handler + libusb_interrupt_event_handler@4 = libusb_interrupt_event_handler + libusb_interrupt_transfer + libusb_interrupt_transfer@24 = libusb_interrupt_transfer + libusb_kernel_driver_active + libusb_kernel_driver_active@8 = libusb_kernel_driver_active + libusb_lock_event_waiters + libusb_lock_event_waiters@4 = libusb_lock_event_waiters + libusb_lock_events + libusb_lock_events@4 = libusb_lock_events + libusb_open + libusb_open@8 = libusb_open + libusb_open_device_with_vid_pid + libusb_open_device_with_vid_pid@12 = libusb_open_device_with_vid_pid + libusb_pollfds_handle_timeouts + libusb_pollfds_handle_timeouts@4 = libusb_pollfds_handle_timeouts + libusb_ref_device + libusb_ref_device@4 = libusb_ref_device + libusb_release_interface + libusb_release_interface@8 = libusb_release_interface + libusb_reset_device + libusb_reset_device@4 = libusb_reset_device + libusb_set_auto_detach_kernel_driver + libusb_set_auto_detach_kernel_driver@8 = libusb_set_auto_detach_kernel_driver + libusb_set_configuration + libusb_set_configuration@8 = libusb_set_configuration + libusb_set_debug + libusb_set_debug@8 = libusb_set_debug + libusb_set_interface_alt_setting + libusb_set_interface_alt_setting@12 = libusb_set_interface_alt_setting + libusb_set_pollfd_notifiers + libusb_set_pollfd_notifiers@16 = libusb_set_pollfd_notifiers + libusb_setlocale + libusb_setlocale@4 = libusb_setlocale + libusb_strerror + libusb_strerror@4 = libusb_strerror + libusb_submit_transfer + libusb_submit_transfer@4 = libusb_submit_transfer + libusb_transfer_get_stream_id + libusb_transfer_get_stream_id@4 = libusb_transfer_get_stream_id + libusb_transfer_set_stream_id + libusb_transfer_set_stream_id@8 = libusb_transfer_set_stream_id + libusb_try_lock_events + libusb_try_lock_events@4 = libusb_try_lock_events + libusb_unlock_event_waiters + libusb_unlock_event_waiters@4 = libusb_unlock_event_waiters + libusb_unlock_events + libusb_unlock_events@4 = libusb_unlock_events + libusb_unref_device + libusb_unref_device@4 = libusb_unref_device + libusb_wait_for_event + libusb_wait_for_event@8 = libusb_wait_for_event diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/HackRFSweepSpectrumAnalyzer.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/HackRFSweepSpectrumAnalyzer.java index c288b90..58aee8f 100644 --- a/src/hackrf-sweep/src-java/jspectrumanalyzer/HackRFSweepSpectrumAnalyzer.java +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/HackRFSweepSpectrumAnalyzer.java @@ -5,12 +5,24 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Font; +import java.awt.Frame; import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.RenderingHints; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.math.BigDecimal; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.concurrent.ArrayBlockingQueue; @@ -18,6 +30,8 @@ import javax.swing.ImageIcon; import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; import javax.swing.JSplitPane; import javax.swing.SwingUtilities; import javax.swing.UIManager; @@ -34,104 +48,823 @@ import org.jfree.chart.event.ChartProgressEvent; import org.jfree.chart.event.ChartProgressListener; import org.jfree.chart.event.OverlayChangeListener; +import org.jfree.chart.event.PlotChangeEvent; +import org.jfree.chart.event.PlotChangeListener; import org.jfree.chart.panel.Overlay; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.ValueMarker; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.chart.title.TextTitle; import org.jfree.data.Range; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; +import org.jfree.ui.Align; +import org.jfree.ui.HorizontalAlignment; import org.jfree.ui.RectangleAnchor; +import org.jfree.ui.RectangleEdge; import org.jfree.ui.RectangleInsets; import org.jfree.ui.TextAnchor; -import jspectrumanalyzer.core.DatasetSpectrum; +import jspectrumanalyzer.capture.ScreenCapture; import jspectrumanalyzer.core.DatasetSpectrumPeak; import jspectrumanalyzer.core.FFTBins; +import jspectrumanalyzer.core.FrequencyAllocationTable; +import jspectrumanalyzer.core.FrequencyAllocations; +import jspectrumanalyzer.core.FrequencyBand; +import jspectrumanalyzer.core.FrequencyRange; import jspectrumanalyzer.core.HackRFSettings; -import jspectrumanalyzer.core.HackRFSweepSettingsUI; -import jspectrumanalyzer.core.PowerCalibration; +import jspectrumanalyzer.core.PersistentDisplay; import jspectrumanalyzer.core.SpurFilter; -import jspectrumanalyzer.core.WaterfallPlot; +import jspectrumanalyzer.core.jfc.XYSeriesCollectionImmutable; import jspectrumanalyzer.nativebridge.HackRFSweepDataCallback; import jspectrumanalyzer.nativebridge.HackRFSweepNativeBridge; +import jspectrumanalyzer.ui.HackRFSweepSettingsUI; +import jspectrumanalyzer.ui.WaterfallPlot; +import shared.mvc.MVCController; +import shared.mvc.ModelValue; +import shared.mvc.ModelValue.ModelValueBoolean; +import shared.mvc.ModelValue.ModelValueInt; + +public class HackRFSweepSpectrumAnalyzer implements HackRFSettings, HackRFSweepDataCallback { + + private static class PerformanceEntry{ + final String name; + long nanosSum; + int count; + public PerformanceEntry(String name) { + this.name = name; + } + public void addDrawingTime(long nanos) { + nanosSum += nanos; + count++; + } + public void reset() { + count = 0; + nanosSum = 0; + } + @Override + public String toString() { + return name; + } + } + + private static class RuntimePerformanceWatch { + /** + * incoming full spectrum updates from the hardware + */ + int hwFullSpectrumRefreshes = 0; + volatile long lastStatisticsRefreshed = System.currentTimeMillis(); + PerformanceEntry persisentDisplay = new PerformanceEntry("Pers.disp"); + PerformanceEntry waterfallUpdate = new PerformanceEntry("Wtrfall.upd"); + PerformanceEntry waterfallDraw = new PerformanceEntry("Wtrfll.drw"); + PerformanceEntry chartDrawing = new PerformanceEntry("Spectr.chart"); + PerformanceEntry spurFilter = new PerformanceEntry("Spur.fil"); + + private ArrayList entries = new ArrayList<>(); + public RuntimePerformanceWatch() { + entries.add(persisentDisplay); + entries.add(waterfallUpdate); + entries.add(waterfallDraw); + entries.add(chartDrawing); + entries.add(spurFilter); + } + + public synchronized String generateStatistics() { + long timeElapsed = System.currentTimeMillis() - lastStatisticsRefreshed; + if (timeElapsed <= 0) + timeElapsed = 1; + StringBuilder b = new StringBuilder(); + long sumNanos = 0; + for (PerformanceEntry entry : entries) { + sumNanos += entry.nanosSum; + float callsPerSec = entry.count/(timeElapsed/1000f); + b.append(entry.name).append(String.format(" %3dms (%5.1f calls/s) \n", entry.nanosSum/1000000, callsPerSec)); + } + b.append(String.format("Total: %4dms draw time/s: ", sumNanos/1000000)); + return b.toString(); +// double timeSpentDrawingChartPerSec = chartDrawingSum / (timeElapsed / 1000d) / 1000d; +// return String.format("Spectrum refreshes: %d / Chart redraws: %d / Drawing time in 1 sec %.2fs", +// hwFullSpectrumRefreshes, chartRedrawed, timeSpentDrawingChartPerSec); + + } + + public synchronized void reset() { + hwFullSpectrumRefreshes = 0; + for (PerformanceEntry dataDrawingEntry : entries) { + dataDrawingEntry.reset(); + } + lastStatisticsRefreshed = System.currentTimeMillis(); + } + } + + /** + * Color palette for UI + */ + protected static class ColorScheme { + Color palette0 = Color.white; + Color palette1 = new Color(0xe5e5e5); + Color palette2 = new Color(0xFCA311); + Color palette3 = new Color(0x14213D); + Color palette4 = Color.BLACK; + } + + public static final int SPECTRUM_PALETTE_SIZE_MIN = 5; + private static boolean captureGIF = false; -public class HackRFSweepSpectrumAnalyzer implements HackRFSettings, HackRFSweepDataCallback -{ - public static final int SPECTRUM_PALETTE_SIZE_MIN = 5; + private static long initTime = System.currentTimeMillis(); + + public static void main(String[] args) throws IOException { + // System.out.println(new File("").getAbsolutePath()); + if (args.length > 0) { + if (args[0].equals("capturegif")) { + captureGIF = true; + } + } + // try { Thread.sleep(20000); System.out.println("Started..."); } catch (InterruptedException e) {} - public static void main(String[] args) throws IOException - { -// System.out.println(new File("").getAbsolutePath()); new HackRFSweepSpectrumAnalyzer(); } - private XYSeriesCollection dataset = new XYSeriesCollection(); - private JFreeChart chart = ChartFactory.createXYLineChart("Spectrum analyzer", "Frequency [MHz]", "Power [dB]", dataset, - PlotOrientation.VERTICAL, false, false, false); - - private DatasetSpectrumPeak datasetSpectrum; - private int dropped = 0; - private JFrame f; - private boolean filterSpectrum; - private int lnaGain; - private ReentrantLock lock = new ReentrantLock(); - private int parameterFFTBinHz = 200000; - private int parameterGaindB = 40; - private int parameterMaxFreqMHz = 2500; - private int parameterMinFreqMHz = 2400; - private int parameterSamples = 8192; - private boolean parameterAntPower = false; - private ArrayBlockingQueue processingQueue = new ArrayBlockingQueue<>(1000); - private boolean showPeaks = false; - private float spectrumInitValue = -150; - private boolean spurRemoval = false; - private Thread threadHackrfSweep; - private ArrayBlockingQueue threadLaunchCommands = new ArrayBlockingQueue<>(1); - - private Thread threadLauncher; - private Thread threadProcessing; - private int vgaGain; - private ValueMarker waterfallPaletteEndMarker; - private ValueMarker waterfallPaletteStartMarker; - - private WaterfallPlot waterfallPlot; - - boolean isChartDrawing = false; - private ArrayList listeners = new ArrayList<>(); - private boolean isCapturing = true; - - public HackRFSweepSpectrumAnalyzer() - { - int axisWidthLeft = 70; - int axisWidthRight = 20; + public boolean flagIsHWSendingData = false; + private float alphaFreqAllocationTableBandsImage = 0.5f; + private float alphaPersistentDisplayImage = 1.0f; + private JFreeChart chart; + + private ModelValue chartDataArea = new ModelValue( + "Chart data area", new Rectangle2D.Double(0, 0, 1, 1)); + private XYSeriesCollectionImmutable chartDataset = new XYSeriesCollectionImmutable(); + private XYLineAndShapeRenderer chartLineRenderer; + private ChartPanel chartPanel; + private ColorScheme colors = new ColorScheme(); + private DatasetSpectrumPeak datasetSpectrum; + private int dropped = 0; + private volatile boolean flagManualGain = false; + private volatile boolean forceStopSweep = false; + /** + * Capture a GIF of the program for the GITHUB page + */ + private ScreenCapture gifCap = null; + private ArrayList hRFlisteners = new ArrayList<>(); + private ArrayBlockingQueue hwProcessingQueue = new ArrayBlockingQueue<>( + 1000); + private BufferedImage imageFrequencyAllocationTableBands = null; + private boolean isChartDrawing = false; + private ReentrantLock lock = new ReentrantLock(); + + private ModelValueBoolean parameterAntPower = new ModelValueBoolean( + "Ant power", false); + private ModelValueInt parameterFFTBinHz = new ModelValueInt( + "FFT Bin [Hz]", 100000); + private ModelValueBoolean parameterFilterSpectrum = new ModelValueBoolean( + "Filter", false); + private ModelValue parameterFrequency = new ModelValue<>( + "Frequency range", new FrequencyRange(2400, 2500)); + + private ModelValue parameterFrequencyAllocationTable = new ModelValue( + "Frequency allocation table", null); + + private ModelValueInt parameterGainLNA = new ModelValueInt("LNA Gain", + 0, 8, 0, 40); + private ModelValueInt parameterGainTotal = new ModelValueInt("Gain [dB]", + 40); + private ModelValueInt parameterGainVGA = new ModelValueInt("VGA Gain", + 0, 2, 0, 60); + private ModelValueBoolean parameterIsCapturingPaused = new ModelValueBoolean( + "Capturing paused", false); + + private ModelValueInt parameterPersistentDisplayPersTime = new ModelValueInt("Persistence time", 30, 1, 1, 60); + private ModelValueInt parameterPeakFallRateSecs = new ModelValueInt( + "Peak fall rate", 30); + private ModelValueBoolean parameterPersistentDisplay = new ModelValueBoolean( + "Persistent display", false); + + private ModelValueInt parameterSamples = new ModelValueInt("Samples", + 8192); + + private ModelValueBoolean parameterShowPeaks = new ModelValueBoolean( + "Show peaks", false); + + private ModelValueBoolean parameterDebugDisplay = new ModelValueBoolean("Debug", false); + + private ModelValue parameterSpectrumLineThickness = new ModelValue<>( + "Spectrum line thickness", new BigDecimal("1")); + private ModelValueInt parameterSpectrumPaletteSize = new ModelValueInt( + "Spectrum palette size", 0); + private ModelValueInt parameterSpectrumPaletteStart = new ModelValueInt( + "Spectrum palette start", 0); + + private ModelValueBoolean parameterSpurRemoval = new ModelValueBoolean( + "Spur removal", false); + private ModelValueBoolean parameterWaterfallVisible = new ModelValueBoolean( + "Waterfall visible", true); + private PersistentDisplay persistentDisplay = new PersistentDisplay(); + private float spectrumInitValue = -150; + private SpurFilter spurFilter; + private Thread threadHackrfSweep; + private ArrayBlockingQueue threadLaunchCommands = new ArrayBlockingQueue<>(1); + private Thread threadLauncher; + private Thread threadProcessing; + private TextTitle titleFreqBand = new TextTitle("", + new Font("Dialog", Font.PLAIN, 11)); + private RuntimePerformanceWatch perfWatch = new RuntimePerformanceWatch(); + private JFrame uiFrame; + private ValueMarker waterfallPaletteEndMarker; + private ValueMarker waterfallPaletteStartMarker; + private WaterfallPlot waterfallPlot; + private JLabel labelMessages; + + public HackRFSweepSpectrumAnalyzer() { + printInit(0); + + if (captureGIF) { +// parameterFrequency.setValue(new FrequencyRange(700, 2700)); + parameterFrequency.setValue(new FrequencyRange(2400, 2700)); + parameterGainTotal.setValue(60); + parameterSpurRemoval.setValue(true); + parameterPersistentDisplay.setValue(true); + parameterFFTBinHz.setValue(500000); + parameterFrequencyAllocationTable.setValue(new FrequencyAllocations().getTable().values().stream().findFirst().get()); + } - recalculateGains(parameterGaindB); + recalculateGains(parameterGainTotal.getValue()); - try - { + try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e1) { + e1.printStackTrace(); } - catch (Exception e1) + // UIManager.getLookAndFeelDefaults().put("TabbedPane.borderHightlightColor", Color.black); + // UIManager.getLookAndFeelDefaults().put("TabbedPane.background", Color.black); + // UIManager.getLookAndFeelDefaults().put("TabbedPane.contentAreaColor", Color.black); + // UIManager.getLookAndFeelDefaults().put("TabbedPane.darkShadow", Color.black); + // UIManager.getLookAndFeelDefaults().put("TabbedPane.focus", Color.black); + // UIManager.getLookAndFeelDefaults().put("TabbedPane.highlight", Color.black); + // UIManager.getLookAndFeelDefaults().put("TabbedPane.light", Color.black); + // UIManager.getLookAndFeelDefaults().put("TabbedPane.selected", Color.black); + // UIManager.getLookAndFeelDefaults().put("TabbedPane.selectedForeground", Color.black); + // UIManager.getLookAndFeelDefaults().put("TabbedPane.selectHighlight", Color.black); + // UIManager.getLookAndFeelDefaults().put("TabbedPane.shadow", Color.black); + // UIManager.getLookAndFeelDefaults().put("TabbedPane.tabAreaBackground", Color.black); + + Insets insets = new Insets(1, 1, 1, 1); + UIManager.getLookAndFeelDefaults().put("TabbedPane.contentBorderInsets", insets); + UIManager.getLookAndFeelDefaults().put("TabbedPane.selectedTabPadInsets", insets); + UIManager.getLookAndFeelDefaults().put("TabbedPane.tabAreaInsets", insets); + // UIManager.getLookAndFeelDefaults().put("", insets); + // UIManager.getLookAndFeelDefaults().put("", insets); + + // UIManager.getLookAndFeelDefaults().values().forEach((p) -> { + // System.out.println(p.toString()); + // }); + + setupChart(); + + setupChartMouseMarkers(); + + waterfallPlot = new WaterfallPlot(chartPanel, 300); + waterfallPaletteStartMarker = new ValueMarker(waterfallPlot.getSpectrumPaletteStart(), colors.palette2, + new BasicStroke(1f)); + waterfallPaletteEndMarker = new ValueMarker( + waterfallPlot.getSpectrumPaletteStart() + waterfallPlot.getSpectrumPaletteSize(), colors.palette2, + new BasicStroke(1f)); + // chart.getXYPlot().addRangeMarker(waterfallPaletteStartMarker); + // chart.getXYPlot().addRangeMarker(waterfallPaletteEndMarker); + + printInit(2); + + HackRFSweepSettingsUI settingsPanel = new HackRFSweepSettingsUI(this); + + printInit(3); + + + JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, chartPanel, waterfallPlot); + splitPane.setResizeWeight(0.8); + splitPane.setBorder(null); + + labelMessages = new JLabel("dsadasd"); + labelMessages.setForeground(Color.white); + labelMessages.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); + parameterDebugDisplay.addListener((debug) -> { + labelMessages.setVisible(debug); + }); + parameterDebugDisplay.callObservers(); + + JPanel splitPanePanel = new JPanel(new BorderLayout()); + splitPanePanel.setBackground(Color.black); + splitPanePanel.add(splitPane, BorderLayout.CENTER); + splitPanePanel.add(labelMessages, BorderLayout.SOUTH); + + uiFrame = new JFrame(); + uiFrame.setUndecorated(captureGIF); + uiFrame.setExtendedState(uiFrame.getExtendedState() | Frame.MAXIMIZED_BOTH); + uiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + uiFrame.setLayout(new BorderLayout()); + uiFrame.setTitle("Spectrum Analyzer - hackrf_sweep"); + uiFrame.add(splitPanePanel, BorderLayout.CENTER); + uiFrame.setMinimumSize(new Dimension(600, 600)); + uiFrame.add(settingsPanel, BorderLayout.EAST); + try { + uiFrame.setIconImage(new ImageIcon("program.png").getImage()); + } catch (Exception e) { + // e.printStackTrace(); + } + + printInit(4); + setupFrequencyAllocationTable(); + printInit(5); + + uiFrame.pack(); + uiFrame.setVisible(true); + + printInit(6); + + startLauncherThread(); + restartHackrfSweep(); + + /** + * register parameter observers + */ + setupParameterObservers(); + + //shutdown on exit + Runtime.getRuntime().addShutdownHook(new Thread(() -> stopHackrfSweep())); + + if (captureGIF) { + try { + gifCap = new ScreenCapture(uiFrame, 35 * 1, 10, 5, 760, 660, new File("screenshot.gif")); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + @Override + public ModelValueBoolean getAntennaPowerEnable() { + return parameterAntPower; + } + + @Override + public ModelValueInt getFFTBinHz() { + return parameterFFTBinHz; + } + + @Override + public ModelValue getFrequency() { + return parameterFrequency; + } + + @Override + public ModelValue getFrequencyAllocationTable() { + return parameterFrequencyAllocationTable; + } + + @Override + public ModelValueInt getGain() { + return parameterGainTotal; + } + + @Override + public ModelValueInt getGainLNA() { + return parameterGainLNA; + } + + @Override + public ModelValueInt getGainVGA() { + return parameterGainVGA; + } + + @Override + public ModelValueInt getPeakFallRate() { + return parameterPeakFallRateSecs; + } + + @Override + public ModelValueInt getSamples() { + return parameterSamples; + } + + @Override + public ModelValue getSpectrumLineThickness() { + return parameterSpectrumLineThickness; + } + + @Override + public ModelValueInt getPersistentDisplayDecayRate() { + return parameterPersistentDisplayPersTime; + } + + @Override + public ModelValueInt getSpectrumPaletteSize() { + return parameterSpectrumPaletteSize; + } + + @Override + public ModelValueInt getSpectrumPaletteStart() { + return parameterSpectrumPaletteStart; + } + + @Override + public ModelValueBoolean isCapturingPaused() { + return parameterIsCapturingPaused; + } + + @Override + public ModelValueBoolean isChartsPeaksVisible() { + return parameterShowPeaks; + } + + @Override + public ModelValueBoolean isDebugDisplay() { + return parameterDebugDisplay; + } + + @Override + public ModelValueBoolean isFilterSpectrum() { + return parameterFilterSpectrum; + } + + @Override + public ModelValueBoolean isPersistentDisplayVisible() { + return parameterPersistentDisplay; + } + + @Override + public ModelValueBoolean isSpurRemoval() { + return this.parameterSpurRemoval; + } + + @Override + public ModelValueBoolean isWaterfallVisible() { + return parameterWaterfallVisible; + } + + @Override + public void newSpectrumData(boolean fullSweepDone, double[] frequencyStart, float fftBinWidthHz, + float[] signalPowerdBm) { + // System.out.println(frequencyStart+" "+fftBinWidthHz+" "+signalPowerdBm); + fireHardwareStateChanged(true); + if (!hwProcessingQueue.offer(new FFTBins(fullSweepDone, frequencyStart, fftBinWidthHz, signalPowerdBm))) { + System.out.println("queue full"); + dropped++; + } + } + + @Override + public void registerListener(HackRFEventListener listener) { + hRFlisteners.add(listener); + } + + @Override + public void removeListener(HackRFEventListener listener) { + hRFlisteners.remove(listener); + } + + private void fireCapturingStateChanged() { + SwingUtilities.invokeLater(() -> { + synchronized (hRFlisteners) { + for (HackRFEventListener hackRFEventListener : hRFlisteners) { + try { + hackRFEventListener.captureStateChanged(!parameterIsCapturingPaused.getValue()); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + }); + } + + private void fireHardwareStateChanged(boolean sendingData) { + if (this.flagIsHWSendingData != sendingData) { + this.flagIsHWSendingData = sendingData; + SwingUtilities.invokeLater(() -> { + synchronized (hRFlisteners) { + for (HackRFEventListener hackRFEventListener : hRFlisteners) { + try { + hackRFEventListener.hardwareStatusChanged(sendingData); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + }); + } + } + + private FrequencyRange getFreq() { + return parameterFrequency.getValue(); + } + + private void printInit(int initNumber) { + // System.out.println("Startup "+(initNumber++)+" in " + (System.currentTimeMillis() - initTime) + "ms"); + } + + private void processingThread() { + long counter = 0; + long frameCounterChart = 0; + + //mainWhile: + //while(true) { - e1.printStackTrace(); + FFTBins bin1 = null; + try { + bin1 = hwProcessingQueue.take(); + } catch (InterruptedException e1) { + return; + } + float binHz = bin1.fftBinWidthHz; + + /** + * prevents from spectrum chart from using too much CPU + */ + int limitChartRefreshFPS = 30; + int limitPersistentRefreshEveryChartFrame = 2; + + // PowerCalibration calibration = new PowerCalibration(-45, -12.5, 40); + + datasetSpectrum = new DatasetSpectrumPeak(binHz, getFreq().getStartMHz(), getFreq().getEndMHz(), + spectrumInitValue, 15, parameterPeakFallRateSecs.getValue() * 1000); + chart.getXYPlot().getDomainAxis().setRange(getFreq().getStartMHz(), getFreq().getEndMHz()); + + XYSeries spectrumPeaksEmpty = new XYSeries("peaks"); + + float maxPeakJitterdB = 6; + float peakThresholdAboveNoise = 4; + int maxPeakBins = 4; + int validIterations = 25; + spurFilter = new SpurFilter(maxPeakJitterdB, peakThresholdAboveNoise, maxPeakBins, validIterations, + datasetSpectrum); + + long lastChartUpdated = System.currentTimeMillis(); + long lastScanStartTime = System.currentTimeMillis(); + double lastFreq = 0; + + while (true) { + try { + counter++; + FFTBins bins = hwProcessingQueue.take(); + if (parameterIsCapturingPaused.getValue()) + continue; + boolean triggerChartRefresh = bins.fullSweepDone; + //continue; + + if (bins.freqStart != null && bins.sigPowdBm != null) { + // PowerCalibration.correctPower(calibration, parameterGaindB, bins); + datasetSpectrum.addNewData(bins); + } + + if ((triggerChartRefresh/* || timeDiff > 1000 */)) { + // System.out.println("ctr "+counter+" dropped "+dropped); + /** + * filter first + */ + if (parameterSpurRemoval.getValue()) { + long start = System.nanoTime(); + spurFilter.filterDataset(); + synchronized (perfWatch) { + perfWatch.spurFilter.addDrawingTime(System.nanoTime()-start); + } + } + /** + * after filtering, calculate peak spectrum + */ + if (parameterShowPeaks.getValue()) { + datasetSpectrum.refreshPeakSpectrum(); + waterfallPlot.setStatusMessage(String.format("Total Spectrum Peak Power %.1fdBm", + datasetSpectrum.calculateSpectrumPeakPower()), 0); + } + + /** + * Update performance counters + */ + if (System.currentTimeMillis() - perfWatch.lastStatisticsRefreshed > 1000) { + synchronized (perfWatch) { +// waterfallPlot.setStatusMessage(perfWatch.generateStatistics(), 1); + perfWatch.waterfallDraw.nanosSum = waterfallPlot.getDrawTimeSumAndReset(); + perfWatch.waterfallDraw.count = waterfallPlot.getDrawingCounterAndReset(); + String stats = perfWatch.generateStatistics(); + SwingUtilities.invokeLater(() -> { + labelMessages.setText(stats); + }); + perfWatch.reset(); + } + } + + boolean flagChartRedraw = false; + /** + * Update chart in the swing thread + */ + if (System.currentTimeMillis() - lastChartUpdated > 1000/limitChartRefreshFPS) { + flagChartRedraw = true; + frameCounterChart++; + lastChartUpdated = System.currentTimeMillis(); + } + + + XYSeries spectrumSeries; + XYSeries spectrumPeaks; + + if (true) { + spectrumSeries = datasetSpectrum.createSpectrumDataset("spectrum"); + + if (parameterShowPeaks.getValue()) { + spectrumPeaks = datasetSpectrum.createPeaksDataset("peaks"); + } else { + spectrumPeaks = spectrumPeaksEmpty; + } + } else { + spectrumSeries = new XYSeries("spectrum", false, true); + spectrumSeries.setNotify(false); + datasetSpectrum.fillToXYSeries(spectrumSeries); + spectrumSeries.setNotify(true); + + spectrumPeaks = + // new XYSeries("peaks"); + new XYSeries("peaks", false, true); + if (parameterShowPeaks.getValue()) { + spectrumPeaks.setNotify(false); + datasetSpectrum.fillPeaksToXYSeries(spectrumPeaks); + spectrumPeaks.setNotify(false); + } + } + + if (parameterPersistentDisplay.getValue()) { + long start = System.nanoTime(); + boolean redraw = false; + if (flagChartRedraw && frameCounterChart % limitPersistentRefreshEveryChartFrame == 0) + redraw = true; + + //persistentDisplay.drawSpectrumFloat + persistentDisplay.drawSpectrum2 + (datasetSpectrum, + (float) chart.getXYPlot().getRangeAxis().getRange().getLowerBound(), + (float) chart.getXYPlot().getRangeAxis().getRange().getUpperBound(), redraw); + synchronized (perfWatch) { + perfWatch.persisentDisplay.addDrawingTime(System.nanoTime()-start); + } + } + + /** + * do not render it in swing thread because it might + * miss data + */ + if (parameterWaterfallVisible.getValue()) { + long start = System.nanoTime(); + waterfallPlot.addNewData(datasetSpectrum); + synchronized (perfWatch) { + perfWatch.waterfallUpdate.addDrawingTime(System.nanoTime()-start); + } + } + + if (flagChartRedraw) { + if (parameterWaterfallVisible.getValue()) { + waterfallPlot.repaint(); + } + SwingUtilities.invokeLater(() -> { + + chart.setNotify(false); + + chartDataset.removeAllSeries(); + chartDataset.addSeries(spectrumPeaks); + chartDataset.addSeries(spectrumSeries); + chart.setNotify(true); + + if (gifCap != null) { + gifCap.captureFrame(); + } + }); + } + + synchronized (perfWatch) { + perfWatch.hwFullSpectrumRefreshes++; + } + + counter = 0; + } + + } catch (InterruptedException e) { + return; + } + } + } + } + + private void recalculateGains(int totalGain) { /** - * Color palette for UI + * use only lna gain when <=40 when >40, add only vga gain */ - Color palette0 = Color.white; - Color palette1 = new Color(0xe5e5e5); - Color palette2 = new Color(0xFCA311); - Color palette3 = new Color(0x14213D); - Color palette4 = Color.BLACK; + int lnaGain = totalGain / 8 * 8; //lna gain has step 8, range <0, 40> + if (lnaGain > 40) + lnaGain = 40; + int vgaGain = lnaGain != 40 ? 0 : ((totalGain - lnaGain) & ~1); //vga gain has step 2, range <0,60> + this.parameterGainLNA.setValue(lnaGain); + this.parameterGainVGA.setValue(vgaGain); + this.parameterGainTotal.setValue(lnaGain + vgaGain); + } + + /** + * uses fifo queue to process launch commands, only the last launch command + * is important, delete others + */ + private synchronized void restartHackrfSweep() { + if (threadLaunchCommands.offer(0) == false) { + threadLaunchCommands.clear(); + threadLaunchCommands.offer(0); + } + } + + /** + * no need to synchronize, executes only in the launcher thread + */ + private void restartHackrfSweepExecute() { + stopHackrfSweep(); + threadHackrfSweep = new Thread(() -> { + Thread.currentThread().setName("hackrf_sweep"); + try { + forceStopSweep = false; + sweep(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + threadHackrfSweep.start(); + } + + private void setupChart() { + int axisWidthLeft = 70; + int axisWidthRight = 20; + + chart = ChartFactory.createXYLineChart("Spectrum analyzer", "Frequency [MHz]", "Power [dB]", chartDataset, + PlotOrientation.VERTICAL, false, false, false); + chart.getRenderingHints().put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); XYPlot plot = chart.getXYPlot(); NumberAxis domainAxis = ((NumberAxis) plot.getDomainAxis()); NumberAxis rangeAxis = ((NumberAxis) plot.getRangeAxis()); - XYLineAndShapeRenderer rend = new XYLineAndShapeRenderer(); - rend.setBaseShapesVisible(false); + chartLineRenderer = new XYLineAndShapeRenderer(); + chartLineRenderer.setBaseShapesVisible(false); + chartLineRenderer.setBaseStroke(new BasicStroke(parameterSpectrumLineThickness.getValue().floatValue())); + + rangeAxis.setAutoRange(false); + rangeAxis.setRange(-110, 20); + rangeAxis.setTickUnit(new NumberTickUnit(10, new DecimalFormat("###"))); + + domainAxis.setNumberFormatOverride(new DecimalFormat(" #.### ")); + + chartLineRenderer.setAutoPopulateSeriesStroke(false); + chartLineRenderer.setAutoPopulateSeriesPaint(false); + chartLineRenderer.setSeriesPaint(0, colors.palette2); + + if (false) + chart.addProgressListener(new ChartProgressListener() { + StandardTickUnitSource tus = new StandardTickUnitSource(); + + @Override + public void chartProgress(ChartProgressEvent event) { + if (event.getType() == ChartProgressEvent.DRAWING_STARTED) { + Range r = domainAxis.getRange(); + domainAxis.setTickUnit((NumberTickUnit) tus.getCeilingTickUnit(r.getLength() / 20)); + domainAxis.setMinorTickCount(2); + domainAxis.setMinorTickMarksVisible(true); + + } + } + }); + + plot.setDomainGridlinesVisible(false); + plot.setRenderer(chartLineRenderer); + + /** + * sets empty space around the plot + */ + AxisSpace axisSpace = new AxisSpace(); + axisSpace.setLeft(axisWidthLeft); + axisSpace.setRight(axisWidthRight); + axisSpace.setTop(0); + axisSpace.setBottom(50); + plot.setFixedDomainAxisSpace(axisSpace);//sets width of the domain axis left/right + plot.setFixedRangeAxisSpace(axisSpace);//sets heigth of range axis top/bottom + + rangeAxis.setAxisLineVisible(false); + rangeAxis.setTickMarksVisible(false); + + plot.setAxisOffset(RectangleInsets.ZERO_INSETS); //no space between range axis and plot + + Font labelFont = new Font(Font.MONOSPACED, Font.BOLD, 16); + rangeAxis.setLabelFont(labelFont); + rangeAxis.setTickLabelFont(labelFont); + rangeAxis.setLabelPaint(colors.palette1); + rangeAxis.setTickLabelPaint(colors.palette1); + domainAxis.setLabelFont(labelFont); + domainAxis.setTickLabelFont(labelFont); + domainAxis.setLabelPaint(colors.palette1); + domainAxis.setTickLabelPaint(colors.palette1); + chartLineRenderer.setBasePaint(Color.white); + plot.setBackgroundPaint(colors.palette4); + chart.setBackgroundPaint(colors.palette4); + chartLineRenderer.setSeriesPaint(1, colors.palette1); chartPanel = new ChartPanel(chart); chartPanel.setMaximumDrawWidth(4096); @@ -142,26 +875,29 @@ public HackRFSweepSpectrumAnalyzer() chartPanel.setPopupMenu(null); chartPanel.setMinimumSize(new Dimension(200, 200)); + printInit(1); + /** - * Draws overlay of waterfall's color scale next to main spectrum chart to show + * Draws overlay of waterfall's color scale next to main spectrum chart + * to show */ - chartPanel.addOverlay(new Overlay() - { - @Override public void addChangeListener(OverlayChangeListener listener) - { + chartPanel.addOverlay(new Overlay() { + @Override + public void addChangeListener(OverlayChangeListener listener) { } - @Override public void paintOverlay(Graphics2D g, ChartPanel chartPanel) - { - + @Override + public void paintOverlay(Graphics2D g, ChartPanel chartPanel) { Rectangle2D area = chartPanel.getChartRenderingInfo().getPlotInfo().getDataArea(); int plotStartX = (int) area.getX(); int plotWidth = (int) area.getWidth(); Rectangle2D subplotArea = chartPanel.getChartRenderingInfo().getPlotInfo().getDataArea(); - int y1 = (int) plot.getRangeAxis().valueToJava2D(waterfallPlot.getSpectrumPaletteStart(), subplotArea, plot.getRangeAxisEdge()); - int y2 = (int) plot.getRangeAxis().valueToJava2D(waterfallPlot.getSpectrumPaletteStart() + waterfallPlot.getSpectrumPaletteSize(), subplotArea, + int y1 = (int) plot.getRangeAxis().valueToJava2D(waterfallPlot.getSpectrumPaletteStart(), subplotArea, + plot.getRangeAxisEdge()); + int y2 = (int) plot.getRangeAxis().valueToJava2D( + waterfallPlot.getSpectrumPaletteStart() + waterfallPlot.getSpectrumPaletteSize(), subplotArea, plot.getRangeAxisEdge()); int x = plotStartX + plotWidth; @@ -170,597 +906,286 @@ public HackRFSweepSpectrumAnalyzer() waterfallPlot.drawScale(g, x, y2, w, h); } - @Override public void removeChangeListener(OverlayChangeListener listener) - { + @Override + public void removeChangeListener(OverlayChangeListener listener) { } }); - //Align the waterfall plot and the spectrum chart - chart.addChangeListener(new ChartChangeListener() - { - @Override public void chartChanged(ChartChangeEvent event) - { - Rectangle2D area = chartPanel.getChartRenderingInfo().getPlotInfo().getDataArea(); - int plotStartX = (int) area.getX(); - int plotWidth = (int) area.getWidth(); - waterfallPlot.setDrawingOffsets(plotStartX, plotWidth); - // System.out.println("l off "+offLeft+" width "+chartWidth ); + /** + * Draw frequency bands as an overlay + */ + if (true) + chartPanel.addOverlay(new Overlay() { + @Override + public void addChangeListener(OverlayChangeListener listener) { } - }); - - addChartMouseMarkers(); - - waterfallPlot = new WaterfallPlot(chartPanel, 300); - waterfallPaletteStartMarker = new ValueMarker(waterfallPlot.getSpectrumPaletteStart(), palette2, new BasicStroke(1f)); - waterfallPaletteEndMarker = new ValueMarker(waterfallPlot.getSpectrumPaletteStart() + waterfallPlot.getSpectrumPaletteSize(), palette2, - new BasicStroke(1f)); - // chart.getXYPlot().addRangeMarker(waterfallPaletteStartMarker); - // chart.getXYPlot().addRangeMarker(waterfallPaletteEndMarker); - - HackRFSweepSettingsUI settingsPanel = new HackRFSweepSettingsUI(this); - - f = new JFrame(); - f.setExtendedState(f.getExtendedState() | JFrame.MAXIMIZED_BOTH); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - f.setLayout(new BorderLayout()); - f.setTitle("Spectrum Analyzer - hackrf_sweep"); - JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, chartPanel, waterfallPlot); - splitPane.setResizeWeight(0.8); - splitPane.setBorder(null); - f.add(splitPane, BorderLayout.CENTER); - f.setMinimumSize(new Dimension(600, 600)); - f.add(settingsPanel, BorderLayout.EAST); - try - { - f.setIconImage(new ImageIcon("program.png").getImage()); - } - catch (Exception e) - { -// e.printStackTrace(); - } - f.pack(); - f.setVisible(true); - - rangeAxis.setAutoRange(false); - rangeAxis.setRange(-110, 0); - rangeAxis.setTickUnit(new NumberTickUnit(10, new DecimalFormat("###"))); - - domainAxis.setNumberFormatOverride(new DecimalFormat(" #.### ")); - - rend.setAutoPopulateSeriesStroke(false); - rend.setAutoPopulateSeriesPaint(false); - rend.setSeriesPaint(0, palette2); - - if (false) - chart.addProgressListener(new ChartProgressListener() - { - StandardTickUnitSource tus = new StandardTickUnitSource(); - - @Override public void chartProgress(ChartProgressEvent event) - { - if (event.getType() == ChartProgressEvent.DRAWING_STARTED) - { - Range r = domainAxis.getRange(); - domainAxis.setTickUnit((NumberTickUnit) tus.getCeilingTickUnit(r.getLength() / 20)); - domainAxis.setMinorTickCount(2); - domainAxis.setMinorTickMarksVisible(true); - - } + @Override + public void paintOverlay(Graphics2D g2, ChartPanel chartPanel) { + BufferedImage img = imageFrequencyAllocationTableBands; + if (img != null) { + Rectangle2D area = chartPanel.getChartRenderingInfo().getPlotInfo().getDataArea(); + g2.drawImage(img, (int) area.getX(), (int) area.getY(), null); } - }); + } - plot.setDomainGridlinesVisible(false); - plot.setRenderer(rend); + @Override + public void removeChangeListener(OverlayChangeListener listener) { + } + }); /** - * sets empty space around the plot + * monitors chart data area for change due to no other way to extract + * that info from jfreechart when it changes */ - AxisSpace axisSpace = new AxisSpace(); - axisSpace.setLeft(axisWidthLeft); - axisSpace.setRight(axisWidthRight); - axisSpace.setTop(20); - axisSpace.setBottom(50); - plot.setFixedDomainAxisSpace(axisSpace);//sets width of the domain axis left/right - plot.setFixedRangeAxisSpace(axisSpace);//sets heigth of range axis top/bottom - - rangeAxis.setAxisLineVisible(false); - rangeAxis.setTickMarksVisible(false); - - plot.setAxisOffset(RectangleInsets.ZERO_INSETS); //no space between range axis and plot + chart.addChangeListener(event -> { + Rectangle2D aN = chartPanel.getChartRenderingInfo().getPlotInfo().getDataArea(); + Rectangle2D aO = chartDataArea.getValue(); + if (aO.getX() != aN.getX() || aO.getY() != aN.getY() || aO.getWidth() != aN.getWidth() + || aO.getHeight() != aN.getHeight()) { + chartDataArea.setValue(new Rectangle2D.Double(aN.getX(), aN.getY(), aN.getWidth(), aN.getHeight())); + } + }); - Font labelFont = new Font(Font.MONOSPACED, Font.BOLD, 16); - rangeAxis.setLabelFont(labelFont); - rangeAxis.setTickLabelFont(labelFont); - rangeAxis.setLabelPaint(palette1); - rangeAxis.setTickLabelPaint(palette1); - domainAxis.setLabelFont(labelFont); - domainAxis.setTickLabelFont(labelFont); - domainAxis.setLabelPaint(palette1); - domainAxis.setTickLabelPaint(palette1); - rend.setBasePaint(Color.white); - plot.setBackgroundPaint(palette4); - chart.setBackgroundPaint(palette4); - rend.setSeriesPaint(1, palette1); + chart.addProgressListener(new ChartProgressListener() { + private long chartRedrawStarted; - startLauncherThread(); - restartHackrfSweep(); - - //shutdown on exit - Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() - { - @Override public void run() - { - stopHackrfSweep(); + @Override + public void chartProgress(ChartProgressEvent arg0) { + if (arg0.getType() == ChartProgressEvent.DRAWING_STARTED) { + chartRedrawStarted = System.nanoTime(); + } else if (arg0.getType() == ChartProgressEvent.DRAWING_FINISHED) { + synchronized (perfWatch) { + perfWatch.chartDrawing.addDrawingTime(System.nanoTime() - chartRedrawStarted); + } + } } - })); + }); + + } - private void addChartMouseMarkers() { - ValueMarker freqMarker = new ValueMarker(0, Color.WHITE, new BasicStroke(1f)); + /** + * Displays a cross marker with current frequency and signal strength when + * mouse hovers over the frequency chart + */ + private void setupChartMouseMarkers() { + ValueMarker freqMarker = new ValueMarker(0, Color.WHITE, new BasicStroke(1f)); freqMarker.setLabelPaint(Color.white); freqMarker.setLabelAnchor(RectangleAnchor.TOP_RIGHT); freqMarker.setLabelTextAnchor(TextAnchor.TOP_LEFT); freqMarker.setLabelFont(new Font(Font.MONOSPACED, Font.BOLD, 16)); - ValueMarker signalMarker = new ValueMarker(0, Color.WHITE, new BasicStroke(1f)); + ValueMarker signalMarker = new ValueMarker(0, Color.WHITE, new BasicStroke(1f)); signalMarker.setLabelPaint(Color.white); signalMarker.setLabelAnchor(RectangleAnchor.TOP_RIGHT); signalMarker.setLabelTextAnchor(TextAnchor.BOTTOM_RIGHT); signalMarker.setLabelFont(new Font(Font.MONOSPACED, Font.BOLD, 16)); chartPanel.addMouseMotionListener(new MouseMotionAdapter() { + DecimalFormat format = new DecimalFormat("0.#"); + @Override public void mouseMoved(MouseEvent e) { - int x = e.getX(); - int y = e.getY(); + int x = e.getX(); + int y = e.getY(); - XYPlot plot = chart.getXYPlot(); + XYPlot plot = chart.getXYPlot(); Rectangle2D subplotArea = chartPanel.getChartRenderingInfo().getPlotInfo().getDataArea(); - double crosshairRange = plot.getRangeAxis().java2DToValue(y, subplotArea, plot.getRangeAxisEdge()); + double crosshairRange = plot.getRangeAxis().java2DToValue(y, subplotArea, plot.getRangeAxisEdge()); signalMarker.setValue(crosshairRange); signalMarker.setLabel(String.format("%.1fdB", crosshairRange)); - double crosshairDomain = plot.getDomainAxis().java2DToValue(x, subplotArea, plot.getDomainAxisEdge()); + double crosshairDomain = plot.getDomainAxis().java2DToValue(x, subplotArea, plot.getDomainAxisEdge()); freqMarker.setValue(crosshairDomain); freqMarker.setLabel(String.format("%.1fMHz", crosshairDomain)); + + FrequencyAllocationTable activeTable = parameterFrequencyAllocationTable.getValue(); + if (activeTable != null) { + FrequencyBand band = activeTable.lookupBand((long) (crosshairDomain * 1000000l)); + if (band == null) + titleFreqBand.setText(" "); + else { + titleFreqBand.setText(String.format("%s - %s MHz %s", format.format(band.getMHzStartIncl()), + format.format(band.getMHzEndExcl()), band.getApplications().replaceAll("/", " / "))); + } + } } }); chartPanel.addMouseListener(new MouseAdapter() { @Override - public void mouseExited(MouseEvent e) { + public void mouseEntered(MouseEvent e) { chart.getXYPlot().clearDomainMarkers(); chart.getXYPlot().clearRangeMarkers(); + chart.getXYPlot().addRangeMarker(signalMarker); + chart.getXYPlot().addDomainMarker(freqMarker); } + @Override - public void mouseEntered(MouseEvent e) { + public void mouseExited(MouseEvent e) { chart.getXYPlot().clearDomainMarkers(); chart.getXYPlot().clearRangeMarkers(); - chart.getXYPlot().addRangeMarker(signalMarker); - chart.getXYPlot().addDomainMarker(freqMarker); + titleFreqBand.setText(" "); } }); - } - public boolean isHWSendingData = false; - private void fireHardwareStateChanged(boolean sendingData) - { - if (this.isHWSendingData != sendingData){ - this.isHWSendingData = sendingData; - SwingUtilities.invokeLater(new Runnable() - { - @Override public void run() - { - synchronized (listeners) - { - for (HackRFEventListener hackRFEventListener : listeners) - { - try - { - hackRFEventListener.hardwareStatusChanged(sendingData); - } - catch (Exception e) - { - e.printStackTrace(); - } - } - } - } - }); - } + titleFreqBand.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 14)); + titleFreqBand.setPosition(RectangleEdge.BOTTOM); + titleFreqBand.setHorizontalAlignment(HorizontalAlignment.LEFT); + titleFreqBand.setMargin(0.0, 2.0, 0.0, 2.0); + titleFreqBand.setPaint(Color.white); + chart.addSubtitle(titleFreqBand); } - private void fireCapturingStateChanged(boolean isCapturing) - { - if (!this.isCapturing != isCapturing){ - this.isCapturing = isCapturing; - SwingUtilities.invokeLater(new Runnable() - { - @Override public void run() - { - synchronized (listeners) - { - for (HackRFEventListener hackRFEventListener : listeners) - { - try - { - hackRFEventListener.captureStateChanged(isCapturing); - } - catch (Exception e) - { - e.printStackTrace(); - } - } - } + private void setupFrequencyAllocationTable() { + SwingUtilities.invokeLater(() -> { + chartPanel.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + redrawFrequencySpectrumTable(); } }); - } - } - - @Override - public boolean getAntennaPowerEnable() { - return parameterAntPower; - } - - @Override - public void setAntennaPowerEnable(boolean enable) { - this.parameterAntPower = enable; - restartHackrfSweep(); - } - - @Override public int getFFTBinHz() - { - return parameterFFTBinHz; - } - - @Override public int getFrequencyEnd() - { - return parameterMaxFreqMHz; - } - - @Override public int getFrequencyStart() - { - return parameterMinFreqMHz; - } - - @Override public int getGain() - { - return parameterGaindB; - } - - @Override public int getLNAGain() - { - return lnaGain; - } - - @Override public int getSamples() - { - return parameterSamples; - } - - @Override public int getSpectrumPaletteSize() - { - return (int) waterfallPlot.getSpectrumPaletteSize(); - } - - @Override public int getSpectrumPaletteStart() - { - return (int) waterfallPlot.getSpectrumPaletteStart(); - } - - @Override public int getVGAGain() - { - return vgaGain; - } - - @Override public boolean isChartsPeaksVisible() - { - return showPeaks; - } - - @Override public boolean isFilterSpectrum() - { - return filterSpectrum; - } - - @Override public void newSpectrumData(boolean fullSweepDone, double[] frequencyStart, float fftBinWidthHz, float[] signalPowerdBm) - { - // System.out.println(frequencyStart+" "+fftBinWidthHz+" "+signalPowerdBm); - fireHardwareStateChanged(true); - if (!processingQueue.offer(new FFTBins(fullSweepDone, frequencyStart, fftBinWidthHz, signalPowerdBm))) - { - System.out.println("queue full"); - dropped++; - } - } - - @Override public void setChartPeaksVisibility(boolean visible) - { - this.showPeaks = visible; - DatasetSpectrumPeak p = datasetSpectrum; - if (p != null) - { - p.resetPeaks(); - } - } - - @Override public void setFFTBin(int fftBinHz) - { - this.parameterFFTBinHz = fftBinHz; - restartHackrfSweep(); - } - - @Override public void setFilterSpectrum(boolean filter) - { - filterSpectrum = filter; - } - - @Override public void setFrequency(int freqStartMHz, int freqEndMHz) - { - parameterMinFreqMHz = freqStartMHz; - parameterMaxFreqMHz = freqEndMHz; - restartHackrfSweep(); - } + chart.getXYPlot().getDomainAxis().addChangeListener((e) -> { + redrawFrequencySpectrumTable(); + }); + chart.getXYPlot().getRangeAxis().addChangeListener(event -> { + redrawFrequencySpectrumTable(); + System.out.println(event); + }); - @Override public void setGain(int gaindB) - { - if (gaindB == this.parameterGaindB) - return; - recalculateGains(gaindB); - restartHackrfSweep(); + }); + parameterFrequencyAllocationTable.addListener(this::redrawFrequencySpectrumTable); } - @Override public void setSamples(int samples) - { - this.parameterSamples = samples; - restartHackrfSweep(); - } + private void setupParameterObservers() { + Runnable restartHackrf = this::restartHackrfSweep; + parameterFrequency.addListener(restartHackrf); + parameterAntPower.addListener(restartHackrf); + parameterFFTBinHz.addListener(restartHackrf); + parameterSamples.addListener(restartHackrf); + parameterIsCapturingPaused.addListener(this::fireCapturingStateChanged); - @Override public void setSpectrumPaletteSize(int dB) - { - if (dB < SPECTRUM_PALETTE_SIZE_MIN) - return; - waterfallPlot.setSpectrumPaletteSize(dB); - SwingUtilities.invokeLater(new Runnable() - { - @Override public void run() - { - waterfallPaletteStartMarker.setValue(waterfallPlot.getSpectrumPaletteStart()); - waterfallPaletteEndMarker.setValue(waterfallPlot.getSpectrumPaletteStart() + waterfallPlot.getSpectrumPaletteSize()); - } + parameterGainTotal.addListener((gainTotal) -> { + if (flagManualGain) //flag is being adjusted manually by LNA or VGA, do not recalculate the gains + return; + recalculateGains(gainTotal); + restartHackrfSweep(); }); - } - - @Override public void setSpectrumPaletteStart(int dB) - { - waterfallPlot.setSpectrumPaletteStart(dB); - SwingUtilities.invokeLater(new Runnable() - { - @Override public void run() - { - waterfallPaletteStartMarker.setValue(waterfallPlot.getSpectrumPaletteStart()); - waterfallPaletteEndMarker.setValue(waterfallPlot.getSpectrumPaletteStart() + waterfallPlot.getSpectrumPaletteSize()); + Runnable gainRecalc = () -> { + int totalGain = parameterGainLNA.getValue() + parameterGainVGA.getValue(); + flagManualGain = true; + try { + parameterGainTotal.setValue(totalGain); + } catch (Exception e) { + e.printStackTrace(); + } finally { + flagManualGain = false; + } + restartHackrfSweep(); + }; + parameterGainLNA.addListener(gainRecalc); + parameterGainVGA.addListener(gainRecalc); + + parameterSpurRemoval.addListener(() -> { + SpurFilter filter = spurFilter; + if (filter != null) { + filter.recalibrate(); } }); - } - - @Override public void setSpurRemoval(boolean enable) - { - this.spurRemoval = enable; - SpurFilter filter = spurFilter; - if (filter != null){ - filter.recalibrate(); - } - } - - @Override public boolean isSpurRemoval() - { - return this.spurRemoval; - } - - @Override public void registerListener(HackRFEventListener listener) - { - listeners.add(listener); - } - - @Override public void removeListener(HackRFEventListener listener) - { - listeners.remove(listener); - } - - @Override public void setCapturing(boolean capturing) - { - isCapturing = capturing; - fireCapturingStateChanged(isCapturing); - } - - @Override public boolean isCapturing() - { - return isCapturing; - } - - private void processingThread() - { - int counter = 0; - - //mainWhile: - //while(true) - { - FFTBins bin1 = null; - try - { - bin1 = processingQueue.take(); + parameterShowPeaks.addListener(() -> { + DatasetSpectrumPeak p = datasetSpectrum; + if (p != null) { + p.resetPeaks(); } - catch (InterruptedException e1) - { + }); + parameterSpectrumPaletteStart.setValue((int) waterfallPlot.getSpectrumPaletteStart()); + parameterSpectrumPaletteSize.setValue((int) waterfallPlot.getSpectrumPaletteSize()); + parameterSpectrumPaletteStart.addListener((dB) -> { + waterfallPlot.setSpectrumPaletteStart(dB); + SwingUtilities.invokeLater(() -> { + waterfallPaletteStartMarker.setValue(waterfallPlot.getSpectrumPaletteStart()); + waterfallPaletteEndMarker + .setValue(waterfallPlot.getSpectrumPaletteStart() + waterfallPlot.getSpectrumPaletteSize()); + }); + }); + parameterSpectrumPaletteSize.addListener((dB) -> { + if (dB < SPECTRUM_PALETTE_SIZE_MIN) return; - } - float binHz = bin1.fftBinWidthHz; - -// PowerCalibration calibration = new PowerCalibration(-45, -12.5, 40); - - datasetSpectrum = new DatasetSpectrumPeak(binHz, parameterMinFreqMHz, parameterMaxFreqMHz, spectrumInitValue, 15, 30000); - chart.getXYPlot().getDomainAxis().setRange(parameterMinFreqMHz, parameterMaxFreqMHz); - - float maxPeakJitterdB = 6; - float peakThresholdAboveNoise = 4; - int maxPeakBins = 4; - int validIterations = 25; - spurFilter = new SpurFilter(maxPeakJitterdB, peakThresholdAboveNoise, maxPeakBins, validIterations, datasetSpectrum); - - - long lastChartUpdated = System.currentTimeMillis(); - long lastScanStartTime = System.currentTimeMillis(); - double lastFreq = 0; - - while (true) - { - try - { - counter++; - FFTBins bins = processingQueue.take(); - if (!isCapturing) - continue; - boolean triggerChartRefresh = bins.fullSweepDone; - //continue; - - if (bins.freqStart != null && bins.sigPowdBm != null) - { -// PowerCalibration.correctPower(calibration, parameterGaindB, bins); - datasetSpectrum.addNewData(bins); - } + waterfallPlot.setSpectrumPaletteSize(dB); + SwingUtilities.invokeLater(() -> { + waterfallPaletteStartMarker.setValue(waterfallPlot.getSpectrumPaletteStart()); + waterfallPaletteEndMarker + .setValue(waterfallPlot.getSpectrumPaletteStart() + waterfallPlot.getSpectrumPaletteSize()); + }); - if ((triggerChartRefresh/* || timeDiff > 1000*/)) - { - // System.out.println("ctr "+counter+" dropped "+dropped); - - /** - * filter first - */ - if (spurRemoval){ - spurFilter.filterDataset(); - } - /** - * after filtering, calculate peak spectrum - */ - if (showPeaks){ - datasetSpectrum.refreshPeakSpectrum(); - waterfallPlot.setStatusMessage(String.format("Total Spectrum Peak Power %.1fdBm", datasetSpectrum.calculateSpectrumPeakPower())); - } - - XYSeries spectrumSeries = new XYSeries("spectrum"); - spectrumSeries.setNotify(false); - datasetSpectrum.fillToXYSeries(spectrumSeries); - spectrumSeries.setNotify(true); + }); + parameterPeakFallRateSecs.addListener((fallRate) -> { + datasetSpectrum.setPeakFalloutMillis(fallRate * 1000l); + }); - XYSeries spectrumPeaks; - if (showPeaks) - { - spectrumPeaks = new XYSeries("peaks"); - spectrumPeaks.setNotify(false); - datasetSpectrum.fillPeaksToXYSeries(spectrumPeaks); - spectrumPeaks.setNotify(false); - } - else - spectrumPeaks = new XYSeries("peaks"); + parameterSpectrumLineThickness.addListener((thickness) -> { + SwingUtilities.invokeLater(() -> chartLineRenderer.setBaseStroke(new BasicStroke(thickness.floatValue()))); + }); + + parameterPersistentDisplayPersTime.addListener((time) -> { + persistentDisplay.setPersistenceTime(time); + }); - SwingUtilities.invokeLater(new Runnable() - { - @Override public void run() - { - chart.setNotify(false); - dataset.removeAllSeries(); - dataset.addSeries(spectrumPeaks); - dataset.addSeries(spectrumSeries); - chart.setNotify(true); - } - }); - waterfallPlot.addNewData(datasetSpectrum); + int persistentDisplayDownscaleFactor = 4; + + Runnable resetPersistentImage = () -> { + boolean display = parameterPersistentDisplay.getValue(); + persistentDisplay.reset(); + chart.getXYPlot().setBackgroundImage(display ? persistentDisplay.getDisplayImage().getValue() : null); + chart.getXYPlot().setBackgroundImageAlpha(alphaPersistentDisplayImage); + }; + persistentDisplay.getDisplayImage().addListener((image) -> { + if (parameterPersistentDisplay.getValue()) + chart.getXYPlot().setBackgroundImage(image); + }); - counter = 0; - lastChartUpdated = System.currentTimeMillis(); + registerListener(new HackRFEventAdapter() { + @Override + public void hardwareStatusChanged(boolean hardwareSendingData) { + SwingUtilities.invokeLater(() -> { + if (hardwareSendingData && parameterPersistentDisplay.getValue()) { + resetPersistentImage.run(); } - - } - catch (InterruptedException e) - { - return; - } + }); } + }); - } - - } - - private void recalculateGains(int totalGain) - { - /** - * use only lna gain when <=40 - * when >40, add only vga gain - */ - lnaGain = totalGain / 8 * 8; //lna gain step 8, max 40 - if (lnaGain > 40) - lnaGain = 40; - vgaGain = lnaGain != 40 ? 0 : ((totalGain - lnaGain) & ~1); //vga gain step 2, max 60 - this.parameterGaindB = lnaGain + vgaGain; - } + parameterPersistentDisplay.addListener((display) -> { + SwingUtilities.invokeLater(resetPersistentImage::run); + }); - /** - * uses fifo queue to process launch commands, only the last launch command is important, delete others - */ - private synchronized void restartHackrfSweep() - { - if (threadLaunchCommands.offer(0) == false) - { - threadLaunchCommands.clear(); - threadLaunchCommands.offer(0); - } - } - - private volatile boolean forceStopSweep = false; - private SpurFilter spurFilter; - private ChartPanel chartPanel; - /** - * no need to synchronize, executes only in launcher thread - */ - private void restartHackrfSweepExecute() - { - stopHackrfSweep(); - threadHackrfSweep = new Thread(new Runnable() - { - @Override public void run() - { - Thread.currentThread().setName("hackrf_sweep"); - try - { - forceStopSweep = false; - sweep(); + chartDataArea.addListener((area) -> { + SwingUtilities.invokeLater(() -> { + /* + * Align the waterfall plot and the spectrum chart + */ + if (waterfallPlot != null) + waterfallPlot.setDrawingOffsets((int) area.getX(), (int) area.getWidth()); + + /** + * persistent display config + */ + persistentDisplay.setImageSize((int) area.getWidth() / persistentDisplayDownscaleFactor, + (int) area.getWidth() / persistentDisplayDownscaleFactor); + if (parameterPersistentDisplay.getValue()) { + chart.getXYPlot().setBackgroundImage(persistentDisplay.getDisplayImage().getValue()); + chart.getXYPlot().setBackgroundImageAlpha(alphaPersistentDisplayImage); } - catch (IOException e) - { - e.printStackTrace(); - } - } + }); }); - threadHackrfSweep.start(); } - private void startLauncherThread() - { - threadLauncher = new Thread(new Runnable() - { - @Override public void run() - { - Thread.currentThread().setName("Launcher-thread"); - while (true) - { - try - { - threadLaunchCommands.take(); - restartHackrfSweepExecute(); - } - catch (Exception e) - { - e.printStackTrace(); - } + private void startLauncherThread() { + threadLauncher = new Thread(() -> { + Thread.currentThread().setName("Launcher-thread"); + while (true) { + try { + threadLaunchCommands.take(); + restartHackrfSweepExecute(); + } catch (Exception e) { + e.printStackTrace(); } } }); @@ -770,44 +1195,31 @@ private void startLauncherThread() /** * no need to synchronize, executes only in launcher thread */ - private void stopHackrfSweep() - { - forceStopSweep = true; - if (threadHackrfSweep != null) - { - while (threadHackrfSweep.isAlive()) - { - forceStopSweep = true; -// System.out.println("Calling HackRFSweepNativeBridge.stop()"); + private void stopHackrfSweep() { + forceStopSweep = true; + if (threadHackrfSweep != null) { + while (threadHackrfSweep.isAlive()) { + forceStopSweep = true; + // System.out.println("Calling HackRFSweepNativeBridge.stop()"); HackRFSweepNativeBridge.stop(); - try - { + try { Thread.sleep(20); - } - catch (InterruptedException e) - { + } catch (InterruptedException e) { } } - try - { + try { threadHackrfSweep.join(); - } - catch (InterruptedException e) - { + } catch (InterruptedException e) { e.printStackTrace(); } threadHackrfSweep = null; } System.out.println("HackRFSweep thread stopped."); - if (threadProcessing != null) - { + if (threadProcessing != null) { threadProcessing.interrupt(); - try - { + try { threadProcessing.join(); - } - catch (InterruptedException e) - { + } catch (InterruptedException e) { e.printStackTrace(); } threadProcessing = null; @@ -815,44 +1227,54 @@ private void stopHackrfSweep() } } - private void sweep() throws IOException - { + private void sweep() throws IOException { lock.lock(); - try - { - threadProcessing = new Thread(new Runnable() - { - @Override public void run() - { - Thread.currentThread().setName("hackrf_sweep data processing thread"); - processingThread(); - } + try { + threadProcessing = new Thread(() -> { + Thread.currentThread().setName("hackrf_sweep data processing thread"); + processingThread(); }); threadProcessing.start(); /** * Ensures auto-restart if HW disconnects */ - while(forceStopSweep == false){ - System.out.println("Starting hackrf_sweep... " + parameterMinFreqMHz + "-" + parameterMaxFreqMHz + "MHz "); - System.out.println("hackrf_sweep params: freq " + parameterMinFreqMHz + "-" + parameterMaxFreqMHz + "MHz samples " + parameterSamples + " lna: " - + lnaGain + " vga: " + vgaGain); + while (forceStopSweep == false) { + System.out.println( + "Starting hackrf_sweep... " + getFreq().getStartMHz() + "-" + getFreq().getEndMHz() + "MHz "); + System.out.println("hackrf_sweep params: freq " + getFreq().getStartMHz() + "-" + getFreq().getEndMHz() + + "MHz FFTBin " + parameterFFTBinHz.getValue() + "Hz samples " + parameterSamples.getValue() + + " lna: " + parameterGainLNA.getValue() + " vga: " + parameterGainVGA.getValue()); fireHardwareStateChanged(false); - HackRFSweepNativeBridge.start(this, parameterMinFreqMHz, parameterMaxFreqMHz, parameterFFTBinHz, parameterSamples, lnaGain, vgaGain, parameterAntPower); + HackRFSweepNativeBridge.start(this, getFreq().getStartMHz(), getFreq().getEndMHz(), + parameterFFTBinHz.getValue(), parameterSamples.getValue(), parameterGainLNA.getValue(), + parameterGainVGA.getValue(), parameterAntPower.getValue()); fireHardwareStateChanged(false); - if (forceStopSweep==false){ + if (forceStopSweep == false) { Thread.sleep(1000); } } - } - catch (InterruptedException e) - { + } catch (InterruptedException e) { e.printStackTrace(); - } - finally - { + } finally { lock.unlock(); fireHardwareStateChanged(false); } } + + protected void redrawFrequencySpectrumTable() { + Rectangle2D area = chartPanel.getChartRenderingInfo().getPlotInfo().getDataArea(); + FrequencyAllocationTable activeTable = parameterFrequencyAllocationTable.getValue(); + if (activeTable == null) { + imageFrequencyAllocationTableBands = null; + } else if (area.getWidth() > 0 && area.getHeight() > 0) { + imageFrequencyAllocationTableBands = activeTable.drawAllocationTable((int) area.getWidth(), + (int) area.getHeight(), alphaFreqAllocationTableBandsImage, getFreq().getStartMHz() * 1000000l, + getFreq().getEndMHz() * 1000000l, + //colors.palette4, + Color.white, + //colors.palette1 + Color.DARK_GRAY); + } + } } diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/Version.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/Version.java index 261da9c..2f21515 100644 --- a/src/hackrf-sweep/src-java/jspectrumanalyzer/Version.java +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/Version.java @@ -2,6 +2,6 @@ public class Version { - public static final String version = "1.3"; + public static final String version = "1.4"; public static final String url = "https://github.com/pavsa/hackrf-spectrum-analyzer"; } diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/capture/GifSequenceWriter.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/capture/GifSequenceWriter.java new file mode 100644 index 0000000..e03e3fd --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/capture/GifSequenceWriter.java @@ -0,0 +1,169 @@ +package jspectrumanalyzer.capture; + +// + +//GifSequenceWriter.java +// +//Created by Elliot Kroo on 2009-04-25. +// +//This work is licensed under the Creative Commons Attribution 3.0 Unported +//License. To view a copy of this license, visit +//http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative +//Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. + +import javax.imageio.*; +import javax.imageio.metadata.*; +import javax.imageio.stream.*; +import java.awt.image.*; +import java.io.*; +import java.util.Iterator; + +public class GifSequenceWriter { + protected ImageWriter gifWriter; + protected ImageWriteParam imageWriteParam; + protected IIOMetadata imageMetaData; + + /** + * Creates a new GifSequenceWriter + * + * @param outputStream + * the ImageOutputStream to be written to + * @param imageType + * one of the imageTypes specified in BufferedImage + * @param timeBetweenFramesMS + * the time between frames in miliseconds + * @param loopContinuously + * wether the gif should loop repeatedly + * @throws IIOException + * if no gif ImageWriters are found + * + * @author Elliot Kroo (elliot[at]kroo[dot]net) + */ + public GifSequenceWriter(ImageOutputStream outputStream, int imageType, int timeBetweenFramesMS, + boolean loopContinuously) throws IIOException, IOException { + // my method to create a writer + gifWriter = getWriter(); + imageWriteParam = gifWriter.getDefaultWriteParam(); + ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(imageType); + + imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam); + + String metaFormatName = imageMetaData.getNativeMetadataFormatName(); + + IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName); + + IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension"); + + graphicsControlExtensionNode.setAttribute("disposalMethod", "none"); + graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE"); + graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE"); + graphicsControlExtensionNode.setAttribute("delayTime", Integer.toString(timeBetweenFramesMS / 10)); + graphicsControlExtensionNode.setAttribute("transparentColorIndex", "0"); + + IIOMetadataNode commentsNode = getNode(root, "CommentExtensions"); + commentsNode.setAttribute("CommentExtension", "Created by MAH"); + + IIOMetadataNode appEntensionsNode = getNode(root, "ApplicationExtensions"); + + IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension"); + + child.setAttribute("applicationID", "NETSCAPE"); + child.setAttribute("authenticationCode", "2.0"); + + int loop = loopContinuously ? 0 : 1; + + child.setUserObject(new byte[] { 0x1, (byte) (loop & 0xFF), (byte) ((loop >> 8) & 0xFF) }); + appEntensionsNode.appendChild(child); + + imageMetaData.setFromTree(metaFormatName, root); + + gifWriter.setOutput(outputStream); + + gifWriter.prepareWriteSequence(null); + } + + public void writeToSequence(RenderedImage img) throws IOException { + gifWriter.writeToSequence(new IIOImage(img, null, imageMetaData), imageWriteParam); + } + + /** + * Close this GifSequenceWriter object. This does not close the underlying + * stream, just finishes off the GIF. + */ + public void close() throws IOException { + gifWriter.endWriteSequence(); + } + + /** + * Returns the first available GIF ImageWriter using + * ImageIO.getImageWritersBySuffix("gif"). + * + * @return a GIF ImageWriter object + * @throws IIOException + * if no GIF image writers are returned + */ + private static ImageWriter getWriter() throws IIOException { + Iterator iter = ImageIO.getImageWritersBySuffix("gif"); + if (!iter.hasNext()) { + throw new IIOException("No GIF Image Writers Exist"); + } else { + return iter.next(); + } + } + + /** + * Returns an existing child node, or creates and returns a new child node + * (if the requested node does not exist). + * + * @param rootNode + * the IIOMetadataNode to search for the child node. + * @param nodeName + * the name of the child node. + * + * @return the child node, if found or a new node created with the given + * name. + */ + private static IIOMetadataNode getNode(IIOMetadataNode rootNode, String nodeName) { + int nNodes = rootNode.getLength(); + for (int i = 0; i < nNodes; i++) { + if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) == 0) { + return ((IIOMetadataNode) rootNode.item(i)); + } + } + IIOMetadataNode node = new IIOMetadataNode(nodeName); + rootNode.appendChild(node); + return (node); + } + + /** + * public GifSequenceWriter( BufferedOutputStream outputStream, int + * imageType, int timeBetweenFramesMS, boolean loopContinuously) { + * + */ + + public static void main(String[] args) throws Exception { + if (args.length > 1) { + // grab the output image type from the first image in the sequence + BufferedImage firstImage = ImageIO.read(new File(args[0])); + + // create a new BufferedOutputStream with the last argument + ImageOutputStream output = new FileImageOutputStream(new File(args[args.length - 1])); + + // create a gif sequence with the type of the first image, 1 second + // between frames, which loops continuously + GifSequenceWriter writer = new GifSequenceWriter(output, firstImage.getType(), 1, false); + + // write out the first image to our sequence... + writer.writeToSequence(firstImage); + for (int i = 1; i < args.length - 1; i++) { + BufferedImage nextImage = ImageIO.read(new File(args[i])); + writer.writeToSequence(nextImage); + } + + writer.close(); + output.close(); + } else { + System.out.println("Usage: java GifSequenceWriter [list of gif files] [output file]"); + } + } +} \ No newline at end of file diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/capture/ScreenCapture.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/capture/ScreenCapture.java new file mode 100644 index 0000000..3aa55b4 --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/capture/ScreenCapture.java @@ -0,0 +1,113 @@ +package jspectrumanalyzer.capture; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import javax.imageio.stream.FileImageOutputStream; +import javax.imageio.stream.ImageOutputStream; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +/** + * Class to capture a video of the whole JFrame into the animated GIF, while capturing only when the view updates with the new data + */ +public class ScreenCapture { + private final JFrame frame; + private final int width, height; + private final ImageOutputStream output; + private final GifSequenceWriter gif; + private final long captureMillis; + private final File outputFile; + private long startCaptureTS = 0; + private long startedCapture = 0; + private long lastFrameTS = 0; + private int framesCaptured = 0; + private ExecutorService saveThread = Executors.newSingleThreadExecutor(); + private long frameIIntervalMS; + + public ScreenCapture(JFrame frame, int initSecs, int captureSecs, int fps, int width, int height, File outputFile) throws FileNotFoundException, IOException { + this.captureMillis = captureSecs*1000L; + this.frame = frame; + this.width = width; + this.height = height; + this.outputFile = outputFile; + + this.startCaptureTS = System.currentTimeMillis()+initSecs*1000L; + frameIIntervalMS = 1000/fps; + frame.setSize(width, height); + + outputFile.delete(); + + output = new FileImageOutputStream(outputFile); + gif = new GifSequenceWriter(output, BufferedImage.TYPE_3BYTE_BGR, (int)frameIIntervalMS, true); + } + + public void captureFrame() { + if (!SwingUtilities.isEventDispatchThread()) { + throw new IllegalStateException("Capture is NOT inside event dispatch thread!"); + } + + long start = System.currentTimeMillis(); + + if (start-lastFrameTS < frameIIntervalMS || start < startCaptureTS) { + return; + } + lastFrameTS = start; + + if (framesCaptured == -1) + return; + if (framesCaptured == 0) { + startedCapture = System.currentTimeMillis(); + System.out.println("Capture started..."); + } + + if (System.currentTimeMillis() - startedCapture >= captureMillis) { + System.out.println("Capture finished... frames captured: "+framesCaptured); + framesCaptured = -1; +// task.cancel(); + try { + gif.close(); + } catch (IOException e) { + e.printStackTrace(); + } + try { + output.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + System.exit(0); + return; + } + + framesCaptured++; + BufferedImage capImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); + Graphics2D g = (Graphics2D) capImage.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + frame.paint(g); + g.dispose(); + + /** + * convert to gif in a separate thread to not slow down swing's event thread + */ + saveThread.submit(() -> { + try { + gif.writeToSequence(capImage); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + System.out.println("time to save gif "+(System.currentTimeMillis()-start)); + } + + + +} diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/DatasetSpectrum.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/DatasetSpectrum.java index 8f9a15d..eacfee3 100644 --- a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/DatasetSpectrum.java +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/DatasetSpectrum.java @@ -6,19 +6,25 @@ import org.jfree.data.xy.XYDataItem; import org.jfree.data.xy.XYSeries; +import jspectrumanalyzer.core.jfc.XYSeriesImmutable; + public class DatasetSpectrum implements Cloneable { + /** + * caching decreases GC usage + */ + private final boolean useCached = false; + protected ArrayList> cachedDataItems = new ArrayList<>(); + protected int cachedDataItemsIndex = 0; protected final float fftBinSizeHz; + protected final long freqStartHz; protected final int freqStartMHz; + protected final int freqStopMHz; - protected float[] spectrum; protected float spectrumInitPower; - protected ArrayList> cachedDataItems = new ArrayList<>(); - protected int cachedDataItemsIndex = 0; - /** * Inits * @param fftBinSizeHz @@ -40,21 +46,39 @@ public DatasetSpectrum(float fftBinSizeHz, int freqStartMHz, int freqStopMHz, fl spectrum = new float[datapoints]; Arrays.fill(spectrum, spectrumInitPower); - for (int j = 0; j < 5; j++) { - ArrayList list = new ArrayList<>(); - for (int i = 0; i < datapoints; i++) { - double freq = (freqStartHz + fftBinSizeHz * i) / 1000000; - list.add(new XYDataItem(freq, 0)); + if (useCached) { + for (int j = 0; j < 5; j++) { + ArrayList list = new ArrayList<>(); + for (int i = 0; i < datapoints; i++) { + double freq = (freqStartHz + fftBinSizeHz * i) / 1000000; + list.add(new XYDataItem(freq, 0)); + } + cachedDataItems.add(list); } - cachedDataItems.add(list); } } - @Override protected Object clone() throws CloneNotSupportedException + /** + * Adds new data to spectrum's dataset + * @param fftBins + * @return true if the whole spectrum was refreshed once + */ + public boolean addNewData(FFTBins fftBins) { - DatasetSpectrum copy = (DatasetSpectrum) super.clone(); - copy.spectrum = spectrum.clone(); - return copy; + boolean triggerRefresh = false; + triggerRefresh = fftBins.fullSweepDone; + + for (int binsIndex = 0; binsIndex < fftBins.freqStart.length; binsIndex++) + { + double freqStart = fftBins.freqStart[binsIndex]; + int spectrIndex = (int) ((freqStart - freqStartHz) / fftBinSizeHz); + if (spectrIndex < 0 || spectrIndex >= spectrum.length) + continue; + spectrum[spectrIndex] = fftBins.sigPowdBm[binsIndex]; + } + + + return triggerRefresh; } public DatasetSpectrum cloneMe() @@ -73,28 +97,31 @@ public DatasetSpectrum cloneMe() } /** - * Adds new data to spectrum's dataset - * @param fftBins - * @return true if the whole spectrum was refreshed once + * Copies spectrum to destination dataset + * @param filtered */ - public boolean addNewData(FFTBins fftBins) + public void copyTo(DatasetSpectrum filtered) { - boolean triggerRefresh = false; - triggerRefresh = fftBins.fullSweepDone; + System.arraycopy(spectrum, 0, filtered.spectrum, 0, spectrum.length); + } - for (int binsIndex = 0; binsIndex < fftBins.freqStart.length; binsIndex++) + /** + * Creates {@link XYSeriesImmutable} from spectrum data + * @param name + * @return + */ + public XYSeriesImmutable createSpectrumDataset(String name) { + float[] xValues = new float[spectrum.length]; + float[] yValues = spectrum; + for (int i = 0; i < spectrum.length; i++) { - double freqStart = fftBins.freqStart[binsIndex]; - int spectrIndex = (int) ((freqStart - freqStartHz) / fftBinSizeHz); - if (spectrIndex < 0 || spectrIndex >= spectrum.length) - continue; - spectrum[spectrIndex] = fftBins.sigPowdBm[binsIndex]; + float freq = (freqStartHz + fftBinSizeHz * i) / 1000000f; + xValues[i] = freq; } - - - return triggerRefresh; + XYSeriesImmutable xySeriesF = new XYSeriesImmutable(name, xValues, yValues); + return xySeriesF; } - + /** * Fills data to {@link XYSeries}, uses x units in MHz * @param series @@ -104,36 +131,12 @@ public void fillToXYSeries(XYSeries series) fillToXYSeriesPriv(series, spectrum); } - protected void fillToXYSeriesPriv(XYSeries series, float[] spectrum){ - series.clear(); - /** - * caching decreases GC usage - */ - boolean useCached = false; - if (!useCached){ - for (int i = 0; i < spectrum.length; i++) - { - double freq = (freqStartHz + fftBinSizeHz * i) / 1000000; - series.add(freq, spectrum[i]); - } - } - else{ - ArrayList items = cachedDataItems.get(cachedDataItemsIndex); - for (int i = 0; i < spectrum.length; i++) - { - XYDataItem item = items.get(i); - item.setY(spectrum[i]); - series.add(item); - } - cachedDataItemsIndex = (cachedDataItemsIndex+1)%cachedDataItems.size(); - } - } - + public float getFFTBinSizeHz() { return fftBinSizeHz; } - + public int getFreqStartMHz() { return freqStartMHz; @@ -160,13 +163,18 @@ public float getPower(int index) return spectrum[index]; } + public float[] getSpectrumArray() + { + return spectrum; + } + public void resetSpectrum() { Arrays.fill(spectrum, spectrumInitPower); } - public float[] getSpectrumArray() + public void setSpectrumInitPower(float spectrumInitPower) { - return spectrum; + this.spectrumInitPower = spectrumInitPower; } public int spectrumLength() @@ -174,17 +182,31 @@ public int spectrumLength() return spectrum.length; } - public void setSpectrumInitPower(float spectrumInitPower) + @Override protected Object clone() throws CloneNotSupportedException { - this.spectrumInitPower = spectrumInitPower; + DatasetSpectrum copy = (DatasetSpectrum) super.clone(); + copy.spectrum = spectrum.clone(); + return copy; } - /** - * Copies spectrum to destination dataset - * @param filtered - */ - public void copyTo(DatasetSpectrum filtered) - { - System.arraycopy(spectrum, 0, filtered.spectrum, 0, spectrum.length); + protected void fillToXYSeriesPriv(XYSeries series, float[] spectrum){ + series.clear(); + if (!useCached){ + for (int i = 0; i < spectrum.length; i++) + { + double freq = (freqStartHz + fftBinSizeHz * i) / 1000000; + series.add(freq, spectrum[i]); + } + } + else{ + ArrayList items = cachedDataItems.get(cachedDataItemsIndex); + for (int i = 0; i < spectrum.length; i++) + { + XYDataItem item = items.get(i); + item.setY(spectrum[i]); + series.add(item); + } + cachedDataItemsIndex = (cachedDataItemsIndex+1)%cachedDataItems.size(); + } } } diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/DatasetSpectrumPeak.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/DatasetSpectrumPeak.java index 0768df2..786440c 100644 --- a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/DatasetSpectrumPeak.java +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/DatasetSpectrumPeak.java @@ -1,11 +1,11 @@ package jspectrumanalyzer.core; -import java.util.ArrayList; import java.util.Arrays; -import org.jfree.data.xy.XYDataItem; import org.jfree.data.xy.XYSeries; +import jspectrumanalyzer.core.jfc.XYSeriesImmutable; + public class DatasetSpectrumPeak extends DatasetSpectrum { protected long lastAdded = System.currentTimeMillis(); @@ -39,6 +39,10 @@ public DatasetSpectrumPeak(float fftBinSizeHz, int freqStartMHz, int freqStopMHz } + public void setPeakFalloutMillis(long peakFalloutMillis) { + this.peakFalloutMillis = peakFalloutMillis; + } + public void copyTo(DatasetSpectrumPeak filtered) { super.copyTo(filtered); @@ -53,6 +57,20 @@ public void copyTo(DatasetSpectrumPeak filtered) public void fillPeaksToXYSeries(XYSeries series) { fillToXYSeriesPriv(series, spectrumPeakHold); +// fillToXYSeriesPriv(series, spectrumPeak); + } + + + public XYSeriesImmutable createPeaksDataset(String name) { + float[] xValues = new float[spectrum.length]; + float[] yValues = spectrumPeakHold; + for (int i = 0; i < spectrum.length; i++) + { + float freq = (freqStartHz + fftBinSizeHz * i) / 1000000f; + xValues[i] = freq; + } + XYSeriesImmutable xySeriesF = new XYSeriesImmutable(name, xValues, yValues); + return xySeriesF; } public double calculateSpectrumPeakPower(){ @@ -64,12 +82,24 @@ public double calculateSpectrumPeakPower(){ return powerSum; } + private long debugLastPeakRerfreshTime = 0; public void refreshPeakSpectrum() { + if (false) { + long debugMinPeakRefreshTime = 100; + if (System.currentTimeMillis()-debugLastPeakRerfreshTime < debugMinPeakRefreshTime) + return; + debugLastPeakRerfreshTime = System.currentTimeMillis(); + } + long timeDiffFromPrevValueMillis = System.currentTimeMillis() - lastAdded; + if (timeDiffFromPrevValueMillis < 1) + timeDiffFromPrevValueMillis = 1; + lastAdded = System.currentTimeMillis(); - - peakFallThreshold = 10; + +// peakFallThreshold = 10; +// peakFalloutMillis = 30000; for (int spectrIndex = 0; spectrIndex < spectrum.length; spectrIndex++) { float spectrumVal = spectrum[spectrIndex]; diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyAllocationTable.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyAllocationTable.java new file mode 100644 index 0000000..59233b4 --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyAllocationTable.java @@ -0,0 +1,194 @@ +package jspectrumanalyzer.core; + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.geom.Rectangle2D.Float; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.SortedSet; +import java.util.TreeSet; + +import jspectrumanalyzer.ui.GraphicsToolkit; + +public class FrequencyAllocationTable { + /** + * bands will be sorted in the set by frequency + */ + private final TreeSet frequencyBands = new TreeSet<>(); + private final String area; + + public FrequencyAllocationTable(String area, ArrayList bands) { + this.area = area; + this.frequencyBands.addAll(bands); + } + + public FrequencyBand lookupBand(long hz) { + FrequencyBand band = frequencyBands.floor(new FrequencyBand(hz, hz, "", "")); + return band; + } + + @Override + public String toString() { + return area; + } + + public ArrayList getFrequencyBands(long startHz, long endHz){ + FrequencyBand startBand = lookupBand(startHz); + ArrayList bands = new ArrayList<>(); + SortedSet entries = frequencyBands.tailSet(startBand); + for (FrequencyBand frequencyBand : entries) { + if (frequencyBand.getHzStartIncl() > endHz) { + break; + } + bands.add(frequencyBand); + } + return bands; + } + private static float map(float x, float in_min, float in_max, float out_min, float out_max) + { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } + + public BufferedImage drawAllocationTable(int width, int height, float alpha, long freqStartHz, long freqEndHz, Color textColor, Color bgColor) { + /* + * draw the image scaled up and then resize it after rendering producing high quality image + */ + int scale = 1; + int drawWidth = width*scale; + int drawHeight = height*scale; + BufferedImage i = GraphicsToolkit.createAcceleratedImageTransparent(drawWidth, drawHeight); + Graphics2D g = i.createGraphics(); + + AlphaComposite alcom = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha); + g.setComposite(alcom); + + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setStroke(new BasicStroke(1f)); + //g.rotate(Math.toRadians(90)); + + AffineTransform affineTransform = new AffineTransform(); + affineTransform.rotate(Math.toRadians(0), 0, 0); + Font font = new Font(Font.DIALOG, Font.PLAIN, 10*scale); + Font rotatedFont = font.deriveFont(affineTransform); + Rectangle2D fontBounds = g.getFontMetrics(rotatedFont).getStringBounds("80M", g); + g.setFont(rotatedFont); + int fontHeight = (int) fontBounds.getHeight(); + + ArrayList bands = getFrequencyBands(freqStartHz, freqEndHz); + + float freqRange = freqEndHz-freqStartHz; + Float rect = new Rectangle2D.Float(); + final float textOffset = fontHeight; + final float textX = fontHeight/2; + + class BandRectangle{ + ArrayList lines; /*null for no text*/; + float x,y,w; + FrequencyBand band; + String longestString; + boolean textVisible = true; + public BandRectangle(FrequencyBand band, ArrayList lines, float x, float y, float w, String longestString) { + this.band = band; + this.lines = lines; + this.x = x; + this.y = y; + this.w = w; + this.longestString = longestString; + } + + } + + ArrayList shapes = new ArrayList<>(bands.size()); + for (int j = 0; j < bands.size(); j++) { + FrequencyBand band = bands.get(j); + float xStart = (band.getHzStartIncl()-freqStartHz)*drawWidth/(freqRange); + float xEnd = (band.getHzEndExcl()-freqStartHz)*drawWidth/(freqRange); + if (xStart < 0) + xStart = 0;//if the band starts offscreen to the left, start it on 0 + float x = xStart; + float y = 0; + float w = xEnd-xStart; + + String[] lines = band.getName().split("/"); + ArrayList listLines = new ArrayList<>(lines.length+1); + listLines.add(String.format("%d - %dM", band.getHzStartIncl()/1000000, band.getHzEndExcl()/1000000)); + for (int line = 0; line < lines.length; line++) { + String linestr = lines[line]; + listLines.add(linestr); + } + /* + * find the longest string + */ + Arrays.sort(lines, (o1, o2) -> Integer.compare(o2.length(), o1.length())); + + shapes.add(new BandRectangle(band, listLines, x, y, w, lines[0])); + } + + float maxVisibleLines = 1; + for (int j = 0; j < shapes.size(); j++) { + BandRectangle shape = shapes.get(j); + /* + * determine if the first line will fit inside the rectangle + */ + Rectangle2D bounds = g.getFontMetrics(rotatedFont).getStringBounds(shape.lines.get(0), g); + if (shape.w > bounds.getWidth()) { + if (shape.lines.size() > maxVisibleLines) + maxVisibleLines = shape.lines.size(); + } + else { + shape.textVisible = false; + } + } + + float rectHeight = (maxVisibleLines) * fontHeight + fontHeight/2; + rect.height = rectHeight; + for (int j = 0; j < shapes.size(); j++) { + BandRectangle shape = shapes.get(j); + rect.x = shape.x; + rect.y = shape.y; + rect.width = shape.w; + g.setColor(bgColor); + g.fill(rect); + g.setColor(Color.black); + g.draw(rect); + if (shape.textVisible) { + g.setColor(textColor); +// g.setColor(Color.white); + Graphics2D gg = (Graphics2D) g.create((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); + for (int line = 0; line < shape.lines.size(); line++) { + String linestr = shape.lines.get(line); + gg.drawString(linestr, textX, textOffset + (line)*fontHeight); + } + gg.dispose(); + } + } + + g.dispose(); + if (scale > 1) { + BufferedImage iOut = GraphicsToolkit.createAcceleratedImageTransparent(width, height); + g = iOut.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g.drawImage(i, 0, 0, width, height, null); + g.dispose(); + return iOut; + } + return i; + } + +} diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyAllocations.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyAllocations.java new file mode 100644 index 0000000..7ea3cbd --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyAllocations.java @@ -0,0 +1,71 @@ +package jspectrumanalyzer.core; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class FrequencyAllocations { + private HashMap table = new HashMap<>(); + + public FrequencyAllocations() { + loadEurope(); + } + + private Pattern patternEU = Pattern.compile("\"[^\"]+\";\"([0-9.]+)\\s+-\\s+([0-9.]+)\\s+([kM])Hz\";\"([^\"]+)\";\"([^\"]+)\""); + + public HashMap getTable() { + return new HashMap<>(table); + } + + private void loadEurope() { + BufferedReader reader = null; + + ArrayList bands = new ArrayList<>(); + + try { + /** + * Source: + * https://www.efis.dk/views2/search-general.jsp + */ + reader = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("/resources/freq-europe.csv"))); + String line = null; + int lineNo = 0; + while((line = reader.readLine()) != null) { + lineNo++; + if (lineNo == 1) + continue; + + //"- Europe (ECA) -";"526.500 - 1606.500 kHz";"Broadcasting";"Inductive applications/Broadcasting" + //"- Europe (ECA) -";"5925.000 - 6700.000 MHz";"Fixed/Fixed-Satellite (Earth-to-space)/Earth Exploration-Satellite (passive)";"Passive sensors (satellite)/Fixed/FSS Earth stations/Radiodetermination applications/UWB applications/-/ESV/Radio astronomy" + Matcher m = patternEU.matcher(line); + if (m.find()) { + double multiplier = m.group(3).equals("k") ? 1000 : 1000000; + long startFreq = Math.round(Double.parseDouble(m.group(1)) * multiplier); + long stopFreq = Math.round(Double.parseDouble(m.group(2)) * multiplier); + String name = m.group(4); + String applications = m.group(5); + FrequencyBand band = new FrequencyBand(startFreq, stopFreq, name, applications); + bands.add(band); + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + + } + } + } + FrequencyAllocationTable allocationTable = new FrequencyAllocationTable("Europe", bands); + table.put("Europe", allocationTable); + } +} diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyBand.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyBand.java new file mode 100644 index 0000000..501f42b --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyBand.java @@ -0,0 +1,49 @@ +package jspectrumanalyzer.core; + +/** + * Describes one frequency allocation band by start frequency and end frequency + */ +public class FrequencyBand implements Comparable{ + private final long hzStartIncl; + private final long hzEndExcl; + private final String name; + private final String applications; + + public FrequencyBand(long hzStartIncl, long hzEndExcl, String name, String applications) { + this.hzStartIncl = hzStartIncl; + this.hzEndExcl = hzEndExcl; + this.name = name; + this.applications = applications; + } + public long getHzStartIncl() { + return hzStartIncl; + } + + public double getMHzStartIncl() { + return hzStartIncl/1000000d; + } + + public long getHzEndExcl() { + return hzEndExcl; + } + public double getMHzEndExcl() { + return hzEndExcl/1000000d; + } + public String getName() { + return name; + } + public String getApplications() { + return applications; + } + + @Override + public String toString() { + return name; + } + + @Override + public int compareTo(FrequencyBand o) { + return Long.compare(hzStartIncl, o.hzStartIncl); + } + +} \ No newline at end of file diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyRange.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyRange.java new file mode 100644 index 0000000..509f7ca --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyRange.java @@ -0,0 +1,25 @@ +package jspectrumanalyzer.core; + +public class FrequencyRange{ + private final int startMHz, endMHz; + + public FrequencyRange(int startMHz, int endMHz) { + this.startMHz = startMHz; + this.endMHz = endMHz; + } + public int getEndMHz() { + return endMHz; + } + public int getStartMHz() { + return startMHz; + } + @Override + public boolean equals(Object obj) { + if (obj instanceof FrequencyRange) { + FrequencyRange fr = (FrequencyRange)obj; + if (fr.endMHz == endMHz && fr.startMHz == startMHz) + return true; + } + return false; + } +} \ No newline at end of file diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyRangeSelector.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyRangeSelector.java deleted file mode 100644 index 8c410a3..0000000 --- a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencyRangeSelector.java +++ /dev/null @@ -1,47 +0,0 @@ -package jspectrumanalyzer.core; - -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.beans.PropertyVetoException; -import java.beans.VetoableChangeListener; - -/** - * Limits frequency selection of two selectors (start/end) - */ -public class FrequencyRangeSelector -{ - public FrequencyRangeSelector(FrequencySelectorPanel selFreqStart, FrequencySelectorPanel selFreqEnd, PropertyChangeListener propertyChangeListener) - { - VetoableChangeListener freqStartVetoable = new VetoableChangeListener() - { - @Override public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException - { - Integer newVal = (Integer) evt.getNewValue(); - if (newVal >= (Integer) selFreqEnd.getValue()) - { - //try to increase freq end by the same value - if (!selFreqEnd.setValue(selFreqEnd.getValue() + (newVal - (Integer) evt.getOldValue()))) - throw new PropertyVetoException(">", evt); - } - } - }; - VetoableChangeListener freqEndVetoable = new VetoableChangeListener() - { - @Override public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException - { - Integer newVal = (Integer) evt.getNewValue(); - if (newVal <= (Integer) selFreqStart.getValue()) - { - if (!selFreqStart.setValue(selFreqStart.getValue() - ((Integer) evt.getOldValue() - newVal))) - throw new PropertyVetoException(">", evt); - } - } - }; - - selFreqStart.addPropertyChangeListener("value", propertyChangeListener); - selFreqEnd.addPropertyChangeListener("value", propertyChangeListener); - - selFreqEnd.addVetoableChangeListener(freqEndVetoable); - selFreqStart.addVetoableChangeListener(freqStartVetoable); - } -} diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/HackRFSettings.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/HackRFSettings.java index 4e8c81a..488a19b 100644 --- a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/HackRFSettings.java +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/HackRFSettings.java @@ -1,78 +1,70 @@ package jspectrumanalyzer.core; -public interface HackRFSettings -{ +import java.math.BigDecimal; + +import shared.mvc.ModelValue; +import shared.mvc.ModelValue.ModelValueBoolean; +import shared.mvc.ModelValue.ModelValueInt; + +public interface HackRFSettings { public static abstract class HackRFEventAdapter implements HackRFEventListener { - @Override public void captureStateChanged(boolean isCapturing) - { - + @Override + public void captureStateChanged(boolean isCapturing) { + } - @Override public void hardwareStatusChanged(boolean hardwareSendingData) - { - + + @Override + public void hardwareStatusChanged(boolean hardwareSendingData) { + } } - public static interface HackRFEventListener{ + public static interface HackRFEventListener { public void captureStateChanged(boolean isCapturing); + public void hardwareStatusChanged(boolean hardwareSendingData); } - public int getFFTBinHz(); + public ModelValueBoolean getAntennaPowerEnable(); - /** - * Get sweep frequency end MHz - * @return - */ - public int getFrequencyEnd(); + public ModelValueInt getFFTBinHz(); - /** - * Get sweep frequency start MHz - * @return - */ - public int getFrequencyStart(); + public ModelValue getFrequency(); - public int getGain(); + public ModelValueInt getGain(); - public int getLNAGain(); + public ModelValueInt getGainLNA(); + + public ModelValueInt getPersistentDisplayDecayRate(); + + public ModelValueBoolean isDebugDisplay(); - public int getSamples(); + public ModelValueInt getSamples(); + + public ModelValueInt getSpectrumPaletteSize(); + + public ModelValueBoolean isPersistentDisplayVisible(); + public ModelValueBoolean isWaterfallVisible(); - public int getSpectrumPaletteSize(); + public ModelValueInt getSpectrumPaletteStart(); + + public ModelValueInt getPeakFallRate(); + + public ModelValue getFrequencyAllocationTable(); - public int getSpectrumPaletteStart(); + public ModelValue getSpectrumLineThickness(); - public boolean isSpurRemoval(); + public ModelValueInt getGainVGA(); - public int getVGAGain(); + public ModelValueBoolean isCapturingPaused(); - public boolean isCapturing(); + public ModelValueBoolean isChartsPeaksVisible(); - public boolean isChartsPeaksVisible(); + public ModelValueBoolean isFilterSpectrum(); - public boolean isFilterSpectrum(); + public ModelValueBoolean isSpurRemoval(); public void registerListener(HackRFEventListener listener); public void removeListener(HackRFEventListener listener); - - public void setCapturing(boolean pause); - - public void setChartPeaksVisibility(boolean visible); - - public void setFFTBin(int fftBinHz); - - public void setFilterSpectrum(boolean filter); - - public void setFrequency(int freqStartMHz, int freqEndMHz); - public void setGain(int gaindB); - - public void setSamples(int samples); - public void setSpectrumPaletteSize(int dB); - - public void setSpectrumPaletteStart(int dB); - public void setSpurRemoval(boolean enable); - - public void setAntennaPowerEnable(boolean enable); - public boolean getAntennaPowerEnable(); } diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/HackRFSweepSettingsUI.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/HackRFSweepSettingsUI.java deleted file mode 100644 index 537c2fc..0000000 --- a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/HackRFSweepSettingsUI.java +++ /dev/null @@ -1,286 +0,0 @@ -package jspectrumanalyzer.core; - -import java.awt.Color; -import java.awt.Desktop; -import java.awt.Font; -import java.awt.Label; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.net.URI; - -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JSlider; -import javax.swing.JSpinner; -import javax.swing.JSpinner.ListEditor; -import javax.swing.JTextField; -import javax.swing.SpinnerListModel; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import jspectrumanalyzer.HackRFSweepSpectrumAnalyzer; -import jspectrumanalyzer.Version; -import net.miginfocom.swing.MigLayout; - -public class HackRFSweepSettingsUI extends JPanel -{ - /** - * - */ - private static final long serialVersionUID = 7721079457485020637L; - private JTextField txtHackrfConnected; - - /** - * Create the panel. - */ - public HackRFSweepSettingsUI(HackRFSettings hackRFSettings) - { - setForeground(Color.WHITE); - setBackground(Color.BLACK); - int minFreq = 1; - int maxFreq = 7250; - int freqStep = 1; - - setLayout(new MigLayout("", "[123.00px,grow,leading]", "[][20px][][][20px][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]")); - - JLabel lblNewLabel = new JLabel("Frequency start [MHz]"); - lblNewLabel.setForeground(Color.WHITE); - add(lblNewLabel, "cell 0 0,growx,aligny center"); - - FrequencySelectorPanel frequencySelectorStart = new FrequencySelectorPanel(minFreq, maxFreq, freqStep, minFreq); - add(frequencySelectorStart, "cell 0 1,grow"); - - frequencySelectorStart.setValue(hackRFSettings.getFrequencyStart()); - - JLabel lblFrequencyEndmhz = new JLabel("Frequency end [MHz]"); - lblFrequencyEndmhz.setForeground(Color.WHITE); - add(lblFrequencyEndmhz, "cell 0 3,alignx left,aligny center"); - - FrequencySelectorPanel frequencySelectorEnd = new FrequencySelectorPanel(minFreq, maxFreq, freqStep, maxFreq); - add(frequencySelectorEnd, "cell 0 4,grow"); - frequencySelectorEnd.setValue(hackRFSettings.getFrequencyEnd()); - - JLabel lblFftBinhz = new JLabel("FFT Bin [Hz]"); - lblFftBinhz.setForeground(Color.WHITE); - add(lblFftBinhz, "cell 0 6"); - - JSpinner spinnerFFTBinHz = new JSpinner(); - spinnerFFTBinHz.setFont(new Font("Monospaced", Font.BOLD, 16)); - spinnerFFTBinHz.setModel(new SpinnerListModel( - new String[] { "1000", "2000", "5000", "10 000", "20 000", "50 000", "100 000", "200 000", "500 000", "1 000 000", "2 000 000", "5 000 000" })); - add(spinnerFFTBinHz, "cell 0 7,growx"); - ((ListEditor) spinnerFFTBinHz.getEditor()).getTextField().setHorizontalAlignment(JTextField.RIGHT); - spinnerFFTBinHz.addChangeListener(new ChangeListener() - { - @Override public void stateChanged(ChangeEvent e) - { - hackRFSettings.setFFTBin(Integer.parseInt(spinnerFFTBinHz.getValue().toString().replaceAll("\\s", ""))); - } - }); - spinnerFFTBinHz.setValue("100 000"); - - JLabel lblGain = new JLabel("Gain [dB]"); - lblGain.setForeground(Color.WHITE); - add(lblGain, "cell 0 9"); - - JSlider sliderGain = new JSlider(JSlider.HORIZONTAL, 0, 100, 2); - sliderGain.setFont(new Font("Monospaced", Font.BOLD, 16)); - sliderGain.setBackground(Color.BLACK); - sliderGain.setForeground(Color.WHITE); - add(sliderGain, "flowy,cell 0 10,growx"); - - JLabel lbl_gainValue = new JLabel(hackRFSettings.getGain() + "dB"); - lbl_gainValue.setForeground(Color.WHITE); - add(lbl_gainValue, "cell 0 10,alignx right"); - - sliderGain.addChangeListener(new ChangeListener() - { - @Override public void stateChanged(ChangeEvent e) - { - int val = sliderGain.getValue(); - hackRFSettings.setGain(val); - lbl_gainValue.setText( - String.format(" %ddB [LNA: %ddB VGA: %ddB]", hackRFSettings.getGain(), hackRFSettings.getLNAGain(), hackRFSettings.getVGAGain())); - } - }); - sliderGain.setValue(hackRFSettings.getGain()); - - JLabel lblNumberOfSamples = new JLabel("Number of samples"); - lblNumberOfSamples.setForeground(Color.WHITE); - add(lblNumberOfSamples, "cell 0 12"); - - JSpinner spinner_numberOfSamples = new JSpinner(); - spinner_numberOfSamples.setModel(new SpinnerListModel(new String[] { "8192", "16384", "32768", "65536", "131072", "262144" })); - spinner_numberOfSamples.setFont(new Font("Monospaced", Font.BOLD, 16)); - ((ListEditor) spinner_numberOfSamples.getEditor()).getTextField().setHorizontalAlignment(JTextField.RIGHT); - ((ListEditor) spinner_numberOfSamples.getEditor()).getTextField().setEditable(false); - ; - add(spinner_numberOfSamples, "cell 0 13,growx"); - - JCheckBox chckbxAntennaPower = new JCheckBox("Antenna power"); - chckbxAntennaPower.setBackground(Color.BLACK); - chckbxAntennaPower.setForeground(Color.WHITE); - add(chckbxAntennaPower, "cell 0 15"); - chckbxAntennaPower.setSelected(hackRFSettings.getAntennaPowerEnable()); - chckbxAntennaPower.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - hackRFSettings.setAntennaPowerEnable(chckbxAntennaPower.isSelected()); - } - }); - - - JLabel lblWaterfallPaletteStart = new JLabel("Waterfall palette start [dB]"); - lblWaterfallPaletteStart.setForeground(Color.WHITE); - add(lblWaterfallPaletteStart, "cell 0 18"); - - JSlider slider_waterfallPaletteStart = new JSlider(); - slider_waterfallPaletteStart.setForeground(Color.WHITE); - slider_waterfallPaletteStart.setBackground(Color.BLACK); - slider_waterfallPaletteStart.setMinimum(-100); - slider_waterfallPaletteStart.setMaximum(0); - slider_waterfallPaletteStart.setValue(-30); - add(slider_waterfallPaletteStart, "cell 0 19,growx"); - slider_waterfallPaletteStart.setValue(hackRFSettings.getSpectrumPaletteStart()); - slider_waterfallPaletteStart.addChangeListener(new ChangeListener() - { - @Override public void stateChanged(ChangeEvent e) - { - hackRFSettings.setSpectrumPaletteStart(slider_waterfallPaletteStart.getValue()); - } - }); - - spinner_numberOfSamples.setValue(hackRFSettings.getSamples() + ""); - spinner_numberOfSamples.addChangeListener(new ChangeListener() - { - @Override public void stateChanged(ChangeEvent e) - { - hackRFSettings.setSamples(Integer.parseInt(spinner_numberOfSamples.getValue().toString())); - } - }); - - JLabel lblWaterfallPaletteLength = new JLabel("Waterfall palette length [dB]"); - lblWaterfallPaletteLength.setForeground(Color.WHITE); - add(lblWaterfallPaletteLength, "cell 0 21"); - - JSlider slider_waterfallPaletteSize = new JSlider(HackRFSweepSpectrumAnalyzer.SPECTRUM_PALETTE_SIZE_MIN, 100); - slider_waterfallPaletteSize.setBackground(Color.BLACK); - slider_waterfallPaletteSize.setForeground(Color.WHITE); - add(slider_waterfallPaletteSize, "cell 0 22,growx"); - - slider_waterfallPaletteSize.setValue(hackRFSettings.getSpectrumPaletteSize()); - - slider_waterfallPaletteSize.addChangeListener(new ChangeListener() - { - @Override public void stateChanged(ChangeEvent e) - { - hackRFSettings.setSpectrumPaletteSize(slider_waterfallPaletteSize.getValue()); - } - }); - - FrequencyRangeSelector frequencyRangeSelector = new FrequencyRangeSelector(frequencySelectorStart, frequencySelectorEnd, new PropertyChangeListener() - { - @Override public void propertyChange(PropertyChangeEvent evt) - { - hackRFSettings.setFrequency(frequencySelectorStart.getValue(), frequencySelectorEnd.getValue()); - } - }); - - JCheckBox chckbxShowPeaks = new JCheckBox("Show peaks"); - chckbxShowPeaks.setForeground(Color.WHITE); - chckbxShowPeaks.setBackground(Color.BLACK); - add(chckbxShowPeaks, "cell 0 24,growx"); - - JCheckBox chckbxRemoveSpurs = new JCheckBox("Spur filter (may distort real signals)"); - chckbxRemoveSpurs.setForeground(Color.WHITE); - chckbxRemoveSpurs.setBackground(Color.BLACK); - add(chckbxRemoveSpurs, "cell 0 26"); - - txtHackrfConnected = new JTextField(); - txtHackrfConnected.setText("HackRF connected"); - txtHackrfConnected.setForeground(Color.WHITE); - txtHackrfConnected.setBackground(Color.BLACK); - add(txtHackrfConnected, "cell 0 29,growx"); - txtHackrfConnected.setColumns(10); - txtHackrfConnected.setBorder(null); - - JButton btnPause = new JButton("Pause"); - add(btnPause, "cell 0 31,growx"); - btnPause.setBackground(Color.black); - - JButton btnAbout = new JButton("Visit homepage"); - btnAbout.addActionListener(new ActionListener() - { - @Override public void actionPerformed(ActionEvent e) - { - if (Desktop.isDesktopSupported()) { - Desktop desktop = Desktop.getDesktop(); - try { - URI uri = new URI(Version.url); - desktop.browse(uri); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - } - }); - - Label labelVersion = new Label("Version: v"+Version.version); - add(labelVersion, "flowx,cell 0 42"); - btnAbout.setBackground(Color.BLACK); - add(btnAbout, "cell 0 42,alignx right"); - btnPause.addActionListener(new ActionListener() - { - @Override public void actionPerformed(ActionEvent e) - { - hackRFSettings.setCapturing(!hackRFSettings.isCapturing()); - } - }); - hackRFSettings.registerListener(new HackRFSettings.HackRFEventAdapter() - { - @Override public void captureStateChanged(boolean isCapturing) - { - btnPause.setText(isCapturing ? "Pause" : "Resume"); - } - @Override public void hardwareStatusChanged(boolean hardwareSendingData) - { - txtHackrfConnected.setText("HackRF "+(hardwareSendingData ? "connected":"disconnected")); - } - });; - - JCheckBox chckbxFilterSpectrum = new JCheckBox("Filter spectrum"); - chckbxFilterSpectrum.setBackground(Color.BLACK); - chckbxFilterSpectrum.setForeground(Color.WHITE); - // add(chckbxFilterSpectrum, "cell 0 23"); - - chckbxShowPeaks.addActionListener(new ActionListener() - { - @Override public void actionPerformed(ActionEvent e) - { - hackRFSettings.setChartPeaksVisibility(chckbxShowPeaks.isSelected()); - } - }); - - chckbxFilterSpectrum.addActionListener(new ActionListener() - { - @Override public void actionPerformed(ActionEvent e) - { - hackRFSettings.setFilterSpectrum(chckbxFilterSpectrum.isSelected()); - } - }); - - chckbxRemoveSpurs.setSelected(hackRFSettings.isSpurRemoval()); - chckbxRemoveSpurs.addActionListener(new ActionListener() - { - @Override public void actionPerformed(ActionEvent e) - { - hackRFSettings.setSpurRemoval(chckbxRemoveSpurs.isSelected()); - } - }); - } - -} diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/PersistentDisplay.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/PersistentDisplay.java new file mode 100644 index 0000000..6fc6ebe --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/PersistentDisplay.java @@ -0,0 +1,248 @@ +package jspectrumanalyzer.core; + +import java.awt.Color; +import java.awt.image.BufferedImage; + +import jspectrumanalyzer.ui.GraphicsToolkit; +import jspectrumanalyzer.ui.HotIronBluePalette; +import shared.mvc.ModelValue; + +public class PersistentDisplay { + /** + * Image represented by single float array + */ + private static class FloatImage { + private final float[] data; + private final int width, height; + + public FloatImage(int width, int height) { + data = new float[width * height]; + this.width = width; + this.height = height; + } + + public float add(int x, int y, float power) { + return data[y * width + x] += power; + } + + public float get(int x, int y) { + return data[y * width + x]; + } + + public int getIndex(int x, int y) { + return y * width + x; + } + + public void multiplyAllValues(float value) { + for (int i = 0; i < data.length; i++) { + data[i] *= value; + } + } + + public void set(int x, int y, float value) { + data[y * width + x] = value; + } + + public void subtractAllValues(float value) { + for (int i = 0; i < data.length; i++) { + data[i] -= value; + } + } + } + + public static float map(float in, float in_min, float in_max, float out_min, float out_max) { + return (in - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } + + public static int map(int x, int in_min, int in_max, int out_min, int out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + } + + private boolean calibrated = false; + private boolean calibrating = false; + private long calibrationStarted = 0; + private final long calibrationTime = 1000; + private ModelValue displayImage = new ModelValue("", null); + private FloatImage imagePowerAccumulated; + private int incomingDataCounter = 0; + private HotIronBluePalette palette = new HotIronBluePalette(); + private int persistenceTimeSecs = 5; + private float updatesPerSecond = 1; + + public PersistentDisplay() { + setImageSize(320, 240); + } + + public void drawSpectrum2(DatasetSpectrum datasetSpectrum, float yMin, float yMax, boolean renderImage) { + drawSpectrumFloat(datasetSpectrum, yMin, yMax, renderImage); + } + + public void drawSpectrumFloat(DatasetSpectrum datasetSpectrum, float yMin, float yMax, boolean renderImage) { + if (!calibrated) { + if (!calibrating) { + calibrating = true; + calibrationStarted = System.currentTimeMillis(); + incomingDataCounter = 0; + } else { + incomingDataCounter++; + long t = System.currentTimeMillis() - calibrationStarted; + if (t >= calibrationTime) { + updatesPerSecond = (float) incomingDataCounter / (t / 1000f); + int bins = (int) ((datasetSpectrum.getFreqStopMHz() - datasetSpectrum.getFreqStartMHz()) * 1000000l + / datasetSpectrum.getFFTBinSizeHz()); + BufferedImage image = displayImage.getValue(); + if (bins < image.getWidth()) { + setImageSize(bins, image.getHeight()); + } + calibrated = true; + calibrating = false; + + if (updatesPerSecond < 1) + updatesPerSecond = 1; + } + } + return; + } + + BufferedImage image = this.displayImage.getValue(); + FloatImage imagePowerAccumulated = this.imagePowerAccumulated; + + if (image == null) + return; + + float rawImagePowerArr[] = imagePowerAccumulated.data; + + /** + * EMA + */ + float order = persistenceTimeSecs * updatesPerSecond; + float k = 2f / (order + 1f); + // double result = currentValue * k + previousEMA * (1 - k); + float kM1 = 1 - k; /* apply decay only */ + imagePowerAccumulated.multiplyAllValues(kM1); + + float[] spectrum = datasetSpectrum.getSpectrumArray(); + int width = image.getWidth(); + int height = image.getHeight(); + float hDivYRange = (-height) / (yMax - yMin); + + /** + * pipeline: float image accumulates power for each pixel, then the + * power value gets converted to color based on the hot iron palette + */ + float maxAccumulatedValue = updatesPerSecond * persistenceTimeSecs; + for (int i = 0; i < spectrum.length; i++) { + float power = spectrum[i]; + float powerLin = 1; /* + * each occurence of power value at given + * frequency is simply +1 + */ + + int x = i * width / spectrum.length; + int y = //(power - yMin) * (0 - height) / (yMax - yMin) + height; + (int) ((power - yMin) * hDivYRange + + height); /* optimized map() */ + + if (x >= 0 && y >= 0 && x < width && y < height) { + int index = imagePowerAccumulated.getIndex(x, y); + if (imagePowerAccumulated.data[index] < maxAccumulatedValue) + imagePowerAccumulated.data[index] += powerLin; + } + } + + /** + * render image only when requested + */ + if (renderImage) { + /** + * Find the max value to properly scale + */ + float maxValue = Float.MIN_NORMAL; + for (int i = 0; i < rawImagePowerArr.length; i++) { + float value = rawImagePowerArr[i]; + if (value > maxValue) + maxValue = value; + } + + /** + * Fill the image with black color + */ + // Graphics2D g = image.createGraphics(); + // g.setColor(Color.red); + // g.fillRect(0, 0, width, height); + // g.dispose(); + // renderImage = false; + // + + float setToZeroThreshold = 0.01f; + float minOutToLog = 1.0f; + float maxOutToLog = 100; + float logMin = (float) Math.log10(minOutToLog); + float logMax = (float) Math.log10(maxOutToLog); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + float val = imagePowerAccumulated.get(x, y); + if (val < setToZeroThreshold) { + imagePowerAccumulated.set(x, y, 0); + val = 0; + } + + if (val == 0) { + image.setRGB(x, y, Color.black.getRGB()); + } else { + float outPower = val; + + /** + * Log compressed values + */ + outPower = (float) Math.log10(map(outPower, 0, maxValue, minOutToLog, maxOutToLog)); + float normalized = map(outPower, logMin, logMax, 0.15f, 0.95f); //(imagePowerAccumulated.get(x, y)) / (maxValue); + + /** + * linear values + */ + // float normalized = map(outPower, 0, maxValue, 0.4f, 0.9f); //(imagePowerAccumulated.get(x, y)) / (maxValue); + + Color color = palette.getColorNormalized(normalized); + + image.setRGB(x, y, color.getRGB()); + // g.setColor(color); + // g.drawLine(x, y, x, y); + } + } + } + + } + } + + public ModelValue getDisplayImage() { + return displayImage; + } + + public int getPersistenceTime() { + return persistenceTimeSecs; + } + + public void reset() { + BufferedImage image = displayImage.getValue(); + if (image != null) { + setImageSize(image.getWidth(), image.getHeight()); + } + } + + public void setImageSize(int width, int height) { + if (width < 1 || height < 1) + return; + + calibrated = false; + calibrating = false; + + System.out.println("Persistent image set to " + width + "x" + height); + displayImage.setValue(GraphicsToolkit.createAcceleratedImageOpaque(width, height)); + imagePowerAccumulated = new FloatImage(width, height); + } + + public void setPersistenceTime(int persistenceTimeSecs) { + this.persistenceTimeSecs = persistenceTimeSecs; + } +} diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/WaterfallPlot.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/WaterfallPlot.java deleted file mode 100644 index c630e5a..0000000 --- a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/WaterfallPlot.java +++ /dev/null @@ -1,312 +0,0 @@ -package jspectrumanalyzer.core; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.Toolkit; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseMotionAdapter; -import java.awt.event.MouseMotionListener; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; -import java.util.Arrays; - -import javax.swing.JPanel; - -import org.jfree.chart.ChartPanel; - -public class WaterfallPlot extends JPanel -{ - /** - * - */ - private static final long serialVersionUID = 3249110968962287324L; - private BufferedImage bufferedImages[] = new BufferedImage[2]; - private int chartXOffset = 0, chartWidth = 100; - private boolean displayMarker = false; - private double displayMarkerFrequency = 0; - private int displayMarkerX = 0; - private int drawIndex = 0; - /** - * stores max value in pixel - */ - private float drawMaxBuffer[]; - private EMA fps = new EMA(3); - - private int fpsRenderedFrames = 0; - - private long lastFPSRecalculated = 0; - - private DatasetSpectrum lastSpectrum = null; - private ColorPalette palette = new HotIronBluePalette(); - - private Rectangle2D.Float rect = new Rectangle2D.Float(0f, 0f, 1f, 1f); - - private String renderingInfo = ""; - private int screenWidth; - private double spectrumPaletteSize = 65; - private double spectrumPaletteStart = -90; - private String statusMessage = ""; - - public WaterfallPlot(ChartPanel chartPanel, int maxHeight) - { - setPreferredSize(new Dimension(100, 200)); - setMinimumSize(new Dimension(100, 200)); - - addComponentListener(new ComponentAdapter() - { - @Override public void componentResized(ComponentEvent e) - { - setHistorySize(getHeight()); - } - }); - - screenWidth = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth(); - drawMaxBuffer = new float[screenWidth]; - bufferedImages[0] = new BufferedImage(screenWidth, maxHeight, BufferedImage.TYPE_3BYTE_BGR); - bufferedImages[1] = new BufferedImage(screenWidth, maxHeight, BufferedImage.TYPE_3BYTE_BGR); - - /** - * setup frequency marker - */ - addMouseMotionListener(new MouseMotionAdapter() { - @Override - public void mouseMoved(MouseEvent e) { - displayMarker = false; - int x = e.getX(); - if (x < chartXOffset || x > chartXOffset+chartWidth){ - return; - } - double freq = translateChartXToFrequency(x-chartXOffset); - if (freq != -1){ - displayMarker = true; - displayMarkerFrequency = freq; - displayMarkerX = x; - } - WaterfallPlot.this.repaint(); - } - }); - addMouseListener(new MouseAdapter() { - @Override - public void mouseExited(MouseEvent e) { - displayMarker = false; - } - }); - } - /** - * Adds new data to the waterfall plot and renders it - * @param spectrum - */ - public synchronized void addNewData(DatasetSpectrum spectrum) - { - int size = spectrum.spectrumLength(); - double startFreq = spectrum.getFreqStartMHz() * 1000000d; - double freqRange = (spectrum.getFreqStopMHz() - spectrum.getFreqStartMHz()) * 1000000d; - double width = bufferedImages[0].getWidth(); - double spectrumPalleteMax = spectrumPaletteStart + spectrumPaletteSize; - - this.lastSpectrum = spectrum; - - /** - * shift image by one pixel down - */ - BufferedImage previousImage = bufferedImages[drawIndex]; - drawIndex = (drawIndex + 1) % 2; - Graphics2D g = bufferedImages[drawIndex].createGraphics(); - g.drawImage(previousImage, 0, 1, null); - g.setColor(Color.black); - g.fillRect(0, 0, (int) width, 1); - - float binWidth = (float) (spectrum.getFFTBinSizeHz() / freqRange * width); - rect.x = 0; - rect.y = 0; - rect.height = 0; - rect.width = binWidth; - - float minimumValueDrawBuffer = -150; - Arrays.fill(drawMaxBuffer, minimumValueDrawBuffer); - - /** - * draw in two passes - first determines maximum power for the pixel, second draws it - */ - for (int i = 0; i < size; i++) - { - double freq = spectrum.getFrequency(i); - double power = spectrum.getPower(i); - double percentageFreq = (freq - startFreq) / freqRange; - double percentagePower = power < spectrumPaletteStart ? 0 : power > spectrumPalleteMax ? 1 : (power - spectrumPaletteStart) / spectrumPaletteSize; - int pixelX = (int) Math.round(width * percentageFreq); - pixelX = pixelX >= drawMaxBuffer.length ? drawMaxBuffer.length-1 : pixelX < 0 ? 0 : pixelX; - if (percentagePower > drawMaxBuffer[pixelX]) - drawMaxBuffer[pixelX] = (float) percentagePower; - } - - /** - * fill in pixels that do not have power with last bin's color - */ - Color lastValidColor = palette.getColor(0); - for (int x = 0; x < drawMaxBuffer.length; x++) - { - Color color; - if (drawMaxBuffer[x] == minimumValueDrawBuffer) - color = lastValidColor; - else - { - color = palette.getColorNormalized(drawMaxBuffer[x]); - lastValidColor = color; - } - rect.x = x; - g.setColor(color); - g.draw(rect); - } - - renderingInfo = String.format("RBW %.1fkHz / FFT bins: %d%s / %.1ffps", lastSpectrum == null ? 0 : lastSpectrum.getFFTBinSizeHz()/1000d, size >= 10000 ? size / 1000 : size, size >= 10000 ? "k" : "", fps.getEma()); - fpsRenderedFrames++; - if (System.currentTimeMillis() - lastFPSRecalculated > 1000) - { - double rawfps = fpsRenderedFrames / ((System.currentTimeMillis() - (double) lastFPSRecalculated) / 1000d); - fps.addNewValue(rawfps); - lastFPSRecalculated = System.currentTimeMillis(); - fpsRenderedFrames = 0; - } - g.dispose(); - repaint(); - } - - private void copyImage(BufferedImage src, BufferedImage dst) - { - Graphics2D g = dst.createGraphics(); - g.drawImage(src, 0, 0, null); - g.dispose(); - } - - /** - * Draws color palette into given area from bottom (0%) to top (100%) - * @param g - * @param x - * @param y - * @param w - * @param h - */ - public void drawScale(Graphics2D g, int x, int y, int w, int h) - { - g = (Graphics2D) g.create(x, y, w, h); - int step = 3; - for (int i = 0; i < h; i += step) - { - Color c = palette.getColorNormalized(1 - (double) i / h); - g.setColor(c); - g.fillRect(0, i, w, step); - } - - /** - * draw border around the scale - */ - int thickness = 2; - g.setColor(Color.darkGray); - g.fillRect(0, 0, w, thickness); - g.fillRect(w-thickness, 0, thickness, h); - g.fillRect(0, h-thickness, w, thickness); - g.dispose(); - } - - public int getHistorySize() - { - return bufferedImages[0].getHeight(); - } - - public double getSpectrumPaletteSize() - { - return spectrumPaletteSize; - } - - public double getSpectrumPaletteStart() - { - return spectrumPaletteStart; - } - - @Override protected void paintComponent(Graphics arg0) - { - Graphics2D g = (Graphics2D) arg0; - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - int w = chartWidth; - int h = getHeight(); - g.setColor(Color.black); - g.fillRect(0, 0, getWidth(), getHeight()); - - g.drawImage(bufferedImages[drawIndex], chartXOffset, 0, w, h, null); - - if (displayMarker){ - g.setColor(Color.gray); - g.drawLine(displayMarkerX, 0, displayMarkerX, h); - g.drawString(String.format("%.1fMHz", displayMarkerFrequency/1000000.0), displayMarkerX+5, h/2); - }//finish marker - - g.setColor(Color.white); - int x = chartXOffset + w - 250; - int y = h - 20; - g.drawString(renderingInfo, x, y-20); - g.drawString(statusMessage, x, y); - } - - public void setStatusMessage(String message) - { - this.statusMessage = message; - } - - public void setDrawingOffsets(int xOffsetLeft, int width) - { - this.chartXOffset = xOffsetLeft; - this.chartWidth = width; - } - - public synchronized void setHistorySize(int historyInPixels) - { - BufferedImage bufferedImages[] = new BufferedImage[2]; - bufferedImages[0] = new BufferedImage(screenWidth, historyInPixels, BufferedImage.TYPE_3BYTE_BGR); - bufferedImages[1] = new BufferedImage(screenWidth, historyInPixels, BufferedImage.TYPE_3BYTE_BGR); - copyImage(this.bufferedImages[0], bufferedImages[0]); - copyImage(this.bufferedImages[1], bufferedImages[1]); - this.bufferedImages = bufferedImages; - } - - public void setSpectrumPaletteSize(int dB) - { - this.spectrumPaletteSize = dB; - } - - /** - * Sets start and end of the color scale - * @param minFreqency - * @param maxFrequency - */ - public void setSpectrumPaletteStart(int dB) - { - this.spectrumPaletteStart = dB; - } - - private double translateChartXToFrequency(int x){ - if (lastSpectrum != null){ - double startFreq = lastSpectrum.getFreqStartMHz() * 1000000d; - double stopFreq = lastSpectrum.getFreqStopMHz() * 1000000d; - double freqRange = (stopFreq - startFreq); - double width = bufferedImages[0].getWidth(); - double percentageFreq = x/(double)chartWidth; - double freq = percentageFreq*freqRange + startFreq; - if (freq > stopFreq) - freq = stopFreq; - if (freq < startFreq) - freq = startFreq; - return freq; - } - return -1; - } -} diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/jfc/XYSeriesCollectionImmutable.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/jfc/XYSeriesCollectionImmutable.java new file mode 100644 index 0000000..b351f23 --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/jfc/XYSeriesCollectionImmutable.java @@ -0,0 +1,20 @@ +package jspectrumanalyzer.core.jfc; + +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * {@link XYSeriesCollection} for use with {@link XYSeriesImmutable} + */ +public class XYSeriesCollectionImmutable extends XYSeriesCollection { + + @Override + public double getXValue(int series, int item) { + return ((XYSeriesImmutable)getSeries(series)).getXX(item); + } + + @Override + public double getYValue(int series, int item) { + return ((XYSeriesImmutable)getSeries(series)).getYY(item); + } +} diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/jfc/XYSeriesImmutable.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/jfc/XYSeriesImmutable.java new file mode 100644 index 0000000..43f75d4 --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/core/jfc/XYSeriesImmutable.java @@ -0,0 +1,41 @@ +package jspectrumanalyzer.core.jfc; + +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYDataItem; +import org.jfree.data.xy.XYSeries; + +/** + * Optimized immutable {@link XYSeries} for use with {@link XYLineAndShapeRenderer}. + * No allocation of {@link XYDataItem} or any other objects. + */ +public class XYSeriesImmutable extends XYSeries { + private float[] xValues; + private float[] yValues; + + public XYSeriesImmutable(Comparable key, float[] xValues, float[] yValues) { + super(key, false, false); + if (xValues.length != yValues.length) + throw new IllegalArgumentException("x/y values are not of the same size"); + this.xValues = xValues.clone(); + this.yValues = yValues.clone(); + } + + public double getXX(int item) { + return xValues[item]; + } + + public double getYY(int item) { + return yValues[item]; + } + + @Override + public int getItemCount() { + return xValues.length; + } + + @Override + public XYDataItem getDataItem(int index) { + return null; + } + +} diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/ColorPalette.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/ColorPalette.java similarity index 86% rename from src/hackrf-sweep/src-java/jspectrumanalyzer/core/ColorPalette.java rename to src/hackrf-sweep/src-java/jspectrumanalyzer/ui/ColorPalette.java index 2dd3c24..aa54344 100644 --- a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/ColorPalette.java +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/ColorPalette.java @@ -1,4 +1,4 @@ -package jspectrumanalyzer.core; +package jspectrumanalyzer.ui; import java.awt.Color; diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencySelectorPanel.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/FrequencySelectorPanel.java similarity index 88% rename from src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencySelectorPanel.java rename to src/hackrf-sweep/src-java/jspectrumanalyzer/ui/FrequencySelectorPanel.java index f58e1be..25b2183 100644 --- a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/FrequencySelectorPanel.java +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/FrequencySelectorPanel.java @@ -1,10 +1,9 @@ -package jspectrumanalyzer.core; +package jspectrumanalyzer.ui; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.GridLayout; -import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyVetoException; import java.lang.reflect.InvocationTargetException; @@ -152,18 +151,14 @@ public boolean setValue(int newValue) { try { - SwingUtilities.invokeAndWait(new Runnable() - { - @Override public void run() + SwingUtilities.invokeAndWait(() -> { + try { - try - { - fireValueChange(oldValue, newValue); - } - catch (PropertyVetoException e) - { - throw new RuntimeException(e); - } + fireValueChange(oldValue, newValue); + } + catch (PropertyVetoException e) + { + throw new RuntimeException(e); } }); } @@ -203,15 +198,11 @@ private void add(int digit) private ActionListener addListener(boolean add, int digit) { - ActionListener listener = new ActionListener() - { - @Override public void actionPerformed(ActionEvent e) - { - if (add) - add(digit); - else - subtract(digit); - } + ActionListener listener = e -> { + if (add) + add(digit); + else + subtract(digit); }; return listener; } diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/FrequencySelectorRangeBinder.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/FrequencySelectorRangeBinder.java new file mode 100644 index 0000000..adfaae5 --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/FrequencySelectorRangeBinder.java @@ -0,0 +1,50 @@ +package jspectrumanalyzer.ui; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; + +import jspectrumanalyzer.core.FrequencyRange; +import jspectrumanalyzer.core.HackRFSettings; + +/** + * Limits frequency selection of two selectors (start/end) + */ +public class FrequencySelectorRangeBinder +{ + public FrequencySelectorPanel selFreqStart, selFreqEnd; + public FrequencySelectorRangeBinder(FrequencySelectorPanel selFreqStart, FrequencySelectorPanel selFreqEnd) + { + this.selFreqEnd = selFreqEnd; + this.selFreqStart = selFreqStart; + VetoableChangeListener freqStartVetoable = evt -> { + Integer newVal = (Integer) evt.getNewValue(); + if (newVal >= selFreqEnd.getValue()) + { + //try to increase freq end by the same value + if (!selFreqEnd.setValue(selFreqEnd.getValue() + (newVal - (Integer) evt.getOldValue()))) + throw new PropertyVetoException(">", evt); + } + }; + VetoableChangeListener freqEndVetoable = evt -> { + Integer newVal = (Integer) evt.getNewValue(); + if (newVal <= selFreqStart.getValue()) + { + if (!selFreqStart.setValue(selFreqStart.getValue() - ((Integer) evt.getOldValue() - newVal))) + throw new PropertyVetoException(">", evt); + } + }; + + selFreqEnd.addVetoableChangeListener(freqEndVetoable); + selFreqStart.addVetoableChangeListener(freqStartVetoable); + } + + public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener) { + selFreqStart.addPropertyChangeListener("value", propertyChangeListener); + selFreqEnd.addPropertyChangeListener("value", propertyChangeListener); + } + + public FrequencyRange getFrequencyRange() { + return new FrequencyRange(selFreqStart.getValue(), selFreqEnd.getValue()); + } +} diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/GraphicsToolkit.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/GraphicsToolkit.java new file mode 100644 index 0000000..42d7387 --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/GraphicsToolkit.java @@ -0,0 +1,26 @@ +package jspectrumanalyzer.ui; + +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsEnvironment; +import java.awt.Transparency; +import java.awt.image.BufferedImage; + +public class GraphicsToolkit { + + public static BufferedImage createAcceleratedImageTransparent(int width, int height) { + return createAcceleratedImage(width, height, Transparency.TRANSLUCENT); + } + + public static BufferedImage createAcceleratedImageOpaque(int width, int height) { + return createAcceleratedImage(width, height, Transparency.OPAQUE); + } + + private static BufferedImage createAcceleratedImage(int width, int height, int transparency) { + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration(); + BufferedImage image = gc.createCompatibleImage(width, height, transparency); + image.setAccelerationPriority(1); + return image; + } + +} diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/HackRFSweepSettingsUI.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/HackRFSweepSettingsUI.java new file mode 100644 index 0000000..c84360c --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/HackRFSweepSettingsUI.java @@ -0,0 +1,455 @@ +package jspectrumanalyzer.ui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Desktop; +import java.awt.Font; +import java.awt.GridLayout; +import java.awt.Label; +import java.beans.PropertyChangeEvent; +import java.math.BigDecimal; +import java.net.URI; +import java.util.Optional; +import java.util.Vector; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import javax.swing.DefaultBoundedRangeModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.JSpinner; +import javax.swing.JTabbedPane; +import javax.swing.JSpinner.ListEditor; +import javax.swing.JTextField; +import javax.swing.SpinnerListModel; +import javax.swing.UIManager; + +import jspectrumanalyzer.HackRFSweepSpectrumAnalyzer; +import jspectrumanalyzer.Version; +import jspectrumanalyzer.core.FrequencyAllocationTable; +import jspectrumanalyzer.core.FrequencyAllocations; +import jspectrumanalyzer.core.FrequencyRange; +import jspectrumanalyzer.core.HackRFSettings; +import jspectrumanalyzer.core.HackRFSettings.HackRFEventAdapter; +import net.miginfocom.swing.MigLayout; +import shared.mvc.MVCController; +import javax.swing.border.BevelBorder; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingUtilities; +import javax.swing.JComboBox; +import javax.swing.SwingConstants; + +public class HackRFSweepSettingsUI extends JPanel +{ + /** + * + */ + private HackRFSettings hRF; + private static final long serialVersionUID = 7721079457485020637L; + private JTextField txtHackrfConnected; + private FrequencySelectorPanel frequencySelectorStart; + private FrequencySelectorPanel frequencySelectorEnd; + private JSpinner spinnerFFTBinHz; + private JSlider sliderGain; + private JSpinner spinner_numberOfSamples; + private JCheckBox chckbxAntennaPower; + private JSlider slider_waterfallPaletteStart; + private JSlider slider_waterfallPaletteSize; + private JCheckBox chckbxShowPeaks; + private JCheckBox chckbxRemoveSpurs; + private JButton btnPause; + private SpinnerListModel spinnerModelFFTBinHz; + private FrequencySelectorRangeBinder frequencyRangeSelector; + private JCheckBox chckbxFilterSpectrum; + private JSpinner spinnerPeakFallSpeed; + private JComboBox comboBoxFrequencyAllocationBands; + private JSlider sliderGainVGA; + private JSlider sliderGainLNA; + private JLabel lblPeakFall; + private JComboBox comboBoxLineThickness; + private JLabel lblPersistentDisplay; + private JCheckBox checkBoxPersistentDisplay; + private JCheckBox checkBoxWaterfallEnabled; + private JLabel lblDecayRate; + private JComboBox comboBoxDecayRate; + private JLabel lblDebugDisplay; + private JCheckBox checkBoxDebugDisplay; + + /** + * Create the panel. + */ + public HackRFSweepSettingsUI(HackRFSettings hackRFSettings) + { + this.hRF = hackRFSettings; + setForeground(Color.WHITE); + setBackground(Color.BLACK); + int minFreq = 1; + int maxFreq = 7250; + int freqStep = 1; + + JPanel panelMainSettings = new JPanel(new MigLayout("", "[123.00px,grow,leading]", "[][][::0px][][]")); + panelMainSettings.setBorder(new EmptyBorder(UIManager.getInsets("TabbedPane.tabAreaInsets")));; + panelMainSettings.setBackground(Color.BLACK); + JLabel lblNewLabel = new JLabel("Frequency start [MHz]"); + lblNewLabel.setForeground(Color.WHITE); + panelMainSettings.add(lblNewLabel, "cell 0 0,growx,aligny center"); + + frequencySelectorStart = new FrequencySelectorPanel(minFreq, maxFreq, freqStep, minFreq); + panelMainSettings.add(frequencySelectorStart, "cell 0 1,grow"); + + JLabel lblFrequencyEndmhz = new JLabel("Frequency end [MHz]"); + lblFrequencyEndmhz.setForeground(Color.WHITE); + panelMainSettings.add(lblFrequencyEndmhz, "cell 0 3,alignx left,aligny center"); + + frequencySelectorEnd = new FrequencySelectorPanel(minFreq, maxFreq, freqStep, maxFreq); + panelMainSettings.add(frequencySelectorEnd, "cell 0 4,grow"); + + + txtHackrfConnected = new JTextField(); + txtHackrfConnected.setText("HackRF disconnected"); + txtHackrfConnected.setForeground(Color.WHITE); + txtHackrfConnected.setBackground(Color.BLACK); + panelMainSettings.add(txtHackrfConnected, "cell 0 23,growx"); + txtHackrfConnected.setColumns(10); + txtHackrfConnected.setBorder(null); + + btnPause = new JButton("Pause"); + panelMainSettings.add(btnPause, "cell 0 25,growx"); + btnPause.setBackground(Color.black); + + + + + JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP); + setLayout(new BorderLayout()); + add(panelMainSettings, BorderLayout.NORTH); + add(tabbedPane, BorderLayout.CENTER); + tabbedPane.setForeground(Color.WHITE); + tabbedPane.setBackground(Color.BLACK); + + JPanel tab1 = new JPanel(new MigLayout("", "[123.00px,grow,leading]", "[][][0][][][0][][][0][][][0][][][0][][0][][grow,fill]")); + tab1.setForeground(Color.WHITE); + tab1.setBackground(Color.BLACK); + + JPanel tab2 = new JPanel(new MigLayout("", "[123.00px,grow,leading]", "[][0][][][0][][][0][][0][][][0][][0][][][0][0][][][0][][0][grow,fill]")); + tab2.setForeground(Color.WHITE); + tab2.setBackground(Color.BLACK); + + tabbedPane.addTab("HackRF Settings", tab1); + tabbedPane.addTab("Chart options", tab2); + tabbedPane.setForegroundAt(1, Color.BLACK); + tabbedPane.setBackgroundAt(1, Color.WHITE); + + tabbedPane.setForegroundAt(0, Color.BLACK); + tabbedPane.setBackgroundAt(0, Color.WHITE); + + JLabel lblNewLabel_2 = new JLabel("LNA Gain [dB]"); + lblNewLabel_2.setForeground(Color.WHITE); + tab1.add(lblNewLabel_2, "cell 0 3"); + + sliderGainLNA = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 2); + sliderGainLNA.setForeground(Color.WHITE); + sliderGainLNA.setFont(new Font("Monospaced", Font.BOLD, 16)); + sliderGainLNA.setBackground(Color.BLACK); + tab1.add(sliderGainLNA, "cell 0 4,growx"); + + JLabel lblVgfaGaindb = new JLabel("VGA Gain [dB]"); + lblVgfaGaindb.setForeground(Color.WHITE); + tab1.add(lblVgfaGaindb, "cell 0 6"); + + sliderGainVGA = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 2); + sliderGainVGA.setForeground(Color.WHITE); + sliderGainVGA.setFont(new Font("Monospaced", Font.BOLD, 16)); + sliderGainVGA.setBackground(Color.BLACK); + tab1.add(sliderGainVGA, "cell 0 7,growx"); + + + + JLabel lblFftBinhz = new JLabel("FFT Bin [Hz]"); + lblFftBinhz.setForeground(Color.WHITE); + tab1.add(lblFftBinhz, "cell 0 9"); + + spinnerFFTBinHz = new JSpinner(); + spinnerFFTBinHz.setFont(new Font("Monospaced", Font.BOLD, 16)); + spinnerModelFFTBinHz = new SpinnerListModel(new String[] { "1 000", "2 000", "5 000", "10 000", "20 000", + "50 000", "100 000", "200 000", "500 000", "1 000 000", "2 000 000", "5 000 000" }); + spinnerFFTBinHz.setModel(spinnerModelFFTBinHz); + tab1.add(spinnerFFTBinHz, "cell 0 10,growx"); + ((ListEditor) spinnerFFTBinHz.getEditor()).getTextField().setHorizontalAlignment(JTextField.RIGHT); + + + JLabel lblGain = new JLabel("Gain [dB]"); + lblGain.setForeground(Color.WHITE); + tab1.add(lblGain, "cell 0 0"); + + sliderGain = new JSlider(JSlider.HORIZONTAL, 0, 100, 2); + sliderGain.setFont(new Font("Monospaced", Font.BOLD, 16)); + sliderGain.setBackground(Color.BLACK); + sliderGain.setForeground(Color.WHITE); + tab1.add(sliderGain, "flowy,cell 0 1,growx"); + + JLabel lbl_gainValue = new JLabel(hackRFSettings.getGain() + "dB"); + lbl_gainValue.setForeground(Color.WHITE); + tab1.add(lbl_gainValue, "cell 0 1,alignx right"); + + hackRFSettings.getGain().addListener((gain) -> lbl_gainValue.setText(String.format(" %ddB [LNA: %ddB VGA: %ddB]", + gain, hackRFSettings.getGainLNA().getValue(), hackRFSettings.getGainVGA().getValue()))); + + + JLabel lblNumberOfSamples = new JLabel("Number of samples"); + lblNumberOfSamples.setForeground(Color.WHITE); + tab1.add(lblNumberOfSamples, "cell 0 12"); + + spinner_numberOfSamples = new JSpinner(); + spinner_numberOfSamples.setModel(new SpinnerListModel(new String[] { "8192", "16384", "32768", "65536", "131072", "262144" })); + spinner_numberOfSamples.setFont(new Font("Monospaced", Font.BOLD, 16)); + ((ListEditor) spinner_numberOfSamples.getEditor()).getTextField().setHorizontalAlignment(JTextField.RIGHT); + ((ListEditor) spinner_numberOfSamples.getEditor()).getTextField().setEditable(false); + tab1.add(spinner_numberOfSamples, "cell 0 13,growx"); + + JButton btnAbout = new JButton("Visit homepage"); + btnAbout.addActionListener(e -> { + if (Desktop.isDesktopSupported()) { + Desktop desktop = Desktop.getDesktop(); + try { + URI uri = new URI(Version.url); + desktop.browse(uri); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }); + + Label labelVersion = new Label("Version: v"+Version.version); + tab1.add(labelVersion, "flowx,cell 0 17"); + btnAbout.setBackground(Color.BLACK); + tab1.add(btnAbout, "cell 0 17,alignx right"); + + JLabel lblAntennaPower = new JLabel("Antenna power"); + lblAntennaPower.setForeground(Color.WHITE); + tab1.add(lblAntennaPower, "flowx,cell 0 15,growx"); + + chckbxAntennaPower = new JCheckBox(""); + chckbxAntennaPower.setHorizontalTextPosition(SwingConstants.LEADING); + chckbxAntennaPower.setBackground(Color.BLACK); + chckbxAntennaPower.setForeground(Color.WHITE); + tab1.add(chckbxAntennaPower, "cell 0 15,alignx right"); + + chckbxFilterSpectrum = new JCheckBox("Filter spectrum"); + chckbxFilterSpectrum.setBackground(Color.BLACK); + chckbxFilterSpectrum.setForeground(Color.WHITE); + + JLabel lblWaterfallEnabled = new JLabel("Waterfall enabled"); + lblWaterfallEnabled.setForeground(Color.WHITE); + tab2.add(lblWaterfallEnabled, "flowx,cell 0 0,growx"); + + + + + JLabel lblWaterfallPaletteStart = new JLabel("Waterfall palette start [dB]"); + lblWaterfallPaletteStart.setForeground(Color.WHITE); + tab2.add(lblWaterfallPaletteStart, "cell 0 2"); + + slider_waterfallPaletteStart = new JSlider(); + slider_waterfallPaletteStart.setForeground(Color.WHITE); + slider_waterfallPaletteStart.setBackground(Color.BLACK); + slider_waterfallPaletteStart.setMinimum(-100); + slider_waterfallPaletteStart.setMaximum(0); + slider_waterfallPaletteStart.setValue(-30); + tab2.add(slider_waterfallPaletteStart, "cell 0 3,growx"); + + + JLabel lblWaterfallPaletteLength = new JLabel("Waterfall palette length [dB]"); + lblWaterfallPaletteLength.setForeground(Color.WHITE); + tab2.add(lblWaterfallPaletteLength, "cell 0 5"); + + slider_waterfallPaletteSize = new JSlider(HackRFSweepSpectrumAnalyzer.SPECTRUM_PALETTE_SIZE_MIN, 100); + slider_waterfallPaletteSize.setBackground(Color.BLACK); + slider_waterfallPaletteSize.setForeground(Color.WHITE); + tab2.add(slider_waterfallPaletteSize, "cell 0 6,growx"); + + JLabel lblSpectrLineThickness = new JLabel("Spectr. Line Thickness"); + lblSpectrLineThickness.setForeground(Color.WHITE); + tab2.add(lblSpectrLineThickness, "flowx,cell 0 8,growx"); + + JLabel lblShowPeaks = new JLabel("Show peaks"); + lblShowPeaks.setForeground(Color.WHITE); + tab2.add(lblShowPeaks, "flowx,cell 0 10,growx"); + + + chckbxShowPeaks = new JCheckBox(""); + chckbxShowPeaks.setForeground(Color.WHITE); + chckbxShowPeaks.setBackground(Color.BLACK); + tab2.add(chckbxShowPeaks, "cell 0 10,alignx right"); + + JLabel lblSpurFiltermay = new JLabel("Spur filter (may distort real signals)"); + lblSpurFiltermay.setForeground(Color.WHITE); + tab2.add(lblSpurFiltermay, "flowx,cell 0 13,growx"); + + chckbxRemoveSpurs = new JCheckBox(""); + chckbxRemoveSpurs.setForeground(Color.WHITE); + chckbxRemoveSpurs.setBackground(Color.BLACK); + tab2.add(chckbxRemoveSpurs, "cell 0 13,alignx right"); + + lblPeakFall = new JLabel(" Fall speed [s]"); + lblPeakFall.setForeground(Color.WHITE); + tab2.add(lblPeakFall, "flowx,cell 0 11,growx"); + + spinnerPeakFallSpeed = new JSpinner(); + spinnerPeakFallSpeed.setModel(new SpinnerNumberModel(10, 0, 500, 1)); + tab2.add(spinnerPeakFallSpeed, "cell 0 11,alignx right"); + + lblPersistentDisplay = new JLabel("Persistent Display"); + lblPersistentDisplay.setForeground(Color.WHITE); + tab2.add(lblPersistentDisplay, "flowx,cell 0 15,growx"); + + lblDecayRate = new JLabel(" Persistence time [s]"); + lblDecayRate.setForeground(Color.WHITE); + tab2.add(lblDecayRate, "flowx,cell 0 16,growx"); + + JLabel lblDisplayFrequencyAllocation = new JLabel("Frequency Allocation Bands"); + lblDisplayFrequencyAllocation.setForeground(Color.WHITE); + tab2.add(lblDisplayFrequencyAllocation, "cell 0 19"); + + + FrequencyAllocations frequencyAllocations = new FrequencyAllocations(); + Vector freqAllocValues = new Vector<>(); + freqAllocValues.add(null); + freqAllocValues.addAll(frequencyAllocations.getTable().values()); + DefaultComboBoxModel freqAllocModel = new DefaultComboBoxModel<>(freqAllocValues); + comboBoxFrequencyAllocationBands = new JComboBox(freqAllocModel); + tab2.add(comboBoxFrequencyAllocationBands, "cell 0 20,growx"); + + comboBoxLineThickness = new JComboBox(new BigDecimal[] { + new BigDecimal("1"), new BigDecimal("1.5"), new BigDecimal("2"), new BigDecimal("3") + }); + tab2.add(comboBoxLineThickness, "cell 0 8,alignx right"); + + checkBoxPersistentDisplay = new JCheckBox(""); + checkBoxPersistentDisplay.setForeground(Color.WHITE); + checkBoxPersistentDisplay.setBackground(Color.BLACK); + tab2.add(checkBoxPersistentDisplay, "cell 0 15,alignx right"); + + checkBoxWaterfallEnabled = new JCheckBox(""); + checkBoxWaterfallEnabled.setForeground(Color.WHITE); + checkBoxWaterfallEnabled.setBackground(Color.BLACK); + tab2.add(checkBoxWaterfallEnabled, "cell 0 0,alignx right"); + + comboBoxDecayRate = new JComboBox( + new Vector<>(IntStream.rangeClosed(hRF.getPersistentDisplayDecayRate().getMin(), hRF.getPersistentDisplayDecayRate().getMax()). + boxed().collect(Collectors.toList()))); + tab2.add(comboBoxDecayRate, "cell 0 16,alignx right"); + + lblDebugDisplay = new JLabel("Debug display"); + lblDebugDisplay.setForeground(Color.WHITE); + tab2.add(lblDebugDisplay, "flowx,cell 0 22,growx"); + + checkBoxDebugDisplay = new JCheckBox(""); + checkBoxDebugDisplay.setForeground(Color.WHITE); + checkBoxDebugDisplay.setBackground(Color.BLACK); + tab2.add(checkBoxDebugDisplay, "cell 0 22,alignx right"); + + + bindViewToModel(); + } + + private void bindViewToModel() { + frequencyRangeSelector = new FrequencySelectorRangeBinder(frequencySelectorStart, frequencySelectorEnd); + + new MVCController(spinnerFFTBinHz, hRF.getFFTBinHz(), + viewValue -> Integer.parseInt(viewValue.toString().replaceAll("\\s", "")), + modelValue -> { + Optional val = spinnerModelFFTBinHz.getList().stream().filter(value -> modelValue <= Integer.parseInt(value.toString().replaceAll("\\s", ""))).findFirst(); + if (val.isPresent()) + return val.get(); + else + return spinnerModelFFTBinHz.getList().get(0); + }); + new MVCController(sliderGain, hRF.getGain()); + new MVCController(spinner_numberOfSamples, hRF.getSamples(), val -> Integer.parseInt(val.toString()), val -> val.toString()); + new MVCController(chckbxAntennaPower, hRF.getAntennaPowerEnable()); + new MVCController(slider_waterfallPaletteStart, hRF.getSpectrumPaletteStart()); + new MVCController(slider_waterfallPaletteSize, hRF.getSpectrumPaletteSize()); + new MVCController( (Consumer valueChangedCall) -> + frequencyRangeSelector.addPropertyChangeListener((PropertyChangeEvent evt) -> valueChangedCall.accept(frequencyRangeSelector.getFrequencyRange()) ) , + (FrequencyRange newComponentValue) -> { + if(frequencyRangeSelector.selFreqStart.getValue() != newComponentValue.getStartMHz()) + frequencyRangeSelector.selFreqStart.setValue(newComponentValue.getStartMHz()); + if(frequencyRangeSelector.selFreqEnd.getValue() != newComponentValue.getEndMHz()) + frequencyRangeSelector.selFreqEnd.setValue(newComponentValue.getEndMHz()); + }, + hRF.getFrequency() + ); + new MVCController(chckbxShowPeaks, hRF.isChartsPeaksVisible()); + new MVCController(chckbxFilterSpectrum, hRF.isFilterSpectrum()); + new MVCController(chckbxRemoveSpurs, hRF.isSpurRemoval()); + + new MVCController((valueChangedCall) -> btnPause.addActionListener((event) -> valueChangedCall.accept(!hRF.isCapturingPaused().getValue())), + isCapt -> btnPause.setText(!isCapt ? "Pause" : "Resume"), + hRF.isCapturingPaused()); + + new MVCController(spinnerPeakFallSpeed, hRF.getPeakFallRate(), in -> (Integer)in, in -> in); + + new MVCController(comboBoxFrequencyAllocationBands, hRF.getFrequencyAllocationTable()); + + sliderGainLNA.setModel(new DefaultBoundedRangeModel(hRF.getGainLNA().getValue(), 0, hRF.getGainLNA().getMin(), hRF.getGainLNA().getMax())); + sliderGainVGA.setModel(new DefaultBoundedRangeModel(hRF.getGainVGA().getValue(), 0, hRF.getGainVGA().getMin(), hRF.getGainVGA().getMax())); + + sliderGainLNA.setSnapToTicks(true); + sliderGainLNA.setMinorTickSpacing(hRF.getGainLNA().getStep()); + + sliderGainVGA.setSnapToTicks(true); + sliderGainVGA.setMinorTickSpacing(hRF.getGainVGA().getStep()); + + new MVCController(sliderGainLNA, hRF.getGainLNA()); + new MVCController(sliderGainVGA, hRF.getGainVGA()); + + new MVCController(comboBoxLineThickness, hRF.getSpectrumLineThickness()); + + new MVCController(checkBoxPersistentDisplay, hRF.isPersistentDisplayVisible()); + + new MVCController(checkBoxWaterfallEnabled, hRF.isWaterfallVisible()); + + new MVCController(checkBoxDebugDisplay, hRF.isDebugDisplay()); + + hRF.isChartsPeaksVisible().addListener((enabled) -> { + SwingUtilities.invokeLater(()->{ + spinnerPeakFallSpeed.setEnabled(enabled); + spinnerPeakFallSpeed.setVisible(enabled); + lblPeakFall.setVisible(enabled); + }); + }); + hRF.isChartsPeaksVisible().callObservers(); + + new MVCController(comboBoxDecayRate, hRF.getPersistentDisplayDecayRate()); + hRF.isPersistentDisplayVisible().addListener((visible) -> { + SwingUtilities.invokeLater(()->{ + comboBoxDecayRate.setVisible(visible); + lblDecayRate.setVisible(visible); + }); + }); + hRF.isPersistentDisplayVisible().callObservers(); + + hRF.registerListener(new HackRFSettings.HackRFEventAdapter() + { + @Override public void captureStateChanged(boolean isCapturing) + { +// btnPause.setText(isCapturing ? "Pause" : "Resume"); + } + @Override public void hardwareStatusChanged(boolean hardwareSendingData) + { + txtHackrfConnected.setText("HackRF "+(hardwareSendingData ? "connected":"disconnected")); + } + });; + + } + +} diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/HotIronBluePalette.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/HotIronBluePalette.java similarity index 95% rename from src/hackrf-sweep/src-java/jspectrumanalyzer/core/HotIronBluePalette.java rename to src/hackrf-sweep/src-java/jspectrumanalyzer/ui/HotIronBluePalette.java index 3570fd2..5520398 100644 --- a/src/hackrf-sweep/src-java/jspectrumanalyzer/core/HotIronBluePalette.java +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/HotIronBluePalette.java @@ -1,4 +1,4 @@ -package jspectrumanalyzer.core; +package jspectrumanalyzer.ui; import java.awt.Color; @@ -62,12 +62,12 @@ public HotIronBluePalette() @Override public Color getColorNormalized(double value) { - int index = (int) (size() * value); + int index = (int) (colors.length * value); if (index < 0) index = 0; - if (index >= size()) - index = size() - 1; - return getColor(index); + if (index >= colors.length) + index = colors.length - 1; + return colors[index]; } @Override public int size() diff --git a/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/WaterfallPlot.java b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/WaterfallPlot.java new file mode 100644 index 0000000..0ce67ed --- /dev/null +++ b/src/hackrf-sweep/src-java/jspectrumanalyzer/ui/WaterfallPlot.java @@ -0,0 +1,366 @@ +package jspectrumanalyzer.ui; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Toolkit; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.util.Arrays; + +import javax.swing.JPanel; + +import org.jfree.chart.ChartPanel; + +import jspectrumanalyzer.core.DatasetSpectrum; +import jspectrumanalyzer.core.EMA; + +public class WaterfallPlot extends JPanel { + /** + * + */ + private static final long serialVersionUID = 3249110968962287324L; + private BufferedImage bufferedImages[] = new BufferedImage[2]; + private int chartXOffset = 0, chartWidth = 100; + private boolean displayMarker = false; + private double displayMarkerFrequency = 0; + private int displayMarkerX = 0; + private int drawIndex = 0; + /** + * stores max value in pixel + */ + private float drawMaxBuffer[]; + private EMA fps = new EMA(3); + private int fpsRenderedFrames = 0; + private long lastFPSRecalculated = 0; + private DatasetSpectrum lastSpectrum = null; + private ColorPalette palette = new HotIronBluePalette(); + private Rectangle2D.Float rect = new Rectangle2D.Float(0f, 0f, 1f, 1f); + private String renderingInfo = ""; + private int screenWidth; + private double spectrumPaletteSize = 65; + private double spectrumPaletteStart = -90; + private String[] statusMessage = new String[4]; + + public WaterfallPlot(ChartPanel chartPanel, int maxHeight) { + setPreferredSize(new Dimension(100, 200)); + setMinimumSize(new Dimension(100, 200)); + + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + setHistorySize(getHeight()); + } + }); + + screenWidth = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth(); + drawMaxBuffer = new float[screenWidth]; + + bufferedImages[0] = GraphicsToolkit.createAcceleratedImageOpaque(screenWidth, maxHeight); + bufferedImages[1] = GraphicsToolkit.createAcceleratedImageOpaque(screenWidth, maxHeight); + + /** + * setup frequency marker + */ + addMouseMotionListener(new MouseMotionAdapter() { + @Override + public void mouseMoved(MouseEvent e) { + displayMarker = false; + int x = e.getX(); + if (x < chartXOffset || x > chartXOffset + chartWidth) { + return; + } + double freq = translateChartXToFrequency(x - chartXOffset); + if (freq != -1) { + displayMarker = true; + displayMarkerFrequency = freq; + displayMarkerX = x; + } + WaterfallPlot.this.repaint(); + } + }); + addMouseListener(new MouseAdapter() { + @Override + public void mouseExited(MouseEvent e) { + displayMarker = false; + } + }); + } + + private EMA newDataTimeEMA = new EMA(100); + /** + * Adds new data to the waterfall plot and renders it + * + * @param spectrum + */ + public synchronized void addNewData(DatasetSpectrum spectrum) { + long start = System.nanoTime(); + + int size = spectrum.spectrumLength(); + double startFreq = spectrum.getFreqStartMHz() * 1000000d; + double freqRange = (spectrum.getFreqStopMHz() - spectrum.getFreqStartMHz()) * 1000000d; + double width = bufferedImages[0].getWidth(); + double spectrumPalleteMax = spectrumPaletteStart + spectrumPaletteSize; + + this.lastSpectrum = spectrum; + + /** + * shift image by one pixel down + */ + BufferedImage previousImage = bufferedImages[drawIndex]; + drawIndex = (drawIndex + 1) % 2; + Graphics2D g = bufferedImages[drawIndex].createGraphics(); + g.drawImage(previousImage, 0, 1, null); + g.setColor(Color.black); + g.fillRect(0, 0, (int) width, 1); + + float binWidth = (float) (spectrum.getFFTBinSizeHz() / freqRange * width); + rect.x = 0; + rect.y = 0; + rect.height = 0; + rect.width = binWidth; + + float minimumValueDrawBuffer = -150; + Arrays.fill(drawMaxBuffer, minimumValueDrawBuffer); + + /** + * draw in two passes - first determines maximum power for the pixel, + * second draws it + */ + if (true) { + //optimized drawing + double widthDivSize = (double)width / size; + double inverseSpectrumPaletteSize = 1d/spectrumPaletteSize; + double spectrumPaletteStartDivSpectrumPaletteSize = (double)spectrumPaletteStart/spectrumPaletteSize; + for (int i = 0; i < size; i++) { + double power = spectrum.getPower(i); + double percentagePower = 0; + if (power > spectrumPaletteStart) { + if ( power < spectrumPalleteMax) { +// percentagePower = (power - spectrumPaletteStart) / spectrumPaletteSize; + //percentagePower = power/spectrumPaletteSize - spectrumPaletteStart/spectrumPaletteSize; + percentagePower = power*inverseSpectrumPaletteSize - spectrumPaletteStartDivSpectrumPaletteSize; + } + else + percentagePower = 1; + } + int pixelX = (int) Math.round(widthDivSize * i); + pixelX = pixelX >= drawMaxBuffer.length ? drawMaxBuffer.length - 1 : pixelX < 0 ? 0 : pixelX; + if (percentagePower > drawMaxBuffer[pixelX]) + drawMaxBuffer[pixelX] = (float) percentagePower; + } + } else { + //unoptimized drawing + for (int i = 0; i < size; i++) { + double freq = spectrum.getFrequency(i); + double power = spectrum.getPower(i); + double percentageFreq = (freq - startFreq) / freqRange; + double percentagePower = power < spectrumPaletteStart ? 0 + : power > spectrumPalleteMax ? 1 : (power - spectrumPaletteStart) / spectrumPaletteSize; + int pixelX = (int) Math.round(width * percentageFreq); + pixelX = pixelX >= drawMaxBuffer.length ? drawMaxBuffer.length - 1 : pixelX < 0 ? 0 : pixelX; + if (percentagePower > drawMaxBuffer[pixelX]) + drawMaxBuffer[pixelX] = (float) percentagePower; + } + } + + /** + * fill in pixels that do not have power with last bin's color in order + * to smooth the spectrum + */ + Color lastValidColor = palette.getColor(0); + for (int x = 0; x < drawMaxBuffer.length; x++) { + Color color; + if (drawMaxBuffer[x] == minimumValueDrawBuffer) + color = lastValidColor; + else { + color = palette.getColorNormalized(drawMaxBuffer[x]); + lastValidColor = color; + } + rect.x = x; + g.setColor(color); + g.draw(rect); + } + + renderingInfo = String.format("RBW %.1fkHz / FFT bins: %d%s / %.1ffps", + lastSpectrum == null ? 0 : lastSpectrum.getFFTBinSizeHz() / 1000d, size >= 10000 ? size / 1000 : size, + size >= 10000 ? "k" : "", fps.getEma()); + fpsRenderedFrames++; + if (System.currentTimeMillis() - lastFPSRecalculated > 1000) { + double rawfps = fpsRenderedFrames / ((System.currentTimeMillis() - (double) lastFPSRecalculated) / 1000d); + fps.addNewValue(rawfps); + lastFPSRecalculated = System.currentTimeMillis(); + fpsRenderedFrames = 0; + } + g.dispose(); + +// double time = newDataTimeEMA.addNewValue(((System.nanoTime()-start)/1000)); +// System.out.println("draw "+(int)time+"us"); + +// repaint(); + } + + /** + * Draws color palette into given area from bottom (0%) to top (100%) + * + * @param g + * @param x + * @param y + * @param w + * @param h + */ + public void drawScale(Graphics2D g, int x, int y, int w, int h) { + g = (Graphics2D) g.create(x, y, w, h); + int step = 3; + for (int i = 0; i < h; i += step) { + Color c = palette.getColorNormalized(1 - (double) i / h); + g.setColor(c); + g.fillRect(0, i, w, step); + } + + /** + * draw border around the scale + */ + int thickness = 2; + g.setColor(Color.darkGray); + g.fillRect(0, 0, w, thickness); + g.fillRect(w - thickness, 0, thickness, h); + g.fillRect(0, h - thickness, w, thickness); + g.dispose(); + } + + public int getHistorySize() { + return bufferedImages[0].getHeight(); + } + + public double getSpectrumPaletteSize() { + return spectrumPaletteSize; + } + + public double getSpectrumPaletteStart() { + return spectrumPaletteStart; + } + + public void setDrawingOffsets(int xOffsetLeft, int width) { + this.chartXOffset = xOffsetLeft; + this.chartWidth = width; + } + + public synchronized void setHistorySize(int historyInPixels) { + BufferedImage bufferedImages[] = new BufferedImage[2]; + bufferedImages[0] = GraphicsToolkit.createAcceleratedImageOpaque(screenWidth, historyInPixels); + bufferedImages[1] = GraphicsToolkit.createAcceleratedImageOpaque(screenWidth, historyInPixels); + copyImage(this.bufferedImages[0], bufferedImages[0]); + copyImage(this.bufferedImages[1], bufferedImages[1]); + this.bufferedImages = bufferedImages; + } + + public void setSpectrumPaletteSize(int dB) { + this.spectrumPaletteSize = dB; + } + + /** + * Sets start and end of the color scale + * + * @param minFreqency + * @param maxFrequency + */ + public void setSpectrumPaletteStart(int dB) { + this.spectrumPaletteStart = dB; + } + + /** + * Sets status message to be drawn near bottom right corner + * + * @param message + * @param index + * max array length is 4 + */ + public void setStatusMessage(String message, int index) { + this.statusMessage[index] = message; + } + + private void copyImage(BufferedImage src, BufferedImage dst) { + Graphics2D g = dst.createGraphics(); + g.drawImage(src, 0, 0, null); + g.dispose(); + } + + private double translateChartXToFrequency(int x) { + if (lastSpectrum != null) { + double startFreq = lastSpectrum.getFreqStartMHz() * 1000000d; + double stopFreq = lastSpectrum.getFreqStopMHz() * 1000000d; + double freqRange = (stopFreq - startFreq); + double width = bufferedImages[0].getWidth(); + double percentageFreq = x / (double) chartWidth; + double freq = percentageFreq * freqRange + startFreq; + if (freq > stopFreq) + freq = stopFreq; + if (freq < startFreq) + freq = startFreq; + return freq; + } + return -1; + } + + Rectangle2D stringBounds; + @Override + protected void paintComponent(Graphics arg0) { + long drawStart = System.nanoTime(); + Graphics2D g = (Graphics2D) arg0; + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + int w = chartWidth; + int h = getHeight(); + g.setColor(Color.black); + g.fillRect(0, 0, getWidth(), getHeight()); + + g.drawImage(bufferedImages[drawIndex], chartXOffset, 0, w, h, null); + + if (displayMarker) { + g.setColor(Color.gray); + g.drawLine(displayMarkerX, 0, displayMarkerX, h); + g.drawString(String.format("%.1fMHz", displayMarkerFrequency / 1000000.0), displayMarkerX + 5, h / 2); + } //finish marker + + g.setColor(Color.white); + if (stringBounds == null) + stringBounds = g.getFontMetrics().getStringBounds("TEST", g); + int fontHeight = (int) stringBounds.getHeight(); + int x = chartXOffset + w - 350; + int y = h - fontHeight * (statusMessage.length + 1); + g.drawString(renderingInfo, x, y); + + for (int i = 0; i < statusMessage.length; i++) { + if (statusMessage[i] != null) + g.drawString(statusMessage[i], x, y + fontHeight * (i + 1)); + } + + long drawingTime = System.nanoTime()-drawStart; + drawingTimeSum += drawingTime; + drawingCounter++; + } + private volatile long drawingTimeSum = 0; + private volatile int drawingCounter = 0; + public int getDrawingCounterAndReset() { + int val = drawingCounter; + drawingCounter = 0; + return val; + } + /** + * Retrieves time in nanos the component spent in drawing itself and resets + * the counter to zero. + * @return + */ + public long getDrawTimeSumAndReset() { + long val = drawingTimeSum; + drawingTimeSum = 0; + return val; + } +} diff --git a/src/hackrf-sweep/src-java/resources/freq-europe.csv b/src/hackrf-sweep/src-java/resources/freq-europe.csv new file mode 100644 index 0000000..f26f9a8 --- /dev/null +++ b/src/hackrf-sweep/src-java/resources/freq-europe.csv @@ -0,0 +1,374 @@ +"Country";"Frequency Range";"Allocations";"Applications" +"- Europe (ECA) -";"526.500 - 1606.500 kHz";"Broadcasting";"Inductive applications/Broadcasting" +"- Europe (ECA) -";"1606.500 - 1625.000 kHz";"Fixed/Land Mobile/Maritime Mobile/Radiolocation";"Radiodetermination applications/Inductive applications/Maritime communications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"1625.000 - 1635.000 kHz";"Radiolocation";"Radiolocation (military)/Inductive applications/Radiodetermination applications" +"- Europe (ECA) -";"1635.000 - 1800.000 kHz";"Fixed/Land Mobile/Maritime Mobile";"Radiodetermination applications/Inductive applications/Maritime communications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"1800.000 - 1810.000 kHz";"Radiolocation";"Radiolocation (military)/Inductive applications/Radiodetermination applications" +"- Europe (ECA) -";"1810.000 - 1850.000 kHz";"Amateur";"Inductive applications/Amateur" +"- Europe (ECA) -";"1850.000 - 2000.000 kHz";"Fixed/Mobile except aeronautical mobile/Amateur";"Amateur/Maritime communications/Inductive applications/Radiodetermination applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"2000.000 - 2025.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Land military systems/Maritime military systems/Radiodetermination applications/Inductive applications/Maritime communications" +"- Europe (ECA) -";"2025.000 - 2045.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Maritime communications/Oceanographic buoys/Inductive applications/Radiodetermination applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"2045.000 - 2160.000 kHz";"Fixed/Land Mobile/Maritime Mobile";"Land military systems/Maritime military systems/Inductive applications/Maritime communications" +"- Europe (ECA) -";"2160.000 - 2170.000 kHz";"Radiolocation";"Inductive applications/Radiodetermination applications/Radiolocation (military)" +"- Europe (ECA) -";"2170.000 - 2173.500 kHz";"Maritime Mobile";"Maritime military systems/Inductive applications/Maritime communications" +"- Europe (ECA) -";"2173.500 - 2190.500 kHz";"Mobile (distress and calling)";"DSC/Inductive applications/Maritime communications" +"- Europe (ECA) -";"2190.500 - 2194.000 kHz";"Maritime Mobile";"Inductive applications/Maritime communications/Maritime military systems" +"- Europe (ECA) -";"2194.000 - 2300.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Land military systems/Maritime military systems/Maritime communications/Inductive applications/Radiodetermination applications" +"- Europe (ECA) -";"2300.000 - 2498.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Inductive applications/Maritime communications/Maritime military systems/Land military systems" +"- Europe (ECA) -";"2498.000 - 2501.000 kHz";"Standard frequency and time signal (2 500 kHz)";"Inductive applications" +"- Europe (ECA) -";"2501.000 - 2502.000 kHz";"Space Research/Standard Frequency and Time Signal";"Inductive applications" +"- Europe (ECA) -";"2502.000 - 2625.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Inductive applications/Radiodetermination applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"2625.000 - 2650.000 kHz";"Maritime Mobile/Maritime Radionavigation";"Maritime military systems/Inductive applications/Maritime communications" +"- Europe (ECA) -";"2650.000 - 2850.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Inductive applications/Radiodetermination applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"2850.000 - 3025.000 kHz";"Aeronautical Mobile-Satellite (R)";"Aeronautical military systems/SAR (communications)/Inductive applications/Aeronautical communications" +"- Europe (ECA) -";"3025.000 - 3155.000 kHz";"Aeronautical Mobile (OR)";"Aeronautical communications/Inductive applications/Aeronautical military systems" +"- Europe (ECA) -";"3155.000 - 3200.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Land military systems/Maritime military systems/Inductive applications/Maritime communications" +"- Europe (ECA) -";"3200.000 - 3230.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Inductive applications/Maritime communications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"3230.000 - 3400.000 kHz";"Fixed/Mobile except aeronautical mobile";"Land military systems/Maritime military systems/Inductive applications/Maritime communications" +"- Europe (ECA) -";"3400.000 - 3500.000 kHz";"Aeronautical Mobile (R)";"Aeronautical communications/Inductive applications/Aeronautical military systems" +"- Europe (ECA) -";"3500.000 - 3800.000 kHz";"Amateur/Fixed/Mobile except aeronautical mobile";"Land military systems/Maritime military systems/Inductive applications/Amateur/Maritime communications" +"- Europe (ECA) -";"3800.000 - 3900.000 kHz";"Aeronautical Mobile (OR)/Fixed/Land Mobile";"Aeronautical communications/Inductive applications/Aeronautical military systems/Land military systems" +"- Europe (ECA) -";"3900.000 - 3950.000 kHz";"Aeronautical Mobile (OR)";"Aeronautical military systems/Inductive applications/Aeronautical communications" +"- Europe (ECA) -";"3950.000 - 4000.000 kHz";"Broadcasting/Fixed";"Inductive applications/Broadcasting/Land military systems" +"- Europe (ECA) -";"4000.000 - 4063.000 kHz";"Fixed/Maritime Mobile";"Land military systems/Maritime military systems/Inductive applications/Maritime communications" +"- Europe (ECA) -";"4063.000 - 4438.000 kHz";"Maritime Mobile";"DSC/Maritime communications/Inductive applications/Railway applications/NAVTEX/Maritime military systems" +"- Europe (ECA) -";"4438.000 - 4488.000 kHz";"Fixed/Mobile except aeronautical mobile (R)/Radiolocation";"Land military systems/Maritime military systems/Radiolocation (military)/Inductive applications" +"- Europe (ECA) -";"4488.000 - 4650.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Inductive applications/Maritime military systems/Land military systems" +"- Europe (ECA) -";"4650.000 - 4700.000 kHz";"Aeronautical Mobile (R)";"Aeronautical military systems/Inductive applications/Aeronautical communications" +"- Europe (ECA) -";"4700.000 - 4750.000 kHz";"Aeronautical Mobile (OR)";"Aeronautical communications/Inductive applications/Aeronautical military systems" +"- Europe (ECA) -";"4750.000 - 4850.000 kHz";"Aeronautical Mobile (OR)/Fixed/Land Mobile";"Aeronautical military systems/Land military systems/Inductive applications/Aeronautical communications" +"- Europe (ECA) -";"4850.000 - 4995.000 kHz";"Fixed/Land Mobile";"Inductive applications/Land military systems" +"- Europe (ECA) -";"4995.000 - 5003.000 kHz";"Standard frequency and time signal (5 000 kHz)";"Inductive applications" +"- Europe (ECA) -";"5003.000 - 5005.000 kHz";"Standard Frequency and Time Signal/Space Research";"Inductive applications" +"- Europe (ECA) -";"5005.000 - 5060.000 kHz";"Fixed";"Inductive applications/Land military systems" +"- Europe (ECA) -";"5060.000 - 5250.000 kHz";"Fixed/Mobile except aeronautical mobile";"Land military systems/Maritime military systems/Inductive applications" +"- Europe (ECA) -";"5250.000 - 5275.000 kHz";"Fixed/Mobile except aeronautical mobile/Radiolocation";"Inductive applications/Land military systems/Maritime military systems/Radiolocation (military)" +"- Europe (ECA) -";"5275.000 - 5351.500 kHz";"Fixed/Mobile except aeronautical mobile";"Inductive applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"5351.500 - 5366.500 kHz";"Fixed/Mobile except aeronautical mobile/Amateur";"Amateur/Inductive applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"5366.500 - 5450.000 kHz";"Fixed/Mobile except aeronautical mobile";"Inductive applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"5450.000 - 5480.000 kHz";"Aeronautical Mobile (OR)/Fixed/Land Mobile";"Aeronautical military systems/Land military systems/Inductive applications/Aeronautical communications" +"- Europe (ECA) -";"5480.000 - 5680.000 kHz";"Aeronautical Mobile-Satellite (R)";"Aeronautical communications/Inductive applications/SAR (communications)/Aeronautical military systems" +"- Europe (ECA) -";"5680.000 - 5730.000 kHz";"Aeronautical Mobile (OR)";"Aeronautical military systems/SAR (communications)/Inductive applications/Aeronautical communications" +"- Europe (ECA) -";"5730.000 - 5900.000 kHz";"Fixed/Land Mobile";"Inductive applications/Land military systems" +"- Europe (ECA) -";"5900.000 - 5950.000 kHz";"Broadcasting";"Inductive applications/Broadcasting" +"- Europe (ECA) -";"5950.000 - 6200.000 kHz";"Broadcasting";"Broadcasting/Inductive applications" +"- Europe (ECA) -";"6200.000 - 6525.000 kHz";"Maritime Mobile";"Inductive applications/DSC/Maritime communications/Maritime military systems" +"- Europe (ECA) -";"6525.000 - 6685.000 kHz";"Aeronautical Mobile (R)";"Aeronautical military systems/Aeronautical communications/Inductive applications" +"- Europe (ECA) -";"6685.000 - 6765.000 kHz";"Aeronautical Mobile (OR)";"Inductive applications/Aeronautical communications/Aeronautical military systems" +"- Europe (ECA) -";"6765.000 - 7000.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Maritime military systems/Land military systems/Inductive applications/ISM" +"- Europe (ECA) -";"7000.000 - 7100.000 kHz";"Amateur/Amateur-Satellite";"Amateur/Inductive applications" +"- Europe (ECA) -";"7100.000 - 7200.000 kHz";"Amateur";"Inductive applications/Amateur" +"- Europe (ECA) -";"7200.000 - 7300.000 kHz";"Broadcasting";"Inductive applications/Broadcasting" +"- Europe (ECA) -";"7300.000 - 7400.000 kHz";"Broadcasting";"Broadcasting/Inductive applications" +"- Europe (ECA) -";"7400.000 - 7450.000 kHz";"Broadcasting";"Broadcasting/Inductive applications" +"- Europe (ECA) -";"7450.000 - 8100.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Inductive applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"8100.000 - 8195.000 kHz";"Fixed/Maritime Mobile";"Land military systems/Maritime military systems/Inductive applications/Maritime communications" +"- Europe (ECA) -";"8195.000 - 8815.000 kHz";"Maritime Mobile";"DSC/Inductive applications/Maritime communications/Maritime military systems" +"- Europe (ECA) -";"8815.000 - 8965.000 kHz";"Aeronautical Mobile (R)";"Aeronautical military systems/Aeronautical communications/Inductive applications" +"- Europe (ECA) -";"8965.000 - 9040.000 kHz";"Aeronautical Mobile (OR)";"Inductive applications/Aeronautical communications/Aeronautical military systems" +"- Europe (ECA) -";"9040.000 - 9305.000 kHz";"Fixed";"Land military systems/Inductive applications" +"- Europe (ECA) -";"9305.000 - 9355.000 kHz";"Fixed/Radiolocation";"Inductive applications/Land military systems" +"- Europe (ECA) -";"9355.000 - 9400.000 kHz";"Fixed";"Land military systems/Inductive applications" +"- Europe (ECA) -";"9400.000 - 9500.000 kHz";"Broadcasting";"Broadcasting/Inductive applications" +"- Europe (ECA) -";"9500.000 - 9900.000 kHz";"Broadcasting";"Inductive applications/Broadcasting" +"- Europe (ECA) -";"9900.000 - 9995.000 kHz";"Fixed";"Inductive applications/Land military systems" +"- Europe (ECA) -";"9995.000 - 10003.000 kHz";"Standard frequency and time signal (10 000 kHz)";"Inductive applications" +"- Europe (ECA) -";"10003.000 - 10005.000 kHz";"Standard Frequency and Time Signal/Space Research";"Inductive applications/SAR (communications)" +"- Europe (ECA) -";"10005.000 - 10100.000 kHz";"Aeronautical Mobile (R)";"Aeronautical communications/Inductive applications/Aeronautical military systems" +"- Europe (ECA) -";"10100.000 - 10150.000 kHz";"Fixed/Amateur";"Land military systems/Inductive applications/Amateur" +"- Europe (ECA) -";"10150.000 - 11175.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Inductive applications/Railway applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"11175.000 - 11275.000 kHz";"Aeronautical Mobile (OR)";"Aeronautical military systems/Inductive applications/Railway applications/Aeronautical communications" +"- Europe (ECA) -";"11275.000 - 11400.000 kHz";"Aeronautical Mobile (R)";"Aeronautical communications/Railway applications/Inductive applications/Aeronautical military systems" +"- Europe (ECA) -";"11400.000 - 11600.000 kHz";"Fixed";"Land military systems/Inductive applications/Railway applications" +"- Europe (ECA) -";"11600.000 - 11650.000 kHz";"Broadcasting";"Railway applications/Inductive applications/Broadcasting" +"- Europe (ECA) -";"11650.000 - 12050.000 kHz";"Broadcasting";"Broadcasting/Inductive applications/Railway applications" +"- Europe (ECA) -";"12050.000 - 12100.000 kHz";"Broadcasting";"Railway applications/Inductive applications/Broadcasting" +"- Europe (ECA) -";"12100.000 - 12230.000 kHz";"Fixed";"Inductive applications/Railway applications/Land military systems" +"- Europe (ECA) -";"12230.000 - 13200.000 kHz";"Maritime Mobile";"Maritime military systems/Railway applications/Inductive applications/DSC/Maritime communications" +"- Europe (ECA) -";"13200.000 - 13260.000 kHz";"Aeronautical Mobile (OR)";"Aeronautical communications/Inductive applications/Railway applications/Aeronautical military systems" +"- Europe (ECA) -";"13260.000 - 13360.000 kHz";"Aeronautical Mobile (R)";"Aeronautical military systems/Railway applications/Inductive applications/Aeronautical communications" +"- Europe (ECA) -";"13360.000 - 13410.000 kHz";"Fixed/Radio Astronomy";"Radio astronomy/Inductive applications/Railway applications/Land military systems" +"- Europe (ECA) -";"13410.000 - 13450.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Maritime military systems/Land military systems/Railway applications/Inductive applications" +"- Europe (ECA) -";"13450.000 - 13550.000 kHz";"Mobile except aeronautical mobile (R)/Fixed/Radiolocation";"Inductive applications/Railway applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"13550.000 - 13570.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Maritime military systems/Land military systems/Railway applications/Non-specific SRDs/ISM/Inductive applications" +"- Europe (ECA) -";"13570.000 - 13600.000 kHz";"Broadcasting";"Broadcasting/Railway applications/Inductive applications" +"- Europe (ECA) -";"13600.000 - 13800.000 kHz";"Broadcasting";"Inductive applications/Railway applications/Broadcasting" +"- Europe (ECA) -";"13800.000 - 13870.000 kHz";"Broadcasting";"Broadcasting/Railway applications/Inductive applications" +"- Europe (ECA) -";"13870.000 - 14000.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Inductive applications/Railway applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"14000.000 - 14250.000 kHz";"Amateur/Amateur-Satellite";"Railway applications/Inductive applications/Amateur/Amateur-satellite" +"- Europe (ECA) -";"14250.000 - 14350.000 kHz";"Amateur";"Amateur/Inductive applications/Railway applications" +"- Europe (ECA) -";"14350.000 - 14990.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Railway applications/Inductive applications/Maritime military systems/Land military systems" +"- Europe (ECA) -";"14990.000 - 15005.000 kHz";"Standard frequency and time signal (15 000 kHz)";"Inductive applications/Railway applications/SAR (communications)" +"- Europe (ECA) -";"15005.000 - 15010.000 kHz";"Standard Frequency and Time Signal/Space Research";"Railway applications/Inductive applications" +"- Europe (ECA) -";"15010.000 - 15100.000 kHz";"Aeronautical Mobile (OR)";"Inductive applications/Railway applications/Aeronautical communications/Aeronautical military systems" +"- Europe (ECA) -";"15100.000 - 15600.000 kHz";"Broadcasting";"Railway applications/Inductive applications/Broadcasting" +"- Europe (ECA) -";"15600.000 - 15800.000 kHz";"Broadcasting";"Broadcasting/Inductive applications/Railway applications" +"- Europe (ECA) -";"15800.000 - 16100.000 kHz";"Fixed";"Railway applications/Inductive applications/Land military systems" +"- Europe (ECA) -";"16100.000 - 16200.000 kHz";"Fixed/Radiolocation";"Land military systems/Inductive applications" +"- Europe (ECA) -";"16200.000 - 16360.000 kHz";"Fixed";"Inductive applications/Land military systems" +"- Europe (ECA) -";"16360.000 - 17410.000 kHz";"Maritime Mobile";"Maritime military systems/Inductive applications/DSC/Maritime communications" +"- Europe (ECA) -";"17410.000 - 17480.000 kHz";"Fixed";"Inductive applications/Land military systems" +"- Europe (ECA) -";"17480.000 - 17550.000 kHz";"Broadcasting";"Inductive applications/Broadcasting" +"- Europe (ECA) -";"17550.000 - 17900.000 kHz";"Broadcasting";"Broadcasting/Inductive applications" +"- Europe (ECA) -";"17900.000 - 17970.000 kHz";"Aeronautical Mobile (R)";"Inductive applications/Aeronautical communications/Aeronautical military systems" +"- Europe (ECA) -";"17970.000 - 18030.000 kHz";"Aeronautical Mobile (OR)";"Aeronautical military systems/Aeronautical communications/Inductive applications" +"- Europe (ECA) -";"18030.000 - 18052.000 kHz";"Fixed";"Inductive applications/Land military systems" +"- Europe (ECA) -";"18052.000 - 18068.000 kHz";"Fixed/Space Research";"Land military systems/Inductive applications" +"- Europe (ECA) -";"18068.000 - 18168.000 kHz";"Amateur/Amateur-Satellite";"Inductive applications/Amateur/Amateur-satellite" +"- Europe (ECA) -";"18168.000 - 18780.000 kHz";"Fixed/Mobile except aeronautical mobile";"DSC/Inductive applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"18780.000 - 18900.000 kHz";"Maritime Mobile";"Maritime military systems/Inductive applications/Maritime communications" +"- Europe (ECA) -";"18900.000 - 19020.000 kHz";"Broadcasting";"Inductive applications/Broadcasting" +"- Europe (ECA) -";"19020.000 - 19680.000 kHz";"Fixed";"Inductive applications/Land military systems" +"- Europe (ECA) -";"19680.000 - 19800.000 kHz";"Maritime Mobile";"Maritime military systems/Inductive applications/DSC/Maritime communications" +"- Europe (ECA) -";"19800.000 - 19990.000 kHz";"Fixed";"Inductive applications/Land military systems" +"- Europe (ECA) -";"19990.000 - 19995.000 kHz";"Standard Frequency and Time Signal/Space Research";"Inductive applications/SAR (communications)" +"- Europe (ECA) -";"19995.000 - 20010.000 kHz";"Standard frequency and time signal (20 000 kHz)";"Inductive applications" +"- Europe (ECA) -";"20010.000 - 21000.000 kHz";"Fixed/Mobile";"Inductive applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"21000.000 - 21450.000 kHz";"Amateur/Amateur-Satellite";"Inductive applications/Amateur/Amateur-satellite" +"- Europe (ECA) -";"21450.000 - 21850.000 kHz";"Broadcasting";"Inductive applications/Broadcasting" +"- Europe (ECA) -";"21850.000 - 21870.000 kHz";"Fixed";"Inductive applications/Land military systems" +"- Europe (ECA) -";"21870.000 - 21924.000 kHz";"Fixed";"Land military systems/Inductive applications" +"- Europe (ECA) -";"21924.000 - 22000.000 kHz";"Aeronautical Mobile (R)";"Inductive applications/Aeronautical communications/Aeronautical military systems" +"- Europe (ECA) -";"22000.000 - 22855.000 kHz";"Maritime Mobile";"Maritime military systems/DSC/Maritime communications/Inductive applications" +"- Europe (ECA) -";"22855.000 - 23000.000 kHz";"Fixed";"Inductive applications/Land military systems" +"- Europe (ECA) -";"23000.000 - 23200.000 kHz";"Fixed/Mobile except aeronautical mobile (R)";"Land military systems/Maritime military systems/Inductive applications" +"- Europe (ECA) -";"23200.000 - 23350.000 kHz";"Aeronautical Mobile (OR)/Fixed";"Inductive applications/Aeronautical communications/Aeronautical military systems/Land military systems" +"- Europe (ECA) -";"23350.000 - 24000.000 kHz";"Fixed/Mobile except aeronautical mobile";"Maritime military systems/Land military systems/Inductive applications" +"- Europe (ECA) -";"24000.000 - 24450.000 kHz";"Fixed/Land Mobile";"Inductive applications/Land military systems" +"- Europe (ECA) -";"24450.000 - 24600.000 kHz";"Fixed/Land Mobile/Radiolocation";"Land military systems/Inductive applications" +"- Europe (ECA) -";"24600.000 - 24890.000 kHz";"Fixed/Land Mobile";"Inductive applications/Land military systems" +"- Europe (ECA) -";"24890.000 - 24990.000 kHz";"Amateur/Amateur-Satellite";"Inductive applications/Amateur/Amateur-satellite" +"- Europe (ECA) -";"24990.000 - 25005.000 kHz";"Standard frequency and time signal (25 000 kHz)";"Inductive applications" +"- Europe (ECA) -";"25005.000 - 25010.000 kHz";"Standard Frequency and Time Signal/Space Research";"Inductive applications/Space research" +"- Europe (ECA) -";"25010.000 - 25070.000 kHz";"Fixed/Mobile except aeronautical mobile";"Inductive applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"25070.000 - 25210.000 kHz";"Maritime Mobile";"Maritime military systems/Inductive applications/DSC/Maritime communications" +"- Europe (ECA) -";"25210.000 - 25550.000 kHz";"Fixed/Mobile except aeronautical mobile";"Inductive applications/Maritime military systems/Land military systems" +"- Europe (ECA) -";"25550.000 - 25670.000 kHz";"Radio Astronomy";"Inductive applications/Radio astronomy" +"- Europe (ECA) -";"25670.000 - 26100.000 kHz";"Broadcasting";"Inductive applications/Broadcasting" +"- Europe (ECA) -";"26100.000 - 26175.000 kHz";"Maritime Mobile";"Inductive applications/DSC/Maritime communications/Maritime military systems" +"- Europe (ECA) -";"26175.000 - 26200.000 kHz";"Fixed/Mobile except aeronautical mobile";"Maritime military systems/Land military systems/Inductive applications" +"- Europe (ECA) -";"26200.000 - 26350.000 kHz";"Fixed/Mobile except aeronautical mobile/Radiolocation";"Inductive applications/Land military systems/Maritime military systems" +"- Europe (ECA) -";"26350.000 - 27500.000 kHz";"Fixed/Mobile except aeronautical mobile";"Maritime military systems/Land military systems/Model control/Non-specific SRDs/ISM/CB radio/Inductive applications/Railway applications" +"- Europe (ECA) -";"27500.000 - 28000.000 kHz";"Fixed/Meteorological Aids/Mobile";"Inductive applications/Aeronautical military systems/Land military systems/Maritime military systems" +"- Europe (ECA) -";" 28.000 - 29.700 MHz";"Amateur/Amateur-Satellite";"Inductive applications/Amateur/Amateur-satellite" +"- Europe (ECA) -";" 29.700 - 30.005 MHz";"Mobile";"Radio microphones and ALD/Inductive applications/Active medical implants/Aeronautical military systems/Maritime military systems/Land military systems" +"- Europe (ECA) -";" 30.005 - 30.010 MHz";"Mobile";"Land military systems/Aeronautical military systems/Maritime military systems/Satellite systems (military)/Active medical implants/Radio microphones and ALD" +"- Europe (ECA) -";" 30.010 - 37.500 MHz";"Mobile";"Model control/PMR/Radio microphones and ALD/Active medical implants/Aeronautical military systems/Maritime military systems/Land military systems" +"- Europe (ECA) -";" 37.500 - 38.250 MHz";"Mobile/Radio Astronomy";"Aeronautical military systems/Maritime military systems/Land military systems/PMR/Radio astronomy/Radio microphones and ALD" +"- Europe (ECA) -";" 38.250 - 39.000 MHz";"Mobile";"PMR/Radio microphones and ALD/Land military systems/Aeronautical military systems/Maritime military systems" +"- Europe (ECA) -";" 39.000 - 39.500 MHz";"Mobile/Radiolocation";"Aeronautical military systems/Maritime military systems/Land military systems/Meteor scatter communications/PMR/Radio microphones and ALD" +"- Europe (ECA) -";" 39.500 - 39.986 MHz";"Mobile";"Radio microphones and ALD/PMR/Meteor scatter communications/Land military systems/Aeronautical military systems/Maritime military systems" +"- Europe (ECA) -";" 39.986 - 40.020 MHz";"Mobile/Space Research";"Aeronautical military systems/Maritime military systems/Land military systems/PMR/Radio microphones and ALD" +"- Europe (ECA) -";" 40.020 - 40.660 MHz";"Mobile";"PMR/Radio microphones and ALD/Maritime military systems/Land military systems/Aeronautical military systems" +"- Europe (ECA) -";" 40.660 - 40.700 MHz";"Mobile";"Land military systems/Aeronautical military systems/Maritime military systems/ISM/Model control/Non-specific SRDs/Radio microphones and ALD" +"- Europe (ECA) -";" 40.700 - 40.980 MHz";"Mobile";"PMR/Radio microphones and ALD/Aeronautical military systems/Maritime military systems/Land military systems" +"- Europe (ECA) -";" 40.980 - 41.015 MHz";"Mobile/Space Research";"Land military systems/Aeronautical military systems/Maritime military systems/PMR/Radio microphones and ALD" +"- Europe (ECA) -";" 41.015 - 42.000 MHz";"Mobile";"PMR/Radio microphones and ALD/Aeronautical military systems/Maritime military systems/Land military systems" +"- Europe (ECA) -";" 42.000 - 42.500 MHz";"Mobile/Fixed/Radiolocation";"Aeronautical military systems/Maritime military systems/Land military systems/PMR/Radio microphones and ALD" +"- Europe (ECA) -";" 42.500 - 44.000 MHz";"Mobile";"Radio microphones and ALD/PMR/Aeronautical military systems/Maritime military systems/Land military systems" +"- Europe (ECA) -";" 44.000 - 47.000 MHz";"Mobile";"Land military systems/Aeronautical military systems/Maritime military systems/PMR/Radio microphones and ALD/Wind profilers" +"- Europe (ECA) -";" 47.000 - 50.000 MHz";"Land Mobile";"On-site paging/PMR/Wind profilers/Earth exploration-satellite/Land military systems" +"- Europe (ECA) -";" 50.000 - 52.000 MHz";"Land Mobile/Amateur";"Land military systems/Amateur/PMR/Wind profilers" +"- Europe (ECA) -";" 52.000 - 68.000 MHz";"Land Mobile";"PMR/Wind profilers/Land military systems" +"- Europe (ECA) -";" 68.000 - 70.450 MHz";"Mobile/Amateur";"Amateur/Land military systems/Maritime military systems/PMR/PAMR" +"- Europe (ECA) -";" 70.450 - 74.800 MHz";"Amateur/Mobile except aeronautical mobile/Radio Astronomy";"PMR/PAMR/Radio astronomy/Maritime military systems/Land military systems/Amateur" +"- Europe (ECA) -";" 74.800 - 75.200 MHz";"Aeronautical Radionavigation";"ILS" +"- Europe (ECA) -";" 75.200 - 87.500 MHz";"Mobile";"PMR/PAMR/Land military systems/Maritime military systems" +"- Europe (ECA) -";" 87.500 - 100.000 MHz";"Broadcasting";"FM sound analogue/Wireless audio/multimedia" +"- Europe (ECA) -";"100.000 - 108.000 MHz";"Broadcasting";"FM sound analogue/Wireless audio/multimedia" +"- Europe (ECA) -";"108.000 - 117.975 MHz";"Aeronautical Radionavigation/Aeronautical Mobile (R)";"Aeronautical communications/ILS/VOR/GBAS" +"- Europe (ECA) -";"117.975 - 121.450 MHz";"Aeronautical Mobile-Satellite (R)";"Aeronautical communications" +"- Europe (ECA) -";"121.450 - 121.550 MHz";"Aeronautical Mobile (R)";"EPIRBs/-" +"- Europe (ECA) -";"121.550 - 136.000 MHz";"Aeronautical Mobile (R)";"Aeronautical communications" +"- Europe (ECA) -";"136.000 - 137.000 MHz";"Aeronautical Mobile (R)";"Aeronautical communications" +"- Europe (ECA) -";"137.000 - 137.025 MHz";"Meteorological-Satellite (space-to-Earth)/Mobile/Mobile-Satellite (space-to-Earth)/Space Operation (space-to-Earth)/Space Research (space-to-Earth)";"S-PCS/Weather satellites/Land mobile/Land military systems/Satellite systems (military)/Aeronautical military systems" +"- Europe (ECA) -";"137.025 - 137.175 MHz";"Meteorological-Satellite (space-to-Earth)/Mobile/Mobile-Satellite (space-to-Earth)/Space Operation (space-to-Earth)/Space Research (space-to-Earth)";"Land military systems/Satellite systems (military)/Aeronautical military systems/S-PCS/Weather satellites/Land mobile" +"- Europe (ECA) -";"137.175 - 137.825 MHz";"Meteorological-Satellite (space-to-Earth)/Mobile/Mobile-Satellite (space-to-Earth)/Space Operation (space-to-Earth)/Space Research (space-to-Earth)";"S-PCS/Weather satellites/Land mobile/Land military systems/Satellite systems (military)/Aeronautical military systems" +"- Europe (ECA) -";"137.825 - 138.000 MHz";"Meteorological-Satellite (space-to-Earth)/Mobile/Mobile-Satellite (space-to-Earth)/Space Operation (space-to-Earth)/Space Research (space-to-Earth)";"Land military systems/Satellite systems (military)/Aeronautical military systems/S-PCS/Weather satellites/Land mobile" +"- Europe (ECA) -";"138.000 - 143.600 MHz";"Aeronautical Mobile (OR)/Land Mobile/Space Research (space-to-Earth)";"Land mobile/Non-specific SRDs/Land military systems/Aeronautical military systems/Maritime military systems" +"- Europe (ECA) -";"143.600 - 143.650 MHz";"Aeronautical Mobile (OR)/Land Mobile/Space Research (space-to-Earth)";"Land military systems/Aeronautical military systems/Maritime military systems/Land mobile" +"- Europe (ECA) -";"143.650 - 144.000 MHz";"Aeronautical Mobile (OR)/Land Mobile";"Land mobile/Land military systems/Aeronautical military systems/Maritime military systems" +"- Europe (ECA) -";"144.000 - 146.000 MHz";"Amateur/Amateur-Satellite";"Amateur/Amateur-satellite" +"- Europe (ECA) -";"146.000 - 148.000 MHz";"Mobile";"PMR/PAMR" +"- Europe (ECA) -";"148.000 - 149.900 MHz";"Mobile/Mobile-Satellite (Earth-to-space)";"S-PCS/PMR/PAMR" +"- Europe (ECA) -";"149.900 - 150.050 MHz";"Mobile/Mobile-Satellite (Earth-to-space)";"S-PCS/PMR/PAMR" +"- Europe (ECA) -";"150.050 - 153.000 MHz";"Mobile except aeronautical mobile/Radio Astronomy";"PMR/PAMR/Radio astronomy" +"- Europe (ECA) -";"153.000 - 154.000 MHz";"Mobile except aeronautical mobile (R)";"PMR/PAMR" +"- Europe (ECA) -";"154.0000 - 156.4875 MHz";"Mobile except aeronautical mobile (R)";"PMR/PAMR/Maritime communications" +"- Europe (ECA) -";"156.4875 - 156.5125 MHz";"Maritime Mobile (distress and calling via DSC)";"Maritime communications" +"- Europe (ECA) -";"156.5125 - 156.5375 MHz";"Maritime Mobile (distress and calling via DSC)";"DSC" +"- Europe (ECA) -";"156.5375 - 156.5625 MHz";"Maritime Mobile (distress and calling via DSC)/Mobile except aeronautical mobile (R)";"Maritime communications" +"- Europe (ECA) -";"156.5625 - 156.7625 MHz";"Mobile except aeronautical mobile (R)";"Maritime communications" +"- Europe (ECA) -";"156.7625 - 156.7875 MHz";"Maritime Mobile (distress and calling)";"Maritime communications" +"- Europe (ECA) -";"156.7875 - 156.8125 MHz";"Maritime Mobile (distress and calling)";"Maritime communications" +"- Europe (ECA) -";"156.8125 - 156.8375 MHz";"Maritime Mobile";"Maritime communications" +"- Europe (ECA) -";"156.8375 - 161.9375 MHz";"Mobile except aeronautical mobile";"Maritime communications/PMR/PAMR" +"- Europe (ECA) -";"161.9375 - 161.9625 MHz";"Mobile except aeronautical mobile/Maritime Mobile-Satellite (Earth-to-space)";"Maritime communications/PMR/PAMR" +"- Europe (ECA) -";"161.9625 - 161.9875 MHz";"";"Maritime communications/AIS" +"- Europe (ECA) -";"161.9875 - 162.0125 MHz";"Mobile except aeronautical mobile/Maritime Mobile-Satellite (Earth-to-space)";"Maritime communications" +"- Europe (ECA) -";"162.0125 - 162.0375 MHz";"Mobile except aeronautical mobile";"Maritime communications/AIS" +"- Europe (ECA) -";"162.0375 - 169.4000 MHz";"Mobile except aeronautical mobile";"PMR/PAMR" +"- Europe (ECA) -";"169.4000 - 169.8125 MHz";"Mobile except aeronautical mobile";"Aids for hearing impaired/Meter reading/Non-specific SRDs" +"- Europe (ECA) -";"169.8125 - 174.0000 MHz";"Mobile except aeronautical mobile";"Radio microphones and ALD/PMR/PAMR/Aids for hearing impaired" +"- Europe (ECA) -";"174.000 - 223.000 MHz";"Broadcasting/Land Mobile";"PMSE/Radio microphones and ALD/Broadcasting (terrestrial)" +"- Europe (ECA) -";"223.000 - 225.000 MHz";"Broadcasting";"Broadcasting (terrestrial)" +"- Europe (ECA) -";"225.000 - 230.000 MHz";"Broadcasting/Land Mobile";"Broadcasting (terrestrial)/Defence systems" +"- Europe (ECA) -";"230.000 - 235.000 MHz";"Mobile";"Defence systems/T-DAB" +"- Europe (ECA) -";"235.000 - 240.000 MHz";"Mobile";"Defence systems/T-DAB" +"- Europe (ECA) -";"240.000 - 242.950 MHz";"Mobile";"Defence systems" +"- Europe (ECA) -";"242.950 - 243.050 MHz";"Aeronautical Mobile";"EPIRBs" +"- Europe (ECA) -";"243.050 - 267.000 MHz";"Mobile";"Defence systems" +"- Europe (ECA) -";"267.000 - 272.000 MHz";"Mobile";"Defence systems" +"- Europe (ECA) -";"272.000 - 273.000 MHz";"Mobile";"Defence systems" +"- Europe (ECA) -";"273.000 - 312.000 MHz";"Mobile";"Defence systems" +"- Europe (ECA) -";"312.000 - 315.000 MHz";"Mobile";"Defence systems" +"- Europe (ECA) -";"315.000 - 322.000 MHz";"Mobile";"Defence systems" +"- Europe (ECA) -";"322.000 - 328.600 MHz";"Mobile/Radio Astronomy";"Defence systems/Radio astronomy" +"- Europe (ECA) -";"328.600 - 335.400 MHz";"Aeronautical Radionavigation";"ILS" +"- Europe (ECA) -";"380.000 - 385.000 MHz";"Mobile";"Defence systems/PPDR" +"- Europe (ECA) -";"385.000 - 387.000 MHz";"Mobile";"Defence systems/PMR/PAMR" +"- Europe (ECA) -";"387.000 - 390.000 MHz";"Mobile";"Defence systems/PMR/PAMR" +"- Europe (ECA) -";"390.000 - 395.000 MHz";"Mobile";"Defence systems/PPDR" +"- Europe (ECA) -";"395.000 - 399.900 MHz";"Mobile";"Defence systems/PMR/PAMR" +"- Europe (ECA) -";"399.900 - 400.050 MHz";"Mobile-Satellite (Earth-to-space)";"PPDR" +"- Europe (ECA) -";"400.050 - 400.150 MHz";"Standard frequency and time signal-satellite (400.1 MHz)";"PPDR" +"- Europe (ECA) -";"400.150 - 401.000 MHz";"Meteorological Aids/Meteorological-Satellite (space-to-Earth)/Mobile-Satellite (space-to-Earth)/Space Research (space-to-Earth)/Space Operation (space-to-Earth)";"PPDR/Sondes/Weather satellites/S-PCS" +"- Europe (ECA) -";"401.000 - 402.000 MHz";"Earth Exploration-Satellite (Earth-to-space)/Meteorological Aids/Meteorological-Satellite (Earth-to-space)";"Sondes/Weather satellites/Active medical implants" +"- Europe (ECA) -";"402.000 - 403.000 MHz";"Earth Exploration-Satellite (Earth-to-space)/Meteorological Aids/Meteorological-Satellite (Earth-to-space)";"Sondes/Active medical implants/Weather satellites" +"- Europe (ECA) -";"403.000 - 406.000 MHz";"Meteorological Aids";"Sondes/Active medical implants" +"- Europe (ECA) -";"406.000 - 406.100 MHz";"Mobile-Satellite (Earth-to-space)";"EPIRBs" +"- Europe (ECA) -";"406.100 - 410.000 MHz";"Land Mobile/Radio Astronomy";"PMR/PAMR/Radio astronomy/Land military systems/Maritime military systems" +"- Europe (ECA) -";"410.000 - 420.000 MHz";"Mobile except aeronautical mobile";"Land military systems/Maritime military systems/PMR/PAMR" +"- Europe (ECA) -";"420.000 - 430.000 MHz";"Mobile except aeronautical mobile/Radiolocation";"PMR/PAMR/Land military systems/Maritime military systems/Radiolocation (military)" +"- Europe (ECA) -";"430.000 - 432.000 MHz";"Amateur/Radiolocation";"Radiolocation (military)/ULP-WMCE/Amateur" +"- Europe (ECA) -";"432.000 - 433.050 MHz";"Amateur/Radiolocation/Earth Exploration-Satellite (active)";"Active sensors (satellite)/Amateur/ULP-WMCE/Radiolocation (military)" +"- Europe (ECA) -";"433.050 - 434.790 MHz";"Amateur/Radiolocation/Land Mobile/Earth Exploration-Satellite (active)";"Radiolocation (military)/ULP-WMCE/Amateur/ISM/Non-specific SRDs/Active sensors (satellite)" +"- Europe (ECA) -";"434.790 - 438.000 MHz";"Amateur/Amateur-Satellite/Radiolocation/Earth Exploration-Satellite (active)";"Amateur/Amateur-satellite/Active sensors (satellite)/ULP-WMCE/Radiolocation (military)" +"- Europe (ECA) -";"438.000 - 440.000 MHz";"Amateur/Radiolocation";"Radiolocation (military)/ULP-WMCE/Amateur" +"- Europe (ECA) -";"440.000 - 450.000 MHz";"Mobile except aeronautical mobile/Radiolocation";"Wind profilers/On-site paging/PMR 446/PMR/PAMR/Land military systems/Maritime military systems/Radiolocation (military)" +"- Europe (ECA) -";"450.000 - 455.000 MHz";"Mobile";"On-site paging/PMR/PAMR" +"- Europe (ECA) -";"455.000 - 456.000 MHz";"Mobile";"Land mobile/On-site paging/PMR/PAMR" +"- Europe (ECA) -";"456.000 - 459.000 MHz";"Mobile";"Land mobile/On-board communications/PMR/PAMR/On-site paging" +"- Europe (ECA) -";"459.000 - 460.000 MHz";"Mobile";"Land mobile/On-site paging/PMR/PAMR" +"- Europe (ECA) -";"460.000 - 470.000 MHz";"Mobile";"Land mobile/On-board communications/PMR/PAMR/On-site paging/Space research/Meteorological aids (military)" +"- Europe (ECA) -";"470.000 - 694.000 MHz";"Broadcasting";"Radio microphones and ALD/PMSE/Broadcasting (terrestrial)/Wind profilers/Radio astronomy" +"- Europe (ECA) -";"694.000 - 790.000 MHz";"Broadcasting/Mobile except aeronautical mobile";"Radio microphones and ALD/PMSE/Broadcasting (terrestrial)/MFCN/PPDR" +"- Europe (ECA) -";"790.000 - 862.000 MHz";"Mobile except aeronautical mobile/Broadcasting";"MFCN/-/Radio microphones and ALD/Broadcasting (terrestrial)" +"- Europe (ECA) -";"862.000 - 870.000 MHz";"Mobile";"Radio microphones and ALD/Alarms/Non-specific SRDs/RFID/Tracking, tracing and data acquisition/-/Maritime military systems/Land military systems/Wideband data transmission systems" +"- Europe (ECA) -";"870.000 - 876.000 MHz";"Mobile";"-/Land military systems/Maritime military systems/Non-specific SRDs/PMR/PAMR/Tracking, tracing and data acquisition" +"- Europe (ECA) -";"876.000 - 880.000 MHz";"Mobile";"-/Land military systems/Maritime military systems/GSM-R" +"- Europe (ECA) -";"880.000 - 890.000 MHz";"Mobile";"GSM/MCV/IMT" +"- Europe (ECA) -";"890.000 - 915.000 MHz";"Mobile/Radiolocation";"IMT/MCV/GSM/Land military systems/Maritime military systems" +"- Europe (ECA) -";"915.000 - 921.000 MHz";"Mobile/Radiolocation";"Maritime military systems/Land military systems/PMR/PAMR/-/Non-specific SRDs/RFID" +"- Europe (ECA) -";"921.000 - 925.000 MHz";"Mobile/Radiolocation";"GSM-R/Land military systems/Maritime military systems/-" +"- Europe (ECA) -";"925.000 - 942.000 MHz";"Radiolocation/Mobile";"GSM/IMT/MCV/Land military systems/Maritime military systems" +"- Europe (ECA) -";"942.000 - 960.000 MHz";"Mobile";"GSM/IMT/MCV" +"- Europe (ECA) -";"960.000 - 1164.000 MHz";"Aeronautical Radionavigation/Aeronautical Mobile-Satellite (R)/Aeronautical Mobile (R)";"Aeronautical/Aeronautical military systems" +"- Europe (ECA) -";"1164.000 - 1215.000 MHz";"Aeronautical Radionavigation/Radionavigation-Satellite (space-to-Earth) (space-to-space)";"Aeronautical military systems/Satellite systems (military)/GALILEO/Aeronautical navigation/GNSS Repeater/GLONASS" +"- Europe (ECA) -";"1215.000 - 1240.000 MHz";"Earth Exploration-Satellite (active)/Radiolocation/Radionavigation-Satellite (space-to-Earth) (space-to-space)/Space Research (active)";"GLONASS/GNSS Repeater/GPS/Radiolocation (civil)/Active sensors (satellite)/Satellite systems (military)/Radiolocation (military)" +"- Europe (ECA) -";"1240.000 - 1300.000 MHz";"Earth Exploration-Satellite (active)/Radionavigation-Satellite (space-to-Earth) (space-to-space)/Radiolocation/Space Research (active)/Amateur/Amateur-Satellite";"Radiolocation (military)/Satellite systems (military)/Amateur-satellite/GALILEO/Wind profilers/Amateur/GLONASS/Radiolocation (civil)/Active sensors (satellite)/GNSS Repeater" +"- Europe (ECA) -";"1300.000 - 1350.000 MHz";"Aeronautical Radionavigation/Radiolocation/Radionavigation-Satellite (Earth-to-space)";"Satellite navigation systems/Radiolocation (civil)/Radio astronomy/Satellite systems (military)/Radiolocation (military)" +"- Europe (ECA) -";"1350.000 - 1400.000 MHz";"Fixed/Mobile/Radiolocation";"Land military systems/Maritime military systems/Aeronautical military systems/Radiolocation (military)/Radio microphones and ALD/Fixed/Radio astronomy" +"- Europe (ECA) -";"1400.000 - 1427.000 MHz";"Earth Exploration-Satellite (passive)/Radio Astronomy/Space Research (passive)";"Passive sensors (satellite)/Radio astronomy" +"- Europe (ECA) -";"1427.000 - 1429.000 MHz";"Fixed/Mobile except aeronautical mobile/Space Operation (Earth-to-space)";"Fixed/MFCN/Maritime military systems/Land military systems" +"- Europe (ECA) -";"1429.000 - 1452.000 MHz";"Fixed/Mobile except aeronautical mobile";"Land military systems/Maritime military systems/MFCN/Fixed" +"- Europe (ECA) -";"1452.000 - 1492.000 MHz";"Broadcasting/Fixed/Mobile except aeronautical mobile";"T-DAB/MFCN" +"- Europe (ECA) -";"1492.000 - 1518.000 MHz";"Fixed/Mobile except aeronautical mobile";"Fixed/MFCN/Maritime military systems/Land military systems/Radio microphones and ALD" +"- Europe (ECA) -";"1518.000 - 1525.000 MHz";"Fixed/Mobile except aeronautical mobile/Mobile-Satellite (space-to-Earth)";"Maritime military systems/Land military systems/Radio microphones and ALD/MSS Earth stations/Fixed/IMT-2000 satellite component" +"- Europe (ECA) -";"1525.000 - 1530.000 MHz";"Fixed/Mobile-Satellite (space-to-Earth)/Space Operation (space-to-Earth)";"IMT-2000 satellite component/MSS Earth stations/Fixed" +"- Europe (ECA) -";"1530.000 - 1535.000 MHz";"Mobile-Satellite (space-to-Earth)/Space Operation (space-to-Earth)/Earth Exploration-Satellite/Fixed/Mobile except aeronautical mobile (R)";"IMT-2000 satellite component/MSS Earth stations" +"- Europe (ECA) -";"1535.000 - 1559.000 MHz";"Mobile-Satellite (space-to-Earth)";"MSS Earth stations/IMT-2000 satellite component" +"- Europe (ECA) -";"1559.000 - 1610.000 MHz";"Aeronautical Radionavigation/Radionavigation-Satellite (space-to-Earth)/Radionavigation-Satellite (space-to-space)";"GNSS Pseudolites/GNSS Repeater/GALILEO/GLONASS/GPS" +"- Europe (ECA) -";"1610.000 - 1610.600 MHz";"Aeronautical Radionavigation/Mobile-Satellite (Earth-to-space)";"MSS Earth stations/IMT-2000 satellite component/GLONASS" +"- Europe (ECA) -";"1610.600 - 1613.800 MHz";"Aeronautical Radionavigation/Mobile-Satellite (Earth-to-space)/Radio Astronomy";"IMT-2000 satellite component/MSS Earth stations/Radio astronomy" +"- Europe (ECA) -";"1613.800 - 1626.500 MHz";"Aeronautical Radionavigation/Mobile-Satellite (Earth-to-space)/Mobile-Satellite (space-to-Earth)";"MSS Earth stations/IMT-2000 satellite component" +"- Europe (ECA) -";"1626.500 - 1660.000 MHz";"Mobile-Satellite (Earth-to-space)";"IMT-2000 satellite component/MSS Earth stations" +"- Europe (ECA) -";"1660.000 - 1660.500 MHz";"Mobile-Satellite (Earth-to-space)/Radio Astronomy";"MSS Earth stations/Radio astronomy/IMT-2000 satellite component" +"- Europe (ECA) -";"1660.500 - 1668.000 MHz";"Radio Astronomy/Space Research (passive)/Fixed/Mobile except aeronautical mobile";"Radio astronomy" +"- Europe (ECA) -";"1668.000 - 1668.400 MHz";"Mobile-Satellite (Earth-to-space)/Radio Astronomy/Space Research (passive)/Fixed/Mobile except aeronautical mobile";"Radio astronomy/IMT-2000 satellite component" +"- Europe (ECA) -";"1668.400 - 1670.000 MHz";"Fixed/Meteorological Aids/Mobile except aeronautical mobile/Mobile-Satellite (Earth-to-space)/Radio Astronomy";"IMT-2000 satellite component/Meteorology/Radio astronomy" +"- Europe (ECA) -";"1670.000 - 1675.000 MHz";"Meteorological Aids/Meteorological-Satellite (space-to-Earth)/Mobile/Mobile-Satellite (Earth-to-space)/Fixed";"Weather satellites/MSS Earth stations/IMT-2000 satellite component/Meteorology" +"- Europe (ECA) -";"1675.000 - 1690.000 MHz";"Fixed/Meteorological Aids/Meteorological-Satellite (space-to-Earth)/Mobile except aeronautical mobile";"Land military systems/Maritime military systems/Meteorological aids (military)/Sondes/Weather satellites" +"- Europe (ECA) -";"1690.000 - 1700.000 MHz";"Meteorological Aids/Meteorological-Satellite (space-to-Earth)/Fixed/Mobile except aeronautical mobile";"Weather satellites/Land military systems/Meteorological aids (military)/Maritime military systems" +"- Europe (ECA) -";"1700.000 - 1710.000 MHz";"Fixed/Meteorological-Satellite (space-to-Earth)/Mobile except aeronautical mobile";"Land military systems/Maritime military systems/Meteorological aids (military)/Weather satellites" +"- Europe (ECA) -";"1710.000 - 1785.000 MHz";"Fixed/Mobile";"GSM/Radio astronomy/IMT/MCV/MCA" +"- Europe (ECA) -";"1785.000 - 1800.000 MHz";"Fixed/Mobile";"-/Land mobile/Radio microphones and ALD/Land military systems" +"- Europe (ECA) -";"1800.000 - 1805.000 MHz";"Mobile/Fixed";"Land military systems/Radio microphones and ALD/-" +"- Europe (ECA) -";"1805.000 - 1880.000 MHz";"Fixed/Mobile";"MCA/MCV/IMT/GSM" +"- Europe (ECA) -";"1880.000 - 1885.000 MHz";"Mobile/Fixed";"DECT" +"- Europe (ECA) -";"1885.000 - 1900.000 MHz";"Mobile/Fixed";"DECT" +"- Europe (ECA) -";"1900.000 - 1930.000 MHz";"Mobile/Fixed";"MCA/-/MFCN/MCV/DA2GC" +"- Europe (ECA) -";"1930.000 - 1970.000 MHz";"Fixed/Mobile";"MFCN/MCV/-/MCA" +"- Europe (ECA) -";"1970.000 - 1980.000 MHz";"Mobile/Fixed";"MCA/-/MFCN/MCV" +"- Europe (ECA) -";"1980.000 - 2010.000 MHz";"Mobile/Mobile-Satellite (Earth-to-space)";"-/MSS Earth stations" +"- Europe (ECA) -";"2010.000 - 2025.000 MHz";"Mobile/Fixed";"IMT/-/PMSE" +"- Europe (ECA) -";"2025.000 - 2110.000 MHz";"Earth Exploration-Satellite (Earth-to-space) (space-to-space)/Fixed/Mobile/Space Operation (Earth-to-space) (space-to-space)/Space Research (Earth-to-space) (space-to-space)";"Land military systems/Telemetry/Telecommand (military)/Aeronautical military systems/Maritime military systems/Fixed/PMSE/Space research" +"- Europe (ECA) -";"2110.000 - 2120.000 MHz";"Mobile/Space Research (deep space) (Earth-to-space)/Fixed";"-/MCA/MFCN/MCV" +"- Europe (ECA) -";"2120.000 - 2170.000 MHz";"Mobile/Fixed";"MFCN/MCV/MCA/-" +"- Europe (ECA) -";"2170.000 - 2200.000 MHz";"Mobile/Mobile-Satellite (space-to-Earth)";"-/MSS Earth stations" +"- Europe (ECA) -";"2200.000 - 2290.000 MHz";"Fixed/Mobile/Space Operation (space-to-Earth) (space-to-space)/Space Research (space-to-Earth) (space-to-space)/Earth Exploration-Satellite (space-to-Earth) (space-to-space)";"Fixed/Radio astronomy/Space research/PMSE/Land military systems/Telemetry/Telecommand (military)/Aeronautical military systems/Maritime military systems" +"- Europe (ECA) -";"2290.000 - 2300.000 MHz";"Fixed/Mobile except aeronautical mobile/Space Research (deep space) (space-to-Earth)";"PMSE/Land mobile/Space research" +"- Europe (ECA) -";"2300.000 - 2400.000 MHz";"Fixed/Mobile/Amateur/Radiolocation";"Aeronautical telemetry/Amateur/PMSE/MFCN/Land military systems/Telemetry/Telecommand (military)/Aeronautical military systems/Maritime military systems" +"- Europe (ECA) -";"2400.000 - 2450.000 MHz";"Fixed/Mobile/Amateur-Satellite/Radiolocation/Amateur";"PMSE/Radiodetermination applications/Amateur/Amateur-satellite/ISM/Non-specific SRDs/Wideband data transmission systems/RFID" +"- Europe (ECA) -";"2450.000 - 2483.500 MHz";"Fixed/Mobile";"ISM/Non-specific SRDs/Wideband data transmission systems/RFID/Radiodetermination applications/PMSE" +"- Europe (ECA) -";"2483.500 - 2500.000 MHz";"Fixed/Mobile/Mobile-Satellite (space-to-Earth)";"PMSE/MBANS/Active medical implants/ISM/Land mobile/MSS Earth stations/IMT-2000 satellite component" +"- Europe (ECA) -";"2500.000 - 2520.000 MHz";"Mobile except aeronautical mobile/Fixed";"MCV/MFCN" +"- Europe (ECA) -";"2520.000 - 2655.000 MHz";"Fixed/Mobile except aeronautical mobile";"MFCN/MCV" +"- Europe (ECA) -";"2655.000 - 2670.000 MHz";"Fixed/Mobile except aeronautical mobile/Earth Exploration-Satellite (passive)/Radio Astronomy/Space Research (passive)";"MCV/MFCN/Radio astronomy" +"- Europe (ECA) -";"2670.000 - 2690.000 MHz";"Mobile except aeronautical mobile/Fixed/Radio Astronomy";"Radio astronomy/MFCN/MCV" +"- Europe (ECA) -";"2690.000 - 2700.000 MHz";"Earth Exploration-Satellite (passive)/Radio Astronomy/Space Research (passive)";"Passive sensors (satellite)/Radio astronomy" +"- Europe (ECA) -";"2700.000 - 2900.000 MHz";"Aeronautical Radionavigation/Radiolocation";"Radiolocation (civil)/Aeronautical navigation/Weather radar/Radiolocation (military)/PMSE" +"- Europe (ECA) -";"2900.000 - 3100.000 MHz";"Radiolocation/Radionavigation";"Radiolocation (military)/Radiolocation (civil)" +"- Europe (ECA) -";"3100.000 - 3300.000 MHz";"Radiolocation/Earth Exploration-Satellite (active)/Space Research (active)";"Radiolocation (civil)/Active sensors (satellite)/Radiolocation (military)/UWB applications/Radio astronomy" +"- Europe (ECA) -";"3300.000 - 3400.000 MHz";"Radiolocation";"Radio astronomy/Radiolocation (military)/Radiolocation (civil)/UWB applications" +"- Europe (ECA) -";"3400.000 - 3600.000 MHz";"Fixed/Fixed-Satellite (space-to-Earth)/Amateur/Radiolocation/Mobile except aeronautical mobile";"Amateur/FSS Earth stations/MFCN/PMSE/Radiolocation (civil)/Radiolocation (military)/UWB applications/BWA" +"- Europe (ECA) -";"3600.000 - 4200.000 MHz";"Mobile/Fixed/Fixed-Satellite (space-to-Earth)";"-/BWA/FSS Earth stations/Fixed/UWB applications/MFCN/ESV" +"- Europe (ECA) -";"4200.000 - 4400.000 MHz";"Aeronautical Radionavigation/Aeronautical Mobile (R)";"WAIC/Aeronautical military systems/UWB applications/Altimeters/Passive sensors (satellite)" +"- Europe (ECA) -";"4400.000 - 4500.000 MHz";"Fixed/Mobile";"PMSE/UWB applications/Aeronautical military systems/Land military systems/Maritime military systems/Telemetry/Telecommand (military)" +"- Europe (ECA) -";"4500.000 - 4800.000 MHz";"Fixed/Fixed-Satellite (space-to-Earth)/Mobile";"Land military systems/Maritime military systems/Telemetry/Telecommand (military)/Aeronautical military systems/UWB applications/Radiodetermination applications/FSS Earth stations/PMSE" +"- Europe (ECA) -";"4800.000 - 4990.000 MHz";"Fixed/Radio Astronomy/Mobile";"PMSE/Passive sensors (satellite)/Radio astronomy/Radiodetermination applications/BBDR/Aeronautical military systems/Land military systems/Telemetry/Telecommand (military)/Maritime military systems" +"- Europe (ECA) -";"4990.000 - 5000.000 MHz";"Fixed/Mobile except aeronautical mobile/Radio Astronomy";"Telemetry/Telecommand (military)/Land military systems/Maritime military systems/Aeronautical military systems/Radiodetermination applications/PMSE/Radio astronomy" +"- Europe (ECA) -";"5000.000 - 5010.000 MHz";"Aeronautical Radionavigation/Radionavigation-Satellite (Earth-to-space)/Radio Astronomy/Space Research (passive)/Aeronautical Mobile-Satellite (R)";"Radio astronomy/Satellite navigation systems/GALILEO/Radiodetermination applications" +"- Europe (ECA) -";"5010.000 - 5030.000 MHz";"Aeronautical Mobile-Satellite (R)/Aeronautical Radionavigation/Radionavigation-Satellite (space-to-Earth) (space-to-space)/Radio Astronomy/Space Research (passive)";"Radiodetermination applications/GALILEO/Radio astronomy/Satellite navigation systems" +"- Europe (ECA) -";"5030.000 - 5091.000 MHz";"Aeronautical Radionavigation/Aeronautical Mobile-Satellite (R)/Aeronautical Mobile (R)";"MLS/Radiodetermination applications" +"- Europe (ECA) -";"5091.000 - 5150.000 MHz";"Aeronautical Mobile-Satellite (R)/Aeronautical Radionavigation/Fixed-Satellite (Earth-to-space)/Aeronautical Mobile";"Radiodetermination applications/-" +"- Europe (ECA) -";"5150.000 - 5250.000 MHz";"Aeronautical Radionavigation/Fixed-Satellite (Earth-to-space)/Mobile except aeronautical mobile";"Radiodetermination applications/BBDR/Aeronautical telemetry/Feeder links/Radio LANs" +"- Europe (ECA) -";"5250.000 - 5255.000 MHz";"Earth Exploration-Satellite (active)/Mobile except aeronautical mobile/Radiolocation/Space Research";"Active sensors (satellite)/Radiodetermination applications/Maritime radar/Weather radar/Radio LANs/-/Radiolocation (military)" +"- Europe (ECA) -";"5255.000 - 5350.000 MHz";"Earth Exploration-Satellite (active)/Mobile except aeronautical mobile/Radiolocation/Space Research (active)";"Radiolocation (military)/-/Radiodetermination applications/Radio LANs/Active sensors (satellite)/Maritime radar/Weather radar" +"- Europe (ECA) -";"5350.000 - 5460.000 MHz";"Aeronautical Radionavigation/Earth Exploration-Satellite (active)/Radiolocation/Space Research (active)";"Active sensors (satellite)/Maritime radar/Weather radar/Radiodetermination applications/-/Radiolocation (military)" +"- Europe (ECA) -";"5460.000 - 5470.000 MHz";"Earth Exploration-Satellite (active)/Radiolocation/Radionavigation/Space Research (active)";"Radiolocation (military)/-/Radiodetermination applications/Active sensors (satellite)/Maritime radar/Weather radar" +"- Europe (ECA) -";"5470.000 - 5570.000 MHz";"Earth Exploration-Satellite (active)/Maritime Radionavigation/Mobile except aeronautical mobile/Radiolocation/Space Research (active)";"Active sensors (satellite)/-/Maritime radar/Weather radar/Radio LANs/Radiodetermination applications/Radiolocation (military)" +"- Europe (ECA) -";"5570.000 - 5650.000 MHz";"Maritime Radionavigation/Mobile except aeronautical mobile/Radiolocation";"Radiolocation (military)/Radiodetermination applications/-/Maritime radar/Radio LANs/Weather radar" +"- Europe (ECA) -";"5650.000 - 5725.000 MHz";"Mobile except aeronautical mobile/Radiolocation/Amateur/Amateur-Satellite (Earth-to-space)";"Amateur/-/Maritime radar/Weather radar/Radio LANs/Radiodetermination applications/Radiolocation (military)/Amateur-satellite" +"- Europe (ECA) -";"5725.000 - 5830.000 MHz";"Radiolocation/Amateur/Mobile/Fixed-Satellite (Earth-to-space)/Fixed";"WIA/Radiolocation (military)/Radiodetermination applications/BFWA/Amateur/ISM/Non-specific SRDs/TTT/Weather radar" +"- Europe (ECA) -";"5830.000 - 5850.000 MHz";"Fixed/Fixed-Satellite (Earth-to-space)/Radiolocation/Amateur/Amateur-Satellite (space-to-Earth)/Mobile";"ISM/Non-specific SRDs/Weather radar/Radiodetermination applications/Amateur-satellite/Radiolocation (military)/WIA/Amateur/BFWA" +"- Europe (ECA) -";"5850.000 - 5925.000 MHz";"Fixed/Mobile/Fixed-Satellite (Earth-to-space)";"MBR/WIA/DA2GC/Radiodetermination applications/BFWA/ITS/FSS Earth stations/ISM/Non-specific SRDs" +"- Europe (ECA) -";"5925.000 - 6700.000 MHz";"Fixed/Fixed-Satellite (Earth-to-space)/Earth Exploration-Satellite (passive)";"Passive sensors (satellite)/Fixed/FSS Earth stations/Radiodetermination applications/UWB applications/-/ESV/Radio astronomy" +"- Europe (ECA) -";"6700.000 - 7075.000 MHz";"Fixed/Earth Exploration-Satellite (passive)/Fixed-Satellite (Earth-to-space) (space-to-Earth)";"PMSE/Radiodetermination applications/UWB applications/Passive sensors (satellite)/Feeder links/Fixed/FSS Earth stations" +"- Europe (ECA) -";"7075.000 - 7145.000 MHz";"Fixed/Earth Exploration-Satellite (passive)";"Passive sensors (satellite)/Fixed/UWB applications/Radiodetermination applications/PMSE" +"- Europe (ECA) -";"7145.000 - 7190.000 MHz";"Fixed/Mobile/Space Research (deep space) (Earth-to-space)/Space Operation (Earth-to-space)";"PMSE/Fixed/UWB applications/Radiodetermination applications" +"- Europe (ECA) -";"7190.000 - 7235.000 MHz";"Earth Exploration-Satellite (Earth-to-space)/Fixed/Space Research (Earth-to-space)/Mobile";"Fixed/Passive sensors (satellite)/UWB applications/Radiodetermination applications/PMSE" +"- Europe (ECA) -";"7235.000 - 7250.000 MHz";"Fixed/Earth Exploration-Satellite (Earth-to-space)/Space Research (Earth-to-space)";"PMSE/UWB applications/Radiodetermination applications/Passive sensors (satellite)/Fixed" +"- Europe (ECA) -";"7250.000 - 7300.000 MHz";"Fixed/Fixed-Satellite (space-to-Earth)/Mobile";"Fixed/MSS Earth stations/Radiodetermination applications/UWB applications/PMSE/Satellite systems (military)/Land military systems" +"- Europe (ECA) -";"7300.000 - 7375.000 MHz";"Fixed/Fixed-Satellite (space-to-Earth)/Mobile except aeronautical mobile";"Land military systems/Fixed/MSS Earth stations/UWB applications/Radiodetermination applications/Satellite systems (military)/PMSE" +"- Europe (ECA) -";"7375.000 - 7450.000 MHz";"Fixed/Fixed-Satellite (space-to-Earth)/Mobile except aeronautical mobile/Maritime Mobile-Satellite (space-to-Earth)";"PMSE/Satellite systems (military)/Land military systems/Fixed/MSS Earth stations/UWB applications/Radiodetermination applications" +"- Europe (ECA) -";"7450.000 - 7550.000 MHz";"Maritime Mobile-Satellite (space-to-Earth)/Fixed/Fixed-Satellite (space-to-Earth)/Meteorological-Satellite (space-to-Earth)/Mobile except aeronautical mobile";"PMSE/Satellite systems (military)/Land military systems/UWB applications/Radiodetermination applications/Fixed/Weather satellites" +"- Europe (ECA) -";"7550.000 - 7750.000 MHz";"Fixed/Fixed-Satellite (space-to-Earth)/Mobile except aeronautical mobile/Maritime Mobile-Satellite (space-to-Earth)";"Fixed/Radiodetermination applications/UWB applications/Land military systems/Satellite systems (military)/PMSE" +"- Europe (ECA) -";"7750.000 - 7900.000 MHz";"Fixed/Meteorological-Satellite (space-to-Earth)/Mobile except aeronautical mobile";"PMSE/UWB applications/Radiodetermination applications/Fixed/Weather satellites" +"- Europe (ECA) -";"7900.000 - 8025.000 MHz";"Fixed/Mobile/Fixed-Satellite (Earth-to-space)";"Fixed/MSS Earth stations/Radiodetermination applications/UWB applications/PMSE/Land military systems/Satellite systems (military)" +"- Europe (ECA) -";"8025.000 - 8175.000 MHz";"Fixed-Satellite (Earth-to-space)/Earth Exploration-Satellite (space-to-Earth)/Fixed/Mobile";"Land military systems/PMSE/Satellite systems (military)/UWB applications/Radiodetermination applications/Earth exploration-satellite/Fixed/Land mobile" +"- Europe (ECA) -";"8175.000 - 8215.000 MHz";"Earth Exploration-Satellite (space-to-Earth)/Fixed/Meteorological-Satellite (Earth-to-space)/Mobile/Fixed-Satellite (Earth-to-space)";"Earth exploration-satellite/Fixed/Land mobile/Radiodetermination applications/UWB applications/Land military systems/Satellite systems (military)/PMSE" +"- Europe (ECA) -";"8215.000 - 8400.000 MHz";"Fixed-Satellite (Earth-to-space)/Earth Exploration-Satellite (space-to-Earth)/Fixed";"PMSE/Land military systems/Satellite systems (military)/UWB applications/Radiodetermination applications/Earth exploration-satellite/Fixed/Radio astronomy" +"- Europe (ECA) -";"8400.000 - 8500.000 MHz";"Fixed/Space Research (space-to-Earth)/Radiolocation";"Fixed/Space research/Radiodetermination applications/UWB applications/PMSE" +"- Europe (ECA) -";"8500.000 - 8550.000 MHz";"Radiolocation";"Aeronautical military systems/Radiolocation (military)/UWB applications/Radiodetermination applications/Aeronautical navigation/Radiolocation (civil)" +"- Europe (ECA) -";"8550.000 - 8650.000 MHz";"Earth Exploration-Satellite (active)/Radiolocation/Space Research (active)";"Active sensors (satellite)/Aeronautical navigation/Radiolocation (civil)/Radiodetermination applications/UWB applications/Radiolocation (military)/Aeronautical military systems" +"- Europe (ECA) -";"8650.000 - 8750.000 MHz";"Radiolocation";"Aeronautical military systems/Radiolocation (military)/UWB applications/Radiodetermination applications/Aeronautical navigation/Radiolocation (civil)" +"- Europe (ECA) -";"8750.000 - 8850.000 MHz";"Aeronautical Radionavigation/Radiolocation/Space Research";"Aeronautical navigation/Radiolocation (civil)/Radiodetermination applications/UWB applications/Radiolocation (military)/Aeronautical military systems" +"- Europe (ECA) -";"8850.000 - 9000.000 MHz";"Maritime Radionavigation/Radiolocation/Space Research";"Aeronautical military systems/Radiolocation (military)/UWB applications/Radiodetermination applications/Aeronautical navigation/Radiolocation (civil)" +"- Europe (ECA) -";"9000.000 - 9200.000 MHz";"Aeronautical Radionavigation/Radiolocation/Space Research";"Aeronautical navigation/Radiolocation (civil)/Radiodetermination applications/Radiolocation (military)/Aeronautical military systems" +"- Europe (ECA) -";"9200.000 - 9300.000 MHz";"Maritime Radionavigation/Radiolocation/Space Research/Earth Exploration-Satellite (active)";"Aeronautical military systems/Synthetic aperture radar/Radiolocation (military)/Radiodetermination applications/Aeronautical navigation/Radiolocation (civil)" +"- Europe (ECA) -";"9300.000 - 9500.000 MHz";"Radionavigation/Radiolocation/Space Research (active)/Earth Exploration-Satellite (active)";"Aeronautical navigation/Weather radar/Radiodetermination applications/Radiolocation (civil)/Radiolocation (military)/Aeronautical military systems/Satellite systems (military)" +"- Europe (ECA) -";"9500.000 - 9800.000 MHz";"Earth Exploration-Satellite (active)/Radiolocation/Space Research (active)";"Aeronautical military systems/Satellite systems (military)/Radiolocation (military)/Radiodetermination applications/Active sensors (satellite)/Aeronautical navigation/Radiolocation (civil)" +"- Europe (ECA) -";"9800.000 - 9900.000 MHz";"Radiolocation/Space Research (active)/Earth Exploration-Satellite (active)";"Aeronautical navigation/Radiolocation (civil)/Radiodetermination applications/Radiolocation (military)/Aeronautical military systems/Satellite systems (military)" +"- Europe (ECA) -";"9900.000 - 10000.000 MHz";"Radiolocation/Fixed/Earth Exploration-Satellite (active)";"Synthetic aperture radar/Aeronautical military systems/Satellite systems (military)/Radiolocation (military)/Radiodetermination applications/Aeronautical navigation/Radiolocation (civil)" +"- Europe (ECA) -";"10000.000 - 10400.000 MHz";"Fixed/Mobile/Radiolocation/Amateur/Earth Exploration-Satellite (active)";"Synthetic aperture radar/Amateur/Radiolocation (military)/Land military systems/Aeronautical military systems/Maritime military systems/PMSE/Radiodetermination applications/Fixed/Radiolocation (civil)" \ No newline at end of file diff --git a/src/hackrf-sweep/src-java/shared/mvc/MVCController.java b/src/hackrf-sweep/src-java/shared/mvc/MVCController.java new file mode 100644 index 0000000..f46dab1 --- /dev/null +++ b/src/hackrf-sweep/src-java/shared/mvc/MVCController.java @@ -0,0 +1,139 @@ +package shared.mvc; + +import java.util.function.Consumer; +import java.util.function.Function; + +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JSlider; +import javax.swing.JSpinner; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListDataListener; + +import shared.mvc.ModelValue.ModelValueBoolean; +import shared.mvc.ModelValue.ModelValueInt; + +/** + * In MVC model, this class is the Controller, listening for change events from + * both the model and the view and ensuring their states are synchronized. + * + * @param + */ +public class MVCController { + public static interface ViewAddChangeListener { + public void addChangeListener(Consumer viewValueChangedCall); + } + + /** + * Flag indicating the view is being updated to prevent its listeners from + * changing the {@link ModelValue} causing an infinite loop. + */ + protected boolean disableViewListeners = false; + + public MVCController(ViewAddChangeListener viewAddChangeListener, Consumer viewSetValue, + ModelValue model) { + this(viewAddChangeListener, viewSetValue, model, (T val) -> val, (T val) -> val); + } + + /** + * Generic ({@link JComponent} independent) constructor + * + * @param viewAddChangeListener + * @param viewSetValue + * @param model + * @param conversionViewToModel + * @param conversionModelToView + */ + public MVCController(ViewAddChangeListener viewAddChangeListener, + Consumer viewSetValue, ModelValue model, Function conversionViewToModel, + Function conversionModelToView) { + viewAddChangeListener.addChangeListener((VIEW_T newViewValue) -> updateModelValue( + () -> model.setValue(conversionViewToModel.apply(newViewValue)))); + model.addListener(() -> updateView(() -> viewSetValue.accept(conversionModelToView.apply(model.getValue())))); + sync(model); + } + + public MVCController(JCheckBox checkbox, ModelValueBoolean model) { + checkbox.addChangeListener((ChangeEvent e) -> updateModelValue(() -> model.setValue(checkbox.isSelected()))); + model.addListener(() -> updateView(() -> checkbox.setSelected(model.getValue()))); + sync(model); + } + + public MVCController(JSlider slider, ModelValueInt model) { + this(slider, model, val -> val, val -> val); + } + + public MVCController(JSlider slider, ModelValue model, Function conversionViewToModel, + Function conversionModelToView) { + slider.addChangeListener((ChangeEvent e) -> updateModelValue( + () -> model.setValue(conversionViewToModel.apply(slider.getValue())))); + model.addListener(() -> updateView(() -> slider.setValue(conversionModelToView.apply(model.getValue())))); + sync(model); + } + + public MVCController(JSpinner spinner, ModelValue model, Function conversionViewToModel, + Function conversionModelToView) { + spinner.addChangeListener((ChangeEvent e) -> updateModelValue( + () -> model.setValue(conversionViewToModel.apply(spinner.getValue())))); + model.addListener(() -> updateView(() -> spinner.setValue(conversionModelToView.apply(model.getValue())))); + sync(model); + } + + public MVCController(JComboBox comboBox, ModelValue model, Function conversionViewToModel, + Function conversionModelToView) { + comboBox.addActionListener(e -> updateModelValue(() -> model.setValue(conversionViewToModel.apply(comboBox.getSelectedItem())))); + model.addListener(() -> updateView(() -> comboBox.setSelectedItem(conversionModelToView.apply(model.getValue())))); + sync(model); + } + + public MVCController(JComboBox comboBox, ModelValue model) { + this(comboBox, model, val -> (T)val, val -> val); + } + + /** + * syncs values with the component + */ + protected void sync(ModelValue val) { + val.callObservers(); + } + + /** + * Makes sure the when the model's value is updated, the view's listeners + * are not triggered, causing an infinite loop. + * + * @param viewChangedCall + */ + protected void updateView(Runnable viewChangedCall) { + /** + * must be run in swing's event loop thread. + */ + SwingUtilities.invokeLater(() -> { + disableViewListeners = true; + try { + viewChangedCall.run(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + disableViewListeners = false; + } + }); + } + + protected void _updateModelValueCall(Runnable updateModelValueCall) { + if (!disableViewListeners) + updateModelValueCall.run(); + } + + protected void updateModelValue(Runnable updateModelValueCall) { + if (!SwingUtilities.isEventDispatchThread()) { + SwingUtilities.invokeLater(() -> { + _updateModelValueCall(updateModelValueCall); + }); + } else { + _updateModelValueCall(updateModelValueCall); + } + } + +} \ No newline at end of file diff --git a/src/hackrf-sweep/src-java/shared/mvc/ModelValue.java b/src/hackrf-sweep/src-java/shared/mvc/ModelValue.java new file mode 100644 index 0000000..9ffd1e1 --- /dev/null +++ b/src/hackrf-sweep/src-java/shared/mvc/ModelValue.java @@ -0,0 +1,105 @@ +package shared.mvc; + +import java.util.Observable; +import java.util.Observer; +import java.util.function.Consumer; + +/** + * Class to represent arbitrary value with notification to observers on value change. + * Used in MVC as a model. + * + * @param + */ +public class ModelValue extends Observable{ + + public static class ModelValueInt extends ModelValue{ + protected final boolean isBounded; + protected final int min, max, step; + public ModelValueInt(String name, int initialValue) { + super(name, initialValue); + isBounded = false; + step = min = max = 0; + } + + public ModelValueInt(String name, int initialValue, int step, int min, int max) { + super(name, initialValue); + isBounded = true; + this.min = min; + this.max = max; + this.step = step; + } + + public int getMax() { + return max; + } + + public int getMin() { + return min; + } + + public int getStep() { + return step; + } + + @Override + public void setValue(Integer value) { + if (isBounded() && (value < min || value > max)) + throw new IllegalStateException("New value of '"+name+"' '"+value+"' is outside of bounds <"+min+", "+max+">"); + super.setValue(value); + } + + public boolean isBounded() { + return isBounded; + } + } + + public static class ModelValueBoolean extends ModelValue{ + public ModelValueBoolean(String name, Boolean initialValue) { + super(name, initialValue); + } + } + + private T value; + protected final String name; + public ModelValue(String name, T initialValue) { + this.value = initialValue; + this.name = name; + } + + + public void addListener(Consumer listener) { + addObserver((Observer) (o, arg) -> listener.accept(getValue())); + } + + public void addListener(Runnable listener) { + addObserver((Observer) (o, arg) -> listener.run()); + } + + + + public void setValue(T value) { + if (value == null || this.value == null) { + if (value == this.value) + return; + } + else if (this.value.equals(value)) { + return; + } + this.value = value; + + callObservers(); + } + + public void callObservers() { + setChanged(); + notifyObservers(); + } + + public T getValue() { + return value; + } + + @Override public String toString() { + return name; + } +} \ No newline at end of file