diff --git a/plugins/channelrx/CMakeLists.txt b/plugins/channelrx/CMakeLists.txt
index 3f98bb1b2..86d6bc75e 100644
--- a/plugins/channelrx/CMakeLists.txt
+++ b/plugins/channelrx/CMakeLists.txt
@@ -11,6 +11,7 @@ add_subdirectory(demodwfm)
add_subdirectory(chanalyzer)
add_subdirectory(chanalyzerng)
add_subdirectory(demodatv)
+add_subdirectory(demoddatv)
if(LIBDSDCC_FOUND AND LIBMBE_FOUND)
add_subdirectory(demoddsd)
diff --git a/plugins/channelrx/demoddatv/.qmake.stash b/plugins/channelrx/demoddatv/.qmake.stash
new file mode 100644
index 000000000..c6486d18c
--- /dev/null
+++ b/plugins/channelrx/demoddatv/.qmake.stash
@@ -0,0 +1,15 @@
+QMAKE_DEFAULT_INCDIRS = \
+ /usr/include/c++/6 \
+ /usr/include/x86_64-linux-gnu/c++/6 \
+ /usr/include/c++/6/backward \
+ /usr/lib/gcc/x86_64-linux-gnu/6/include \
+ /usr/local/include \
+ /usr/lib/gcc/x86_64-linux-gnu/6/include-fixed \
+ /usr/include/x86_64-linux-gnu \
+ /usr/include
+QMAKE_DEFAULT_LIBDIRS = \
+ /usr/lib/gcc/x86_64-linux-gnu/6 \
+ /usr/lib/x86_64-linux-gnu \
+ /usr/lib \
+ /lib/x86_64-linux-gnu \
+ /lib
diff --git a/plugins/channelrx/demoddatv/CMakeLists.txt b/plugins/channelrx/demoddatv/CMakeLists.txt
new file mode 100644
index 000000000..3135612ef
--- /dev/null
+++ b/plugins/channelrx/demoddatv/CMakeLists.txt
@@ -0,0 +1,59 @@
+project(datv)
+
+set(datv_SOURCES
+ datvdemod.cpp
+ datvdemodgui.cpp
+ datvdemodplugin.cpp
+ datvscreen.cpp
+ glshaderarray.cpp
+ datvideostream.cpp
+ datvideorender.cpp
+)
+
+set(datv_HEADERS
+ datvdemod.h
+ datvdemodgui.h
+ datvdemodplugin.h
+ datvscreen.h
+ glshaderarray.h
+ datvideostream.h
+ datvideorender.h
+)
+
+set(datv_FORMS
+ datvdemodgui.ui
+)
+
+set (CMAKE_CXX_FLAGS "-Wno-unused-variable -Wno-deprecated-declarations")
+
+include_directories(
+ .
+ ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+#include(${QT_USE_FILE})
+add_definitions(${QT_DEFINITIONS})
+add_definitions(-DQT_PLUGIN)
+add_definitions(-DQT_SHARED)
+
+#qt5_wrap_cpp(nfm_HEADERS_MOC ${nfm_HEADERS})
+qt5_wrap_ui(datv_FORMS_HEADERS ${datv_FORMS})
+
+add_library(demoddatv SHARED
+ ${datv_SOURCES}
+ ${datv_HEADERS_MOC}
+ ${datv_FORMS_HEADERS}
+)
+
+target_link_libraries(demoddatv
+ ${QT_LIBRARIES}
+ sdrbase
+ sdrgui
+ avcodec
+ avformat
+ swscale
+)
+
+qt5_use_modules(demoddatv Core Widgets Multimedia MultimediaWidgets)
+
+install(TARGETS demoddatv DESTINATION lib/plugins/channelrx)
diff --git a/plugins/channelrx/demoddatv/Makefile b/plugins/channelrx/demoddatv/Makefile
new file mode 100644
index 000000000..0ba71e74a
--- /dev/null
+++ b/plugins/channelrx/demoddatv/Makefile
@@ -0,0 +1,454 @@
+# CMAKE generated file: DO NOT EDIT!
+# Generated by "Unix Makefiles" Generator, CMake Version 3.7
+
+# Default target executed when no arguments are given to make.
+default_target: all
+
+.PHONY : default_target
+
+# Allow only one "make -f Makefile2" at a time, but pass parallelism.
+.NOTPARALLEL:
+
+
+#=============================================================================
+# Special targets provided by cmake.
+
+# Disable implicit rules so canonical targets will work.
+.SUFFIXES:
+
+
+# Remove some rules from gmake that .SUFFIXES does not remove.
+SUFFIXES =
+
+.SUFFIXES: .hpux_make_needs_suffix_list
+
+
+# Suppress display of executed commands.
+$(VERBOSE).SILENT:
+
+
+# A target that is always out of date.
+cmake_force:
+
+.PHONY : cmake_force
+
+#=============================================================================
+# Set environment variables for the build.
+
+# The shell in which to execute make rules.
+SHELL = /bin/sh
+
+# The CMake executable.
+CMAKE_COMMAND = /usr/bin/cmake
+
+# The command to remove a file.
+RM = /usr/bin/cmake -E remove -f
+
+# Escaping for special characters.
+EQUALS = =
+
+# The top-level source directory on which CMake was run.
+CMAKE_SOURCE_DIR = /home/guest/HAM/sdrangel-master
+
+# The top-level build directory on which CMake was run.
+CMAKE_BINARY_DIR = /home/guest/HAM/sdrangel-master
+
+#=============================================================================
+# Targets provided globally by CMake.
+
+# Special rule for the target install/strip
+install/strip: preinstall
+ @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..."
+ /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake
+.PHONY : install/strip
+
+# Special rule for the target install/strip
+install/strip/fast: install/strip
+
+.PHONY : install/strip/fast
+
+# Special rule for the target install/local
+install/local: preinstall
+ @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..."
+ /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake
+.PHONY : install/local
+
+# Special rule for the target install/local
+install/local/fast: install/local
+
+.PHONY : install/local/fast
+
+# Special rule for the target edit_cache
+edit_cache:
+ @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..."
+ /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available.
+.PHONY : edit_cache
+
+# Special rule for the target edit_cache
+edit_cache/fast: edit_cache
+
+.PHONY : edit_cache/fast
+
+# Special rule for the target rebuild_cache
+rebuild_cache:
+ @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..."
+ /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
+.PHONY : rebuild_cache
+
+# Special rule for the target rebuild_cache
+rebuild_cache/fast: rebuild_cache
+
+.PHONY : rebuild_cache/fast
+
+# Special rule for the target install
+install: preinstall
+ @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..."
+ /usr/bin/cmake -P cmake_install.cmake
+.PHONY : install
+
+# Special rule for the target install
+install/fast: preinstall/fast
+ @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..."
+ /usr/bin/cmake -P cmake_install.cmake
+.PHONY : install/fast
+
+# Special rule for the target list_install_components
+list_install_components:
+ @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"Unspecified\""
+.PHONY : list_install_components
+
+# Special rule for the target list_install_components
+list_install_components/fast: list_install_components
+
+.PHONY : list_install_components/fast
+
+# The main all target
+all: cmake_check_build_system
+ cd /home/guest/HAM/sdrangel-master && $(CMAKE_COMMAND) -E cmake_progress_start /home/guest/HAM/sdrangel-master/CMakeFiles /home/guest/HAM/sdrangel-master/plugins/channelrx/demoddatv/CMakeFiles/progress.marks
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f CMakeFiles/Makefile2 plugins/channelrx/demoddatv/all
+ $(CMAKE_COMMAND) -E cmake_progress_start /home/guest/HAM/sdrangel-master/CMakeFiles 0
+.PHONY : all
+
+# The main clean target
+clean:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f CMakeFiles/Makefile2 plugins/channelrx/demoddatv/clean
+.PHONY : clean
+
+# The main clean target
+clean/fast: clean
+
+.PHONY : clean/fast
+
+# Prepare targets for installation.
+preinstall: all
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f CMakeFiles/Makefile2 plugins/channelrx/demoddatv/preinstall
+.PHONY : preinstall
+
+# Prepare targets for installation.
+preinstall/fast:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f CMakeFiles/Makefile2 plugins/channelrx/demoddatv/preinstall
+.PHONY : preinstall/fast
+
+# clear depends
+depend:
+ cd /home/guest/HAM/sdrangel-master && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1
+.PHONY : depend
+
+# Convenience name for target.
+plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/rule:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f CMakeFiles/Makefile2 plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/rule
+.PHONY : plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/rule
+
+# Convenience name for target.
+demoddatv: plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/rule
+
+.PHONY : demoddatv
+
+# fast build rule for target.
+demoddatv/fast:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build
+.PHONY : demoddatv/fast
+
+# Convenience name for target.
+plugins/channelrx/demoddatv/CMakeFiles/demoddatv_automoc.dir/rule:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f CMakeFiles/Makefile2 plugins/channelrx/demoddatv/CMakeFiles/demoddatv_automoc.dir/rule
+.PHONY : plugins/channelrx/demoddatv/CMakeFiles/demoddatv_automoc.dir/rule
+
+# Convenience name for target.
+demoddatv_automoc: plugins/channelrx/demoddatv/CMakeFiles/demoddatv_automoc.dir/rule
+
+.PHONY : demoddatv_automoc
+
+# fast build rule for target.
+demoddatv_automoc/fast:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv_automoc.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv_automoc.dir/build
+.PHONY : demoddatv_automoc/fast
+
+datvdemod.o: datvdemod.cpp.o
+
+.PHONY : datvdemod.o
+
+# target to build an object file
+datvdemod.cpp.o:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvdemod.cpp.o
+.PHONY : datvdemod.cpp.o
+
+datvdemod.i: datvdemod.cpp.i
+
+.PHONY : datvdemod.i
+
+# target to preprocess a source file
+datvdemod.cpp.i:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvdemod.cpp.i
+.PHONY : datvdemod.cpp.i
+
+datvdemod.s: datvdemod.cpp.s
+
+.PHONY : datvdemod.s
+
+# target to generate assembly for a file
+datvdemod.cpp.s:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvdemod.cpp.s
+.PHONY : datvdemod.cpp.s
+
+datvdemodgui.o: datvdemodgui.cpp.o
+
+.PHONY : datvdemodgui.o
+
+# target to build an object file
+datvdemodgui.cpp.o:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvdemodgui.cpp.o
+.PHONY : datvdemodgui.cpp.o
+
+datvdemodgui.i: datvdemodgui.cpp.i
+
+.PHONY : datvdemodgui.i
+
+# target to preprocess a source file
+datvdemodgui.cpp.i:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvdemodgui.cpp.i
+.PHONY : datvdemodgui.cpp.i
+
+datvdemodgui.s: datvdemodgui.cpp.s
+
+.PHONY : datvdemodgui.s
+
+# target to generate assembly for a file
+datvdemodgui.cpp.s:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvdemodgui.cpp.s
+.PHONY : datvdemodgui.cpp.s
+
+datvdemodplugin.o: datvdemodplugin.cpp.o
+
+.PHONY : datvdemodplugin.o
+
+# target to build an object file
+datvdemodplugin.cpp.o:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvdemodplugin.cpp.o
+.PHONY : datvdemodplugin.cpp.o
+
+datvdemodplugin.i: datvdemodplugin.cpp.i
+
+.PHONY : datvdemodplugin.i
+
+# target to preprocess a source file
+datvdemodplugin.cpp.i:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvdemodplugin.cpp.i
+.PHONY : datvdemodplugin.cpp.i
+
+datvdemodplugin.s: datvdemodplugin.cpp.s
+
+.PHONY : datvdemodplugin.s
+
+# target to generate assembly for a file
+datvdemodplugin.cpp.s:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvdemodplugin.cpp.s
+.PHONY : datvdemodplugin.cpp.s
+
+datvideorender.o: datvideorender.cpp.o
+
+.PHONY : datvideorender.o
+
+# target to build an object file
+datvideorender.cpp.o:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvideorender.cpp.o
+.PHONY : datvideorender.cpp.o
+
+datvideorender.i: datvideorender.cpp.i
+
+.PHONY : datvideorender.i
+
+# target to preprocess a source file
+datvideorender.cpp.i:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvideorender.cpp.i
+.PHONY : datvideorender.cpp.i
+
+datvideorender.s: datvideorender.cpp.s
+
+.PHONY : datvideorender.s
+
+# target to generate assembly for a file
+datvideorender.cpp.s:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvideorender.cpp.s
+.PHONY : datvideorender.cpp.s
+
+datvideostream.o: datvideostream.cpp.o
+
+.PHONY : datvideostream.o
+
+# target to build an object file
+datvideostream.cpp.o:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvideostream.cpp.o
+.PHONY : datvideostream.cpp.o
+
+datvideostream.i: datvideostream.cpp.i
+
+.PHONY : datvideostream.i
+
+# target to preprocess a source file
+datvideostream.cpp.i:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvideostream.cpp.i
+.PHONY : datvideostream.cpp.i
+
+datvideostream.s: datvideostream.cpp.s
+
+.PHONY : datvideostream.s
+
+# target to generate assembly for a file
+datvideostream.cpp.s:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvideostream.cpp.s
+.PHONY : datvideostream.cpp.s
+
+datvscreen.o: datvscreen.cpp.o
+
+.PHONY : datvscreen.o
+
+# target to build an object file
+datvscreen.cpp.o:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvscreen.cpp.o
+.PHONY : datvscreen.cpp.o
+
+datvscreen.i: datvscreen.cpp.i
+
+.PHONY : datvscreen.i
+
+# target to preprocess a source file
+datvscreen.cpp.i:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvscreen.cpp.i
+.PHONY : datvscreen.cpp.i
+
+datvscreen.s: datvscreen.cpp.s
+
+.PHONY : datvscreen.s
+
+# target to generate assembly for a file
+datvscreen.cpp.s:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/datvscreen.cpp.s
+.PHONY : datvscreen.cpp.s
+
+demoddatv_automoc.o: demoddatv_automoc.cpp.o
+
+.PHONY : demoddatv_automoc.o
+
+# target to build an object file
+demoddatv_automoc.cpp.o:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/demoddatv_automoc.cpp.o
+.PHONY : demoddatv_automoc.cpp.o
+
+demoddatv_automoc.i: demoddatv_automoc.cpp.i
+
+.PHONY : demoddatv_automoc.i
+
+# target to preprocess a source file
+demoddatv_automoc.cpp.i:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/demoddatv_automoc.cpp.i
+.PHONY : demoddatv_automoc.cpp.i
+
+demoddatv_automoc.s: demoddatv_automoc.cpp.s
+
+.PHONY : demoddatv_automoc.s
+
+# target to generate assembly for a file
+demoddatv_automoc.cpp.s:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/demoddatv_automoc.cpp.s
+.PHONY : demoddatv_automoc.cpp.s
+
+glshaderarray.o: glshaderarray.cpp.o
+
+.PHONY : glshaderarray.o
+
+# target to build an object file
+glshaderarray.cpp.o:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/glshaderarray.cpp.o
+.PHONY : glshaderarray.cpp.o
+
+glshaderarray.i: glshaderarray.cpp.i
+
+.PHONY : glshaderarray.i
+
+# target to preprocess a source file
+glshaderarray.cpp.i:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/glshaderarray.cpp.i
+.PHONY : glshaderarray.cpp.i
+
+glshaderarray.s: glshaderarray.cpp.s
+
+.PHONY : glshaderarray.s
+
+# target to generate assembly for a file
+glshaderarray.cpp.s:
+ cd /home/guest/HAM/sdrangel-master && $(MAKE) -f plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/build.make plugins/channelrx/demoddatv/CMakeFiles/demoddatv.dir/glshaderarray.cpp.s
+.PHONY : glshaderarray.cpp.s
+
+# Help Target
+help:
+ @echo "The following are some of the valid targets for this Makefile:"
+ @echo "... all (the default if no target is provided)"
+ @echo "... clean"
+ @echo "... depend"
+ @echo "... install/strip"
+ @echo "... install/local"
+ @echo "... demoddatv"
+ @echo "... edit_cache"
+ @echo "... rebuild_cache"
+ @echo "... install"
+ @echo "... list_install_components"
+ @echo "... demoddatv_automoc"
+ @echo "... datvdemod.o"
+ @echo "... datvdemod.i"
+ @echo "... datvdemod.s"
+ @echo "... datvdemodgui.o"
+ @echo "... datvdemodgui.i"
+ @echo "... datvdemodgui.s"
+ @echo "... datvdemodplugin.o"
+ @echo "... datvdemodplugin.i"
+ @echo "... datvdemodplugin.s"
+ @echo "... datvideorender.o"
+ @echo "... datvideorender.i"
+ @echo "... datvideorender.s"
+ @echo "... datvideostream.o"
+ @echo "... datvideostream.i"
+ @echo "... datvideostream.s"
+ @echo "... datvscreen.o"
+ @echo "... datvscreen.i"
+ @echo "... datvscreen.s"
+ @echo "... demoddatv_automoc.o"
+ @echo "... demoddatv_automoc.i"
+ @echo "... demoddatv_automoc.s"
+ @echo "... glshaderarray.o"
+ @echo "... glshaderarray.i"
+ @echo "... glshaderarray.s"
+.PHONY : help
+
+
+
+#=============================================================================
+# Special targets to cleanup operation of make.
+
+# Special rule to run CMake to check the build system integrity.
+# No rule that depends on this can have commands that come from listfiles
+# because they might be regenerated.
+cmake_check_build_system:
+ cd /home/guest/HAM/sdrangel-master && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
+.PHONY : cmake_check_build_system
+
diff --git a/plugins/channelrx/demoddatv/datvconstellation.h b/plugins/channelrx/demoddatv/datvconstellation.h
new file mode 100644
index 000000000..9f95a7a15
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvconstellation.h
@@ -0,0 +1,128 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// using LeanSDR Framework (C) 2016 F4DAV //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef DATVCONSTELLATION_H
+#define DATVCONSTELLATION_H
+
+#include
+
+#include "leansdr/framework.h"
+#include "datvscreen.h"
+
+namespace leansdr
+{
+
+ static const int DEFAULT_GUI_DECIMATION = 64;
+
+ template struct datvconstellation : runnable
+ {
+ T xymin, xymax;
+ unsigned long decimation;
+ unsigned long pixels_per_frame;
+ cstln_lut<256> **cstln; // Optional ptr to optional constellation
+ DATVScreen *m_objDATVScreen;
+
+
+ datvconstellation(scheduler *sch, pipebuf< complex > &_in, T _xymin, T _xymax, const char *_name=NULL, DATVScreen * objDATVScreen=NULL) :
+ runnable(sch, _name?_name:_in.name),
+ xymin(_xymin),
+ xymax(_xymax),
+ decimation(DEFAULT_GUI_DECIMATION),
+ pixels_per_frame(1024),
+ cstln(NULL),
+ in(_in),
+ phase(0),
+ m_objDATVScreen(objDATVScreen)
+ {
+
+ }
+
+ void run()
+ {
+ //Symbols
+
+ while ( in.readable() >= pixels_per_frame )
+ {
+ if ( ! phase )
+ {
+ m_objDATVScreen->resetImage();
+
+ complex *p = in.rd(), *pend = p+pixels_per_frame;
+
+ for ( ; pselectRow(256*(p->re-xymin)/(xymax-xymin));
+ m_objDATVScreen->setDataColor(256- 256*((p->im-xymin)/(xymax-xymin)),255,0,255);
+ }
+
+ }
+
+ if ( cstln && (*cstln) )
+ {
+ // Plot constellation points
+
+ for ( int i=0; i<(*cstln)->nsymbols; ++i )
+ {
+ complex *p = &(*cstln)->symbols[i];
+ int x = 256*(p->re-xymin)/(xymax-xymin);
+ int y = 256 - 256*(p->im-xymin)/(xymax-xymin);
+
+ for ( int d=-4; d<=4; ++d )
+ {
+ m_objDATVScreen->selectRow(x+d);
+ m_objDATVScreen->setDataColor(y,5,250,250);
+ m_objDATVScreen->selectRow(x);
+ m_objDATVScreen->setDataColor(y+d,5,250,250);
+ }
+ }
+ }
+
+ m_objDATVScreen->renderImage(NULL);
+
+ }
+
+ in.read(pixels_per_frame);
+
+ if ( ++phase >= decimation )
+ {
+ phase = 0;
+ }
+ }
+ }
+
+ //private:
+ pipereader< complex > in;
+ unsigned long phase;
+ //gfx g;
+
+ void draw_begin()
+ {
+ //g.clear();
+ //g.setfg(0, 255, 0);
+ //g.line(g.w/2,0, g.w/2, g.h);
+ //g.line(0,g.h/2, g.w,g.h/2);
+ }
+
+ };
+
+}
+
+#endif // DATVCONSTELLATION_H
diff --git a/plugins/channelrx/demoddatv/datvdemod.cpp b/plugins/channelrx/demoddatv/datvdemod.cpp
new file mode 100644
index 000000000..3837d0638
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvdemod.cpp
@@ -0,0 +1,1052 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// using LeanSDR Framework (C) 2016 F4DAV //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "datvdemod.h"
+
+#include
+#include
+#include
+#include
+#include "audio/audiooutput.h"
+#include "dsp/dspengine.h"
+
+#include "dsp/downchannelizer.h"
+#include "dsp/threadedbasebandsamplesink.h"
+#include "device/devicesourceapi.h"
+
+
+const QString DATVDemod::m_channelIdURI = "sdrangel.channel.demoddatv";
+const QString DATVDemod::m_channelId = "DATVDemod";
+
+MESSAGE_CLASS_DEFINITION(DATVDemod::MsgConfigureDATVDemod, Message)
+MESSAGE_CLASS_DEFINITION(DATVDemod::MsgConfigureChannelizer, Message)
+
+DATVDemod::DATVDemod(DeviceSourceAPI *deviceAPI) :
+ ChannelSinkAPI(m_channelIdURI),
+ m_deviceAPI(deviceAPI),
+ m_objSettingsMutex(QMutex::NonRecursive),
+ m_objRegisteredDATVScreen(NULL),
+ m_objVideoStream(NULL),
+ m_objRegisteredVideoRender(NULL),
+ m_objRenderThread(NULL),
+ m_enmModulation(BPSK /*DATV_FM1*/),
+ m_blnNeedConfigUpdate(false),
+ m_blnRenderingVideo(false)
+{
+ setObjectName("DATVDemod");
+
+ //*************** DATV PARAMETERS ***************
+ m_blnInitialized=false;
+ CleanUpDATVFramework(false);
+
+ m_objVideoStream = new DATVideostream();
+
+ m_objRFFilter = new fftfilt(-256000.0 / 1024000.0, 256000.0 / 1024000.0, rfFilterFftLength);
+
+ m_channelizer = new DownChannelizer(this);
+ m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this);
+ m_deviceAPI->addThreadedSink(m_threadedChannelizer);
+ m_deviceAPI->addChannelAPI(this);
+
+ connect(m_channelizer, SIGNAL(inputSampleRateChanged()), this, SLOT(channelSampleRateChanged()));
+
+}
+
+DATVDemod::~DATVDemod()
+{
+ m_blnInitialized=false;
+
+
+ if(m_objRenderThread!=NULL)
+ {
+ if(m_objRenderThread->isRunning())
+ {
+ m_objRenderThread->stopRendering();
+ }
+ }
+
+ //CleanUpDATVFramework(true);
+
+ if(m_objRFFilter!=NULL)
+ {
+ //delete m_objRFFilter;
+ }
+
+ if(m_objVideoStream!=NULL)
+ {
+ //m_objVideoStream->close();
+ //delete m_objVideoStream;
+ }
+
+ m_deviceAPI->removeChannelAPI(this);
+ m_deviceAPI->removeThreadedSink(m_threadedChannelizer);
+ delete m_threadedChannelizer;
+ delete m_channelizer;
+}
+
+bool DATVDemod::SetDATVScreen(DATVScreen *objScreen)
+{
+ m_objRegisteredDATVScreen = objScreen;
+}
+
+DATVideostream * DATVDemod::SetVideoRender(DATVideoRender *objScreen)
+{
+ m_objRegisteredVideoRender = objScreen;
+
+ m_objRenderThread = new DATVideoRenderThread(m_objRegisteredVideoRender,m_objVideoStream);
+
+ return m_objVideoStream;
+}
+
+
+bool DATVDemod::PlayVideo(bool blnStartStop)
+{
+
+ if(m_objVideoStream==NULL)
+ {
+ return false;
+ }
+
+ if(m_objRegisteredVideoRender==NULL)
+ {
+ return false;
+ }
+
+ if(m_objRenderThread==NULL)
+ {
+ return false;
+ }
+
+ if(m_objRenderThread->isRunning())
+ {
+ if(blnStartStop==true)
+ {
+ m_objRenderThread->stopRendering();
+ }
+
+ return true;
+ }
+
+ m_objRenderThread->setStreamAndRenderer(m_objRegisteredVideoRender,m_objVideoStream);
+ m_objVideoStream->MultiThreaded=true;
+ m_objRenderThread->start();
+
+ //m_objVideoStream->MultiThreaded=false;
+ //m_objRenderThread->run();
+
+ return true;
+}
+
+void DATVDemod::configure(MessageQueue* objMessageQueue,
+ int intRFBandwidth,
+ int intCenterFrequency,
+ dvb_version enmStandard,
+ DATVModulation enmModulation,
+ code_rate enmFEC,
+ int intSymbolRate,
+ int intNotchFilters,
+ bool blnAllowDrift,
+ bool blnFastLock,
+ bool blnHDLC,
+ bool blnHardMetric,
+ bool blnResample,
+ bool blnViterbi)
+{
+ Message* msgCmd = MsgConfigureDATVDemod::create(intRFBandwidth,intCenterFrequency,enmStandard, enmModulation, enmFEC, intSymbolRate, intNotchFilters, blnAllowDrift,blnFastLock,blnHDLC,blnHardMetric,blnResample, blnViterbi);
+ objMessageQueue->push(msgCmd);
+}
+
+void DATVDemod::InitDATVParameters(int intMsps,
+ int intRFBandwidth,
+ int intCenterFrequency,
+ dvb_version enmStandard,
+ DATVModulation enmModulation,
+ code_rate enmFEC,
+ int intSampleRate,
+ int intSymbolRate,
+ int intNotchFilters,
+ bool blnAllowDrift,
+ bool blnFastLock,
+ bool blnHDLC,
+ bool blnHardMetric,
+ bool blnResample,
+ bool blnViterbi)
+{
+ Real fltLowCut;
+ Real fltHiCut;
+
+ m_blnInitialized=false;
+
+ m_objSettingsMutex.lock();
+
+ //Recalibrage du filtre passe bande
+
+ fltLowCut = -((float)intRFBandwidth / 2.0) / (float)intMsps;
+ fltHiCut = ((float)intRFBandwidth / 2.0) / (float)intMsps;
+ m_objRFFilter->create_filter(fltLowCut, fltHiCut);
+ m_objNCO.setFreq(-(float)intCenterFrequency,(float)intMsps);
+
+ //Mise à jour de la config
+
+ m_objRunning.intMsps = intMsps;
+ m_objRunning.intCenterFrequency = intCenterFrequency;
+ m_objRunning.intRFBandwidth = intRFBandwidth;
+ m_objRunning.enmStandard = enmStandard;
+ m_objRunning.enmModulation = enmModulation;
+ m_objRunning.enmFEC = enmFEC;
+ m_objRunning.intSampleRate = intSampleRate;
+ m_objRunning.intSymbolRate = intSymbolRate;
+ m_objRunning.intNotchFilters = intNotchFilters;
+ m_objRunning.blnAllowDrift = blnAllowDrift;
+ m_objRunning.blnFastLock = blnFastLock;
+ m_objRunning.blnHDLC = blnHDLC;
+ m_objRunning.blnHardMetric = blnHardMetric;
+ m_objRunning.blnResample = blnResample;
+ m_objRunning.blnViterbi = blnViterbi;
+
+ qDebug() << "DATVDemod::InitDATVParameters:"
+ << " - Msps: " << intMsps
+ << " - Sample Rate: " << intSampleRate
+ << " - Symbol Rate: " << intSymbolRate
+ << " - Modulation: " << enmModulation
+ << " - Notch Filters: " << intNotchFilters
+ << " - Allow Drift: " << blnAllowDrift
+ << " - Fast Lock: " << blnFastLock
+ << " - HDLC: " << blnHDLC
+ << " - HARD METRIC: " << blnHardMetric
+ << " - Resample: " << blnResample
+ << " - Viterbi: " << blnViterbi;
+
+
+ m_objSettingsMutex.unlock();
+
+ m_blnNeedConfigUpdate=true;
+
+ m_blnInitialized=true;
+}
+
+void DATVDemod::CleanUpDATVFramework(bool blnRelease)
+{
+ //if(blnRelease==true)
+ if(false)
+ {
+ if(m_objScheduler!=NULL)
+ {
+ m_objScheduler->shutdown();
+ delete m_objScheduler;
+ }
+
+ // INPUT
+ if(p_rawiq!=NULL) delete p_rawiq;
+ if(p_rawiq_writer!=NULL) delete p_rawiq_writer;
+ if(p_preprocessed!=NULL) delete p_preprocessed;
+
+ // NOTCH FILTER
+ if(r_auto_notch!=NULL) delete r_auto_notch;
+ if(p_autonotched!=NULL) delete p_autonotched;
+
+ // FREQUENCY CORRECTION : DEROTATOR
+ if(p_derot!=NULL) delete p_derot;
+ if(r_derot!=NULL) delete r_derot;
+
+ // CNR ESTIMATION
+ if(p_cnr!=NULL) delete p_cnr;
+ if(r_cnr!=NULL) delete r_cnr;
+
+ //FILTERING
+ if(r_resample!=NULL) delete r_resample;
+ if(p_resampled!=NULL) delete p_resampled;
+ if(coeffs!=NULL) delete coeffs;
+
+ // OUTPUT PREPROCESSED DATA
+ if(sampler!=NULL) delete sampler;
+ if(coeffs_sampler!=NULL) delete coeffs_sampler;
+ if(p_symbols!=NULL) delete p_symbols;
+ if(p_freq!=NULL) delete p_freq;
+ if(p_ss!=NULL) delete p_ss;
+ if(p_mer!=NULL) delete p_mer;
+ if(p_sampled!=NULL) delete p_sampled;
+
+ //DECIMATION
+ if(p_decimated!=NULL) delete p_decimated;
+ if(p_decim!=NULL) delete p_decim;
+ if(r_ppout!=NULL) delete r_ppout;
+
+ //GENERIC CONSTELLATION RECEIVER
+ if(m_objDemodulator!=NULL) delete m_objDemodulator;
+
+ //DECONVOLUTION AND SYNCHRONIZATION
+ if(p_bytes!=NULL) delete p_bytes;
+ if(r_deconv!=NULL) delete r_deconv;
+ if(r!=NULL) delete r;
+ if(p_descrambled!=NULL) delete p_descrambled;
+ if(p_frames!=NULL) delete p_frames;
+ if(r_etr192_descrambler!=NULL) delete r_etr192_descrambler;
+ if(r_sync!=NULL) delete r_sync;
+ if(p_mpegbytes!=NULL) delete p_mpegbytes;
+ if(p_lock!=NULL) delete p_lock;
+ if(p_locktime!=NULL) delete p_locktime;
+ if(r_sync_mpeg!=NULL) delete r_sync_mpeg;
+
+
+ // DEINTERLEAVING
+ if(p_rspackets!=NULL) delete p_rspackets;
+ if(r_deinter!=NULL) delete r_deinter;
+ if(p_vbitcount!=NULL) delete p_vbitcount;
+ if(p_verrcount!=NULL) delete p_verrcount;
+ if(p_rtspackets!=NULL) delete p_rtspackets;
+ if(r_rsdec!=NULL) delete r_rsdec;
+
+ //BER ESTIMATION
+ if(p_vber!=NULL) delete p_vber;
+ if(r_vber!=NULL) delete r_vber;
+
+ // DERANDOMIZATION
+ if(p_tspackets!=NULL) delete p_tspackets;
+ if(r_derand!=NULL) delete r_derand;
+
+
+ //OUTPUT : To remove
+ if(r_stdout!=NULL) delete r_stdout;
+ if(r_videoplayer!=NULL) delete r_videoplayer;
+
+ //CONSTELLATION
+ if(r_scope_symbols!=NULL) delete r_scope_symbols;
+
+ }
+
+ m_objScheduler=NULL;
+
+ // INPUT
+
+ p_rawiq = NULL;
+ p_rawiq_writer = NULL;
+
+ p_preprocessed = NULL;
+
+ // NOTCH FILTER
+ r_auto_notch = NULL;
+ p_autonotched = NULL;
+
+ // FREQUENCY CORRECTION : DEROTATOR
+ p_derot = NULL;
+ r_derot=NULL;
+
+ // CNR ESTIMATION
+ p_cnr = NULL;
+ r_cnr = NULL;
+
+ //FILTERING
+ r_resample = NULL;
+ p_resampled = NULL;
+ coeffs = NULL;
+ ncoeffs=0;
+
+ // OUTPUT PREPROCESSED DATA
+ sampler = NULL;
+ coeffs_sampler=NULL;
+ ncoeffs_sampler=0;
+
+ p_symbols = NULL;
+ p_freq = NULL;
+ p_ss = NULL;
+ p_mer = NULL;
+ p_sampled = NULL;
+
+ //DECIMATION
+ p_decimated = NULL;
+ p_decim = NULL;
+ r_ppout = NULL;
+
+ //GENERIC CONSTELLATION RECEIVER
+ m_objDemodulator = NULL;
+
+ //DECONVOLUTION AND SYNCHRONIZATION
+ p_bytes=NULL;
+ r_deconv=NULL;
+ r = NULL;
+
+ p_descrambled = NULL;
+ p_frames = NULL;
+ r_etr192_descrambler = NULL;
+ r_sync = NULL;
+
+ p_mpegbytes = NULL;
+ p_lock = NULL;
+ p_locktime = NULL;
+ r_sync_mpeg = NULL;
+
+
+ // DEINTERLEAVING
+ p_rspackets = NULL;
+ r_deinter = NULL;
+
+ p_vbitcount = NULL;
+ p_verrcount = NULL;
+ p_rtspackets = NULL;
+ r_rsdec = NULL;
+
+
+ //BER ESTIMATION
+ p_vber = NULL;
+ r_vber = NULL;
+
+
+ // DERANDOMIZATION
+ p_tspackets = NULL;
+ r_derand = NULL;
+
+
+ //OUTPUT : To remove
+ r_stdout = NULL;
+ r_videoplayer = NULL;
+
+
+ //CONSTELLATION
+ r_scope_symbols = NULL;
+}
+
+void DATVDemod::InitDATVFramework()
+{
+ m_blnDVBInitialized=false;
+ m_lngReadIQ=0;
+
+ m_objCfg.standard = m_objRunning.enmStandard;
+
+ m_objCfg.fec = m_objRunning.enmFEC;
+ m_objCfg.Fs = (float) m_objRunning.intSampleRate;
+ m_objCfg.Fm = (float) m_objRunning.intSymbolRate;
+ m_objCfg.fastlock = m_objRunning.blnFastLock;
+
+ switch(m_objRunning.enmModulation)
+ {
+ case BPSK:
+ m_objCfg.constellation = cstln_lut<256>::BPSK;
+ break;
+
+ case QPSK:
+ m_objCfg.constellation = cstln_lut<256>::QPSK;
+ break;
+
+ case PSK8:
+ m_objCfg.constellation = cstln_lut<256>::PSK8;
+ break;
+
+ case APSK16:
+ m_objCfg.constellation = cstln_lut<256>::APSK16;
+ break;
+
+ case APSK32:
+ m_objCfg.constellation = cstln_lut<256>::APSK32;
+ break;
+
+ case APSK64E:
+ m_objCfg.constellation = cstln_lut<256>::APSK64E;
+ break;
+
+ case QAM16:
+ m_objCfg.constellation = cstln_lut<256>::QAM16;
+ break;
+
+ case QAM64:
+ m_objCfg.constellation = cstln_lut<256>::QAM64;
+ break;
+
+ case QAM256:
+ m_objCfg.constellation = cstln_lut<256>::QAM256;
+ break;
+
+ default:
+ m_objCfg.constellation = cstln_lut<256>::BPSK;
+ break;
+ }
+
+ m_objCfg.allow_drift = m_objRunning.blnAllowDrift;
+ m_objCfg.anf = m_objRunning.intNotchFilters;
+ m_objCfg.hard_metric = m_objRunning.blnHardMetric;
+ m_objCfg.hdlc = m_objRunning.blnHDLC;
+ m_objCfg.resample = m_objRunning.blnResample;
+ m_objCfg.viterbi = m_objRunning.blnViterbi;
+
+
+ // Min buffer size for baseband data
+ // scopes: 1024
+ // ss_estimator: 1024
+ // anf: 4096
+ // cstln_receiver: reads in chunks of 128+1
+ BUF_BASEBAND = 4096 * m_objCfg.buf_factor;
+
+ // Min buffer size for IQ symbols
+ // cstln_receiver: writes in chunks of 128/omega symbols (margin 128)
+ // deconv_sync: reads at least 64+32
+ // A larger buffer improves performance significantly.
+ BUF_SYMBOLS = 1024 * m_objCfg.buf_factor;
+
+ // Min buffer size for unsynchronized bytes
+ // deconv_sync: writes 32 bytes
+ // mpeg_sync: reads up to 204*scan_syncs = 1632 bytes
+ BUF_BYTES = 2048 * m_objCfg.buf_factor;
+
+ // Min buffer size for synchronized (but interleaved) bytes
+ // mpeg_sync: writes 1 rspacket
+ // deinterleaver: reads 17*11*12+204 = 2448 bytes
+ BUF_MPEGBYTES = 2448 * m_objCfg.buf_factor;
+
+ // Min buffer size for packets: 1
+ BUF_PACKETS = m_objCfg.buf_factor;
+
+ // Min buffer size for misc measurements: 1
+ BUF_SLOW = m_objCfg.buf_factor;
+
+ m_lngExpectedReadIQ = BUF_BASEBAND;
+
+
+ CleanUpDATVFramework(true);
+
+ m_objScheduler = new scheduler();
+
+ //***************
+ p_rawiq = new pipebuf(m_objScheduler, "rawiq", BUF_BASEBAND);
+ p_rawiq_writer = new pipewriter(*p_rawiq);
+ p_preprocessed = p_rawiq;
+
+ // NOTCH FILTER
+
+ if ( m_objCfg.anf )
+ {
+ p_autonotched = new pipebuf(m_objScheduler, "autonotched", BUF_BASEBAND);
+ r_auto_notch = new auto_notch(m_objScheduler, *p_preprocessed, *p_autonotched, m_objCfg.anf, 0);
+ p_preprocessed = p_autonotched;
+ }
+
+
+ // FREQUENCY CORRECTION
+
+ if ( m_objCfg.Fderot )
+ {
+ p_derot = new pipebuf(m_objScheduler, "derotated", BUF_BASEBAND);
+ r_derot = new rotator(m_objScheduler, *p_preprocessed, *p_derot, -m_objCfg.Fderot/m_objCfg.Fs);
+ p_preprocessed = p_derot;
+ }
+
+ // CNR ESTIMATION
+
+ p_cnr = new pipebuf(m_objScheduler, "cnr", BUF_SLOW);
+
+ if ( m_objCfg.cnr )
+ {
+ r_cnr = new cnr_fft(m_objScheduler, *p_preprocessed, *p_cnr, m_objCfg.Fm/m_objCfg.Fs);
+ r_cnr->decimation = decimation(m_objCfg.Fs, 1); // 1 Hz
+ }
+
+ // FILTERING
+
+ int decim = 1;
+
+ if ( m_objCfg.resample )
+ {
+ // Lowpass-filter and decimate.
+ if ( m_objCfg.decim )
+ {
+ decim = m_objCfg.decim;
+ }
+ else
+ {
+ // Decimate to just above 4 samples per symbol
+ float target_Fs = m_objCfg.Fm * 4;
+ decim = m_objCfg.Fs / target_Fs;
+ if ( decim < 1 )
+ {
+ decim = 1;
+ }
+ }
+
+ float transition = (m_objCfg.Fm/2) * m_objCfg.rolloff;
+ int order = m_objCfg.resample_rej * m_objCfg.Fs / (22*transition);
+ order = ((order+1)/2) * 2; // Make even
+
+ p_resampled = new pipebuf(m_objScheduler, "resampled", BUF_BASEBAND);
+
+
+ #if 1 // Cut in middle of roll-off region
+ float Fcut = (m_objCfg.Fm/2) * (1+m_objCfg.rolloff/2) / m_objCfg.Fs;
+ #else // Cut at beginning of roll-off region
+ float Fcut = (m_objCfg.Fm/2) / cfg.Fs;
+ #endif
+
+ ncoeffs = filtergen::lowpass(order, Fcut, &coeffs);
+
+ filtergen::normalize_dcgain(ncoeffs, coeffs, 1);
+
+ r_resample = new fir_filter(m_objScheduler, ncoeffs, coeffs, *p_preprocessed, *p_resampled, decim);
+ p_preprocessed = p_resampled;
+ m_objCfg.Fs /= decim;
+ }
+
+ // DECIMATION
+ // (Unless already done in resampler)
+
+ if ( !m_objCfg.resample && m_objCfg.decim>1 )
+ {
+ decim = m_objCfg.decim;
+
+ p_decimated = new pipebuf(m_objScheduler, "decimated", BUF_BASEBAND);
+ p_decim = new decimator(m_objScheduler, decim, *p_preprocessed, *p_decimated);
+ p_preprocessed = p_decimated;
+ m_objCfg.Fs /= decim;
+ }
+
+ //Resampling FS
+
+
+ // Generic constellation receiver
+
+ p_symbols = new pipebuf(m_objScheduler, "PSK soft-symbols", BUF_SYMBOLS);
+ p_freq = new pipebuf (m_objScheduler, "freq", BUF_SLOW);
+ p_ss = new pipebuf (m_objScheduler, "SS", BUF_SLOW);
+ p_mer = new pipebuf (m_objScheduler, "MER", BUF_SLOW);
+ p_sampled = new pipebuf (m_objScheduler, "PSK symbols", BUF_BASEBAND);
+
+ switch ( m_objCfg.sampler )
+ {
+ case SAMP_NEAREST:
+ sampler = new nearest_sampler();
+ break;
+
+ case SAMP_LINEAR:
+ sampler = new linear_sampler();
+ break;
+
+ case SAMP_RRC:
+ {
+
+
+ if ( m_objCfg.rrc_steps == 0 )
+ {
+ // At least 64 discrete sampling points between symbols
+ m_objCfg.rrc_steps = max(1, (int)(64*m_objCfg.Fm / m_objCfg.Fs));
+ }
+
+ float Frrc = m_objCfg.Fs * m_objCfg.rrc_steps; // Sample freq of the RRC filter
+ float transition = (m_objCfg.Fm/2) * m_objCfg.rolloff;
+ int order = m_objCfg.rrc_rej * Frrc / (22*transition);
+ ncoeffs_sampler = filtergen::root_raised_cosine(order, m_objCfg.Fm/Frrc, m_objCfg.rolloff, &coeffs_sampler);
+
+ sampler = new fir_sampler(ncoeffs_sampler, coeffs_sampler, m_objCfg.rrc_steps);
+ break;
+ }
+
+ default:
+ fatal("Interpolator not implemented");
+ }
+
+ m_objDemodulator = new cstln_receiver(m_objScheduler, sampler, *p_preprocessed, *p_symbols, p_freq, p_ss, p_mer, p_sampled);
+
+ if ( m_objCfg.standard == DVB_S )
+ {
+ if ( m_objCfg.constellation != cstln_lut<256>::QPSK && m_objCfg.constellation != cstln_lut<256>::BPSK )
+ {
+ fprintf(stderr, "Warning: non-standard constellation for DVB-S\n");
+ }
+ }
+
+ if ( m_objCfg.standard == DVB_S2 )
+ {
+ // For DVB-S2 testing only.
+ // Constellation should be determined from PL signalling.
+ fprintf(stderr, "DVB-S2: Testing symbol sampler only.\n");
+ }
+
+ m_objDemodulator->cstln = make_dvbs2_constellation(m_objCfg.constellation, m_objCfg.fec);
+
+ if ( m_objCfg.hard_metric )
+ {
+ m_objDemodulator->cstln->harden();
+ }
+
+ m_objDemodulator->set_omega(m_objCfg.Fs/m_objCfg.Fm);
+
+ if ( m_objCfg.Ftune )
+ {
+
+ m_objDemodulator->set_freq(m_objCfg.Ftune/m_objCfg.Fs);
+ }
+
+ if ( m_objCfg.allow_drift )
+ {
+ m_objDemodulator->set_allow_drift(true);
+ }
+
+ if ( m_objCfg.viterbi )
+ {
+ m_objDemodulator->pll_adjustment /= 6;
+ }
+
+ m_objDemodulator->meas_decimation = decimation(m_objCfg.Fs, m_objCfg.Finfo);
+
+ // TRACKING FILTERS
+
+ if ( r_resample )
+ {
+ r_resample->freq_tap = &m_objDemodulator->freq_tap;
+ r_resample->tap_multiplier = 1.0 / decim;
+ r_resample->freq_tol = m_objCfg.Fm/(m_objCfg.Fs*decim) * 0.1;
+ }
+
+
+ if ( r_cnr )
+ {
+ r_cnr->freq_tap = &m_objDemodulator->freq_tap;
+ r_cnr->tap_multiplier = 1.0 / decim;
+ }
+
+ //constellation
+
+ m_objRegisteredDATVScreen->resizeDATVScreen(256,256);
+
+ r_scope_symbols = new datvconstellation(m_objScheduler, *p_sampled, -128,128, NULL, m_objRegisteredDATVScreen);
+ r_scope_symbols->decimation = 1;
+ r_scope_symbols->cstln = &m_objDemodulator->cstln;
+
+ // DECONVOLUTION AND SYNCHRONIZATION
+
+ p_bytes = new pipebuf(m_objScheduler, "bytes", BUF_BYTES);
+
+ r_deconv = NULL;
+
+ if ( m_objCfg.viterbi )
+ {
+ if ( m_objCfg.fec==FEC23 && (m_objDemodulator->cstln->nsymbols==4 || m_objDemodulator->cstln->nsymbols==64) )
+ {
+ m_objCfg.fec = FEC46;
+ }
+
+ //To uncomment -> Linking Problem : undefined symbol: _ZN7leansdr21viterbi_dec_interfaceIhhiiE6updateEPiS2_
+ r = new viterbi_sync(m_objScheduler, (*p_symbols), (*p_bytes), m_objDemodulator->cstln, m_objCfg.fec);
+
+ if ( m_objCfg.fastlock )
+ {
+ r->resync_period = 1;
+ }
+ }
+ else
+ {
+ r_deconv = make_deconvol_sync_simple(m_objScheduler, (*p_symbols), (*p_bytes), m_objCfg.fec);
+ r_deconv->fastlock = m_objCfg.fastlock;
+ }
+
+
+ if ( m_objCfg.hdlc )
+ {
+ p_descrambled = new pipebuf(m_objScheduler, "descrambled", BUF_MPEGBYTES);
+ r_etr192_descrambler = new etr192_descrambler(m_objScheduler, (*p_bytes), *p_descrambled);
+ p_frames = new pipebuf(m_objScheduler, "frames", BUF_MPEGBYTES);
+ r_sync = new hdlc_sync(m_objScheduler, *p_descrambled, *p_frames, 2, 278);
+
+ if ( m_objCfg.fastlock )
+ {
+ r_sync->resync_period = 1;
+ }
+
+ if ( m_objCfg.packetized )
+ {
+ r_sync->header16 = true;
+ }
+
+ }
+
+ p_mpegbytes = new pipebuf (m_objScheduler, "mpegbytes", BUF_MPEGBYTES);
+ p_lock = new pipebuf (m_objScheduler, "lock", BUF_SLOW);
+ p_locktime = new pipebuf (m_objScheduler, "locktime", BUF_PACKETS);
+
+ if ( ! m_objCfg.hdlc )
+ {
+ r_sync_mpeg = new mpeg_sync(m_objScheduler, *p_bytes, *p_mpegbytes, r_deconv, p_lock, p_locktime);
+ r_sync_mpeg->fastlock = m_objCfg.fastlock;
+ }
+
+ // DEINTERLEAVING
+
+ p_rspackets = new pipebuf< rspacket >(m_objScheduler, "RS-enc packets", BUF_PACKETS);
+ r_deinter = new deinterleaver(m_objScheduler, *p_mpegbytes, *p_rspackets);
+
+
+ // REED-SOLOMON
+
+ p_vbitcount = new pipebuf(m_objScheduler, "Bits processed", BUF_PACKETS);
+ p_verrcount = new pipebuf(m_objScheduler, "Bits corrected", BUF_PACKETS);
+ p_rtspackets = new pipebuf(m_objScheduler, "rand TS packets", BUF_PACKETS);
+ r_rsdec = new rs_decoder (m_objScheduler, *p_rspackets, *p_rtspackets, p_vbitcount, p_verrcount);
+
+
+ // BER ESTIMATION
+
+
+ p_vber = new pipebuf (m_objScheduler, "VBER", BUF_SLOW);
+ r_vber = new rate_estimator (m_objScheduler, *p_verrcount, *p_vbitcount, *p_vber);
+ r_vber->sample_size = m_objCfg.Fm/2; // About twice per second, depending on CR
+ // Require resolution better than 2E-5
+ if ( r_vber->sample_size < 50000 )
+ {
+ r_vber->sample_size = 50000;
+ }
+
+
+ // DERANDOMIZATION
+
+ p_tspackets = new pipebuf(m_objScheduler, "TS packets", BUF_PACKETS);
+ r_derand = new derandomizer(m_objScheduler, *p_rtspackets, *p_tspackets);
+
+
+ // OUTPUT
+ r_videoplayer = new datvvideoplayer(m_objScheduler, *p_tspackets,m_objVideoStream);
+
+ m_blnDVBInitialized=true;
+}
+
+void DATVDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
+{
+ qint16 * ptrBufferToRelease=NULL;
+ float fltI;
+ float fltQ;
+ cf32 objIQ;
+ //Complex objC;
+ fftfilt::cmplx *objRF;
+ int intRFOut;
+
+
+#ifdef EXTENDED_DIRECT_SAMPLE
+
+ qint16 * ptrBuffer;
+ qint32 intLen;
+
+ //********** Reading direct samples **********
+
+ SampleVector::const_iterator it = begin;
+ intLen = it->intLen;
+ ptrBuffer = it->ptrBuffer;
+ ptrBufferToRelease = ptrBuffer;
+ ++it;
+
+ for(qint32 intInd=0; intIndreal();
+ fltQ = it->imag();
+#endif
+
+
+ //********** demodulation **********
+
+ if((m_blnDVBInitialized==false) || (m_blnNeedConfigUpdate==true))
+ {
+ m_blnNeedConfigUpdate=false;
+ InitDATVFramework();
+ }
+
+ //********** iq stream ****************
+
+ if(m_lngReadIQ>p_rawiq_writer->writable())
+ {
+ m_objScheduler->step();
+
+ m_objRegisteredDATVScreen->renderImage(NULL);
+
+ m_lngReadIQ=0;
+ p_rawiq_writer = new pipewriter(*p_rawiq);
+ }
+
+ if(false)
+ {
+ objIQ.re = fltI;
+ objIQ.im = fltQ;
+
+ p_rawiq_writer->write(objIQ);
+
+ m_lngReadIQ++;
+ }
+ else
+ {
+
+ Complex objC(fltI,fltQ);
+
+ objC *= m_objNCO.nextIQ();
+
+ intRFOut = m_objRFFilter->runFilt(objC, &objRF); // filter RF before demod
+
+ for (int intI = 0 ; intI < intRFOut; intI++)
+ {
+ objIQ.re = objRF->real();
+ objIQ.im = objRF->imag();
+
+ p_rawiq_writer->write(objIQ);
+
+ objRF ++;
+ m_lngReadIQ++;
+ }
+ }
+
+
+ //********** demodulation **********
+
+ }
+
+ if(ptrBufferToRelease!=NULL)
+ {
+ delete ptrBufferToRelease;
+ }
+
+ //m_objSettingsMutex.unlock();
+
+}
+
+void DATVDemod::start()
+{
+ //m_objTimer.start();
+}
+
+void DATVDemod::stop()
+{
+
+}
+
+bool DATVDemod::handleMessage(const Message& cmd)
+{
+ qDebug() << "DATVDemod::handleMessage";
+
+ if (DownChannelizer::MsgChannelizerNotification::match(cmd))
+ {
+ DownChannelizer::MsgChannelizerNotification& objNotif = (DownChannelizer::MsgChannelizerNotification&) cmd;
+
+ if(m_objRunning.intMsps!=objNotif.getSampleRate())
+ {
+ m_objRunning.intMsps = objNotif.getSampleRate();
+ m_objRunning.intSampleRate = m_objRunning.intMsps;
+
+ printf("Sample Rate: %d\r\n",m_objRunning.intSampleRate );
+ ApplySettings();
+ }
+
+ qDebug() << "DATVDemod::handleMessage: MsgChannelizerNotification:"
+ << " intMsps: " << m_objRunning.intMsps;
+
+ return true;
+ }
+ else if (MsgConfigureChannelizer::match(cmd))
+ {
+
+ MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd;
+
+ m_channelizer->configure(m_channelizer->getInputMessageQueue(),
+ m_channelizer->getInputSampleRate(),
+ m_objRunning.intCenterFrequency);
+
+
+
+ qDebug() << "ATVDemod::handleMessage: MsgConfigureChannelizer: sampleRate: " << m_channelizer->getInputSampleRate()
+ << " centerFrequency: " << m_objRunning.intCenterFrequency;
+
+ return true;
+ }
+ else if (MsgConfigureDATVDemod::match(cmd))
+ {
+ MsgConfigureDATVDemod& objCfg = (MsgConfigureDATVDemod&) cmd;
+
+
+ if((objCfg.m_objMsgConfig.blnAllowDrift != m_objRunning.blnAllowDrift)
+ || (objCfg.m_objMsgConfig.intRFBandwidth != m_objRunning.intRFBandwidth)
+ || (objCfg.m_objMsgConfig.intCenterFrequency != m_objRunning.intCenterFrequency)
+ || (objCfg.m_objMsgConfig.blnFastLock != m_objRunning.blnFastLock)
+ || (objCfg.m_objMsgConfig.blnHardMetric != m_objRunning.blnHardMetric)
+ || (objCfg.m_objMsgConfig.blnHDLC != m_objRunning.blnHDLC)
+ || (objCfg.m_objMsgConfig.blnResample != m_objRunning.blnResample)
+ || (objCfg.m_objMsgConfig.blnViterbi != m_objRunning.blnViterbi)
+ || (objCfg.m_objMsgConfig.enmFEC != m_objRunning.enmFEC)
+ || (objCfg.m_objMsgConfig.enmModulation != m_objRunning.enmModulation)
+ || (objCfg.m_objMsgConfig.enmStandard != m_objRunning.enmStandard)
+ || (objCfg.m_objMsgConfig.intNotchFilters != m_objRunning.intNotchFilters)
+ || (objCfg.m_objMsgConfig.intSymbolRate != m_objRunning.intSymbolRate))
+ {
+ m_objRunning.blnAllowDrift = objCfg.m_objMsgConfig.blnAllowDrift;
+ m_objRunning.blnFastLock = objCfg.m_objMsgConfig.blnFastLock;
+ m_objRunning.blnHardMetric = objCfg.m_objMsgConfig.blnHardMetric;
+ m_objRunning.blnHDLC = objCfg.m_objMsgConfig.blnHDLC;
+ m_objRunning.blnResample = objCfg.m_objMsgConfig.blnResample;
+ m_objRunning.blnViterbi = objCfg.m_objMsgConfig.blnViterbi;
+ m_objRunning.enmFEC = objCfg.m_objMsgConfig.enmFEC;
+ m_objRunning.enmModulation = objCfg.m_objMsgConfig.enmModulation;
+ m_objRunning.enmStandard = objCfg.m_objMsgConfig.enmStandard;
+ m_objRunning.intNotchFilters = objCfg.m_objMsgConfig.intNotchFilters;
+ m_objRunning.intSymbolRate = objCfg.m_objMsgConfig.intSymbolRate;
+ m_objRunning.intRFBandwidth = objCfg.m_objMsgConfig.intRFBandwidth;
+ m_objRunning.intCenterFrequency = objCfg.m_objMsgConfig.intCenterFrequency;
+
+ ApplySettings();
+ }
+
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void DATVDemod::ApplySettings()
+{
+
+ if(m_objRunning.intMsps==0)
+ {
+ return;
+ }
+
+ //m_objSettingsMutex.lock();
+
+ InitDATVParameters(m_objRunning.intMsps,
+ m_objRunning.intRFBandwidth,
+ m_objRunning.intCenterFrequency,
+ m_objRunning.enmStandard,
+ m_objRunning.enmModulation,
+ m_objRunning.enmFEC,
+ m_objRunning.intSampleRate,
+ m_objRunning.intSymbolRate,
+ m_objRunning.intNotchFilters,
+ m_objRunning.blnAllowDrift,
+ m_objRunning.blnFastLock,
+ m_objRunning.blnHDLC,
+ m_objRunning.blnHardMetric,
+ m_objRunning.blnResample,
+ m_objRunning.blnViterbi);
+
+}
+
+int DATVDemod::GetSampleRate()
+{
+ return m_objRunning.intMsps;
+}
+
diff --git a/plugins/channelrx/demoddatv/datvdemod.h b/plugins/channelrx/demoddatv/datvdemod.h
new file mode 100644
index 000000000..399e596e9
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvdemod.h
@@ -0,0 +1,466 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// using LeanSDR Framework (C) 2016 F4DAV //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_DATVDEMOD_H
+#define INCLUDE_DATVDEMOD_H
+
+class DeviceSourceAPI;
+class ThreadedBasebandSampleSink;
+class DownChannelizer;
+
+#define rfFilterFftLength 1024
+
+#ifndef LEANSDR_FRAMEWORK
+#define LEANSDR_FRAMEWORK
+
+//LeanSDR
+
+#include "leansdr/framework.h"
+#include "leansdr/generic.h"
+#include "leansdr/dsp.h"
+#include "leansdr/sdr.h"
+#include "leansdr/dvb.h"
+#include "leansdr/rs.h"
+#include "leansdr/filtergen.h"
+
+#include "leansdr/hdlc.h"
+#include "leansdr/iess.h"
+
+#endif
+
+
+
+#include "datvconstellation.h"
+#include "datvvideoplayer.h"
+
+#include "channel/channelsinkapi.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "dsp/nco.h"
+#include "dsp/interpolator.h"
+#include "dsp/movingaverage.h"
+#include "dsp/agc.h"
+#include "audio/audiofifo.h"
+#include "util/message.h"
+#include
+
+#include "datvideostream.h"
+#include "datvideorender.h"
+
+using namespace leansdr;
+
+enum DATVModulation { BPSK, QPSK, PSK8, APSK16, APSK32, APSK64E, QAM16, QAM64, QAM256 };
+enum dvb_version { DVB_S, DVB_S2 };
+enum dvb_sampler { SAMP_NEAREST, SAMP_LINEAR, SAMP_RRC };
+
+inline int decimation(float Fin, float Fout) { int d = Fin / Fout; return max(d, 1); }
+
+struct config
+{
+ dvb_version standard;
+ dvb_sampler sampler;
+
+ int buf_factor; // Buffer sizing
+ float Fs; // Sampling frequency (Hz)
+ float Fderot; // Shift the signal (Hz). Note: Ftune is faster
+ int anf; // Number of auto notch filters
+ bool cnr; // Measure CNR
+ unsigned int decim; // Decimation, 0=auto
+ float Fm; // QPSK symbol rate (Hz)
+ cstln_lut<256>::predef constellation;
+ code_rate fec;
+ float Ftune; // Bias frequency for the QPSK demodulator (Hz)
+ bool allow_drift;
+ bool fastlock;
+ bool viterbi;
+ bool hard_metric;
+ bool resample;
+ float resample_rej; // Approx. filter rejection in dB
+ int rrc_steps; // Discrete steps between symbols, 0=auto
+ float rrc_rej; // Approx. RRC filter rejection in dB
+ float rolloff; // Roll-off 0..1
+ bool hdlc; // Expect HDLC frames instead of MPEG packets
+ bool packetized; // Output frames with 16-bit BE length
+ float Finfo; // Desired refresh rate on fd_info (Hz)
+
+ config() : buf_factor(4),
+ Fs(2.4e6),
+ Fderot(0),
+ anf(1),
+ cnr(false),
+ decim(0),
+ Fm(2e6),
+ standard(DVB_S),
+ constellation(cstln_lut<256>::QPSK),
+ fec(FEC12),
+ Ftune(0),
+ allow_drift(false),
+ fastlock(true),
+ viterbi(false),
+ hard_metric(false),
+ resample(false),
+ resample_rej(10),
+ sampler(SAMP_LINEAR),
+ rrc_steps(0),
+ rrc_rej(10),
+ rolloff(0.35),
+ hdlc(false),
+ packetized(false),
+ Finfo(5)
+ {
+ }
+};
+
+
+struct DATVConfig
+{
+ int intMsps;
+ int intRFBandwidth;
+ int intCenterFrequency;
+ dvb_version enmStandard;
+ DATVModulation enmModulation;
+ code_rate enmFEC;
+ int intSampleRate;
+ int intSymbolRate;
+ int intNotchFilters;
+ bool blnAllowDrift;
+ bool blnFastLock;
+ bool blnHDLC;
+ bool blnHardMetric;
+ bool blnResample;
+ bool blnViterbi;
+
+ DATVConfig() :
+ intMsps(1024000),
+ intRFBandwidth(1024000),
+ intCenterFrequency(0),
+ enmStandard(DVB_S),
+ enmModulation(BPSK),
+ enmFEC(FEC12),
+ intSampleRate(1024000),
+ intSymbolRate(250000),
+ intNotchFilters(1),
+ blnAllowDrift(false),
+ blnFastLock(false),
+ blnHDLC(false),
+ blnHardMetric(false),
+ blnResample(false),
+ blnViterbi(false)
+ {
+ }
+};
+
+
+class DATVDemod : public BasebandSampleSink, public ChannelSinkAPI
+{
+ Q_OBJECT
+
+public:
+
+ DATVDemod(DeviceSourceAPI *);
+ ~DATVDemod();
+
+ virtual void destroy() { delete this; }
+ virtual void getIdentifier(QString& id) { id = objectName(); }
+ virtual void getTitle(QString& title) { title = objectName(); }
+ virtual qint64 getCenterFrequency() const { return m_objRunning.intCenterFrequency; }
+
+ virtual QByteArray serialize() const { return QByteArray(); }
+ virtual bool deserialize(const QByteArray& data __attribute__((unused))) { return false; }
+
+
+
+ void configure(MessageQueue* objMessageQueue,
+ int intRFBandwidth,
+ int intCenterFrequency,
+ dvb_version enmStandard,
+ DATVModulation enmModulation,
+ code_rate enmFEC,
+ int intSymbolRate,
+ int intNotchFilters,
+ bool blnAllowDrift,
+ bool blnFastLock,
+ bool blnHDLC,
+ bool blnHardMetric,
+ bool blnResample,
+ bool blnViterbi);
+
+ virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
+ virtual void start();
+ virtual void stop();
+ virtual bool handleMessage(const Message& cmd);
+
+ bool SetDATVScreen(DATVScreen *objScreen);
+ DATVideostream * SetVideoRender(DATVideoRender *objScreen);
+
+ bool PlayVideo(bool blnStartStop);
+
+ void InitDATVParameters(int intMsps,
+ int intRFBandwidth,
+ int intCenterFrequency,
+ dvb_version enmStandard,
+ DATVModulation enmModulation,
+ code_rate enmFEC,
+ int intSampleRate,
+ int intSymbolRate,
+ int intNotchFilters,
+ bool blnAllowDrift,
+ bool blnFastLock,
+ bool blnHDLC,
+ bool blnHardMetric,
+ bool blnResample,
+ bool blnViterbi);
+
+ void CleanUpDATVFramework(bool blnRelease);
+ int GetSampleRate();
+ void InitDATVFramework();
+
+ static const QString m_channelIdURI;
+ static const QString m_channelId;
+
+
+ class MsgConfigureChannelizer : public Message
+ {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ int getCenterFrequency() const { return m_centerFrequency; }
+
+ static MsgConfigureChannelizer* create(int centerFrequency)
+ {
+ return new MsgConfigureChannelizer(centerFrequency);
+ }
+
+ private:
+ int m_centerFrequency;
+
+ MsgConfigureChannelizer(int centerFrequency) :
+ Message(),
+ m_centerFrequency(centerFrequency)
+ { }
+ };
+
+
+
+private:
+
+ unsigned long m_lngExpectedReadIQ;
+ unsigned long m_lngReadIQ;
+
+ //************** LEANDBV Parameters **************
+
+ unsigned long BUF_BASEBAND;
+ unsigned long BUF_SYMBOLS;
+ unsigned long BUF_BYTES;
+ unsigned long BUF_MPEGBYTES;
+ unsigned long BUF_PACKETS;
+ unsigned long BUF_SLOW;
+
+ //************** LEANDBV Scheduler ***************
+
+ scheduler * m_objScheduler;
+ struct config m_objCfg;
+
+ bool m_blnDVBInitialized;
+ bool m_blnNeedConfigUpdate;
+
+ //LeanSDR Pipe Buffer
+ // INPUT
+
+ pipebuf *p_rawiq;
+ pipewriter *p_rawiq_writer;
+ pipebuf *p_preprocessed;
+
+ // NOTCH FILTER
+ auto_notch *r_auto_notch;
+ pipebuf *p_autonotched;
+
+ // FREQUENCY CORRECTION : DEROTATOR
+ pipebuf *p_derot;
+ rotator *r_derot;
+
+ // CNR ESTIMATION
+ pipebuf *p_cnr;
+ cnr_fft *r_cnr;
+
+ //FILTERING
+ fir_filter *r_resample;
+ pipebuf *p_resampled;
+ float *coeffs;
+ int ncoeffs;
+
+ // OUTPUT PREPROCESSED DATA
+ sampler_interface *sampler;
+ float *coeffs_sampler;
+ int ncoeffs_sampler;
+
+ pipebuf *p_symbols;
+ pipebuf *p_freq;
+ pipebuf *p_ss;
+ pipebuf *p_mer;
+ pipebuf *p_sampled;
+
+ //DECIMATION
+ pipebuf *p_decimated;
+ decimator *p_decim;
+
+ //PROCESSED DATA MONITORING
+ file_writer *r_ppout;
+
+ //GENERIC CONSTELLATION RECEIVER
+ cstln_receiver *m_objDemodulator;
+
+ // DECONVOLUTION AND SYNCHRONIZATION
+ pipebuf *p_bytes;
+ deconvol_sync_simple *r_deconv;
+ viterbi_sync *r;
+ pipebuf *p_descrambled;
+ pipebuf *p_frames;
+
+ etr192_descrambler * r_etr192_descrambler;
+ hdlc_sync *r_sync;
+
+ pipebuf *p_mpegbytes;
+ pipebuf *p_lock;
+ pipebuf *p_locktime;
+ mpeg_sync *r_sync_mpeg;
+
+
+ // DEINTERLEAVING
+ pipebuf< rspacket > *p_rspackets;
+ deinterleaver *r_deinter;
+
+ // REED-SOLOMON
+ pipebuf *p_vbitcount;
+ pipebuf *p_verrcount;
+ pipebuf *p_rtspackets;
+ rs_decoder *r_rsdec;
+
+ // BER ESTIMATION
+ pipebuf *p_vber;
+ rate_estimator *r_vber;
+
+ // DERANDOMIZATION
+ pipebuf *p_tspackets;
+ derandomizer *r_derand;
+
+
+ //OUTPUT
+ file_writer *r_stdout;
+ datvvideoplayer *r_videoplayer;
+
+ //CONSTELLATION
+ datvconstellation *r_scope_symbols;
+
+
+private:
+
+ DeviceSourceAPI* m_deviceAPI;
+
+ ThreadedBasebandSampleSink* m_threadedChannelizer;
+ DownChannelizer* m_channelizer;
+
+ //*************** DATV PARAMETERS ***************
+ DATVScreen * m_objRegisteredDATVScreen;
+ DATVideoRender * m_objRegisteredVideoRender;
+ DATVideostream * m_objVideoStream;
+ DATVideoRenderThread * m_objRenderThread;
+
+ fftfilt * m_objRFFilter;
+ NCO m_objNCO;
+
+ bool m_blnInitialized;
+ bool m_blnRenderingVideo;
+
+ DATVModulation m_enmModulation;
+
+ //QElapsedTimer m_objTimer;
+private:
+
+ class MsgConfigureDATVDemod : public Message
+ {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgConfigureDATVDemod* create(int intRFBandwidth,
+ int intCenterFrequency,
+ dvb_version enmStandard,
+ DATVModulation enmModulation,
+ code_rate enmFEC,
+ int intSymbolRate,
+ int intNotchFilters,
+ bool blnAllowDrift,
+ bool blnFastLock,
+ bool blnHDLC,
+ bool blnHardMetric,
+ bool blnResample,
+ bool blnViterbi)
+ {
+ return new MsgConfigureDATVDemod(intRFBandwidth,intCenterFrequency,enmStandard, enmModulation, enmFEC, intSymbolRate, intNotchFilters, blnAllowDrift,blnFastLock,blnHDLC,blnHardMetric,blnResample, blnViterbi);
+ }
+
+ DATVConfig m_objMsgConfig;
+
+ private:
+ MsgConfigureDATVDemod(int intRFBandwidth,
+ int intCenterFrequency,
+ dvb_version enmStandard,
+ DATVModulation enmModulation,
+ code_rate enmFEC,
+ int intSymbolRate,
+ int intNotchFilters,
+ bool blnAllowDrift,
+ bool blnFastLock,
+ bool blnHDLC,
+ bool blnHardMetric,
+ bool blnResample,
+ bool blnViterbi) :
+ Message()
+ {
+ m_objMsgConfig.intRFBandwidth = intRFBandwidth;
+ m_objMsgConfig.intCenterFrequency = intCenterFrequency;
+ m_objMsgConfig.enmStandard = enmStandard;
+ m_objMsgConfig.enmModulation = enmModulation;
+ m_objMsgConfig.enmFEC = enmFEC;
+ m_objMsgConfig.intSymbolRate = intSymbolRate;
+ m_objMsgConfig.intNotchFilters = intNotchFilters;
+ m_objMsgConfig.blnAllowDrift = blnAllowDrift;
+ m_objMsgConfig.blnFastLock = blnFastLock;
+ m_objMsgConfig.blnHDLC = blnHDLC;
+ m_objMsgConfig.blnHardMetric = blnHardMetric;
+ m_objMsgConfig.blnResample = blnResample;
+ m_objMsgConfig.blnViterbi = blnViterbi;
+ }
+ };
+
+
+ DATVConfig m_objRunning;
+
+ QMutex m_objSettingsMutex;
+
+ void ApplySettings();
+
+};
+
+#endif // INCLUDE_DATVDEMOD_H
diff --git a/plugins/channelrx/demoddatv/datvdemodgui.cpp b/plugins/channelrx/demoddatv/datvdemodgui.cpp
new file mode 100644
index 000000000..254d6049f
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvdemodgui.cpp
@@ -0,0 +1,754 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// using LeanSDR Framework (C) 2016 F4DAV //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+#include
+#include
+
+#include "datvdemodgui.h"
+#include "datvideostream.h"
+
+#include "device/devicesourceapi.h"
+#include "device/deviceuiset.h"
+#include "device/devicesourceapi.h"
+#include "dsp/downchannelizer.h"
+
+#include "dsp/threadedbasebandsamplesink.h"
+#include "ui_datvdemodgui.h"
+#include "plugin/pluginapi.h"
+#include "util/simpleserializer.h"
+#include "util/db.h"
+#include "dsp/dspengine.h"
+#include "mainwindow.h"
+
+const QString DATVDemodGUI::m_strChannelID = "sdrangel.channel.demoddatv";
+
+DATVDemodGUI* DATVDemodGUI::create(PluginAPI* objPluginAPI,
+ DeviceUISet *deviceUISet,
+ BasebandSampleSink *rxChannel)
+{
+ DATVDemodGUI* gui = new DATVDemodGUI(objPluginAPI, deviceUISet, rxChannel);
+ return gui;
+}
+
+void DATVDemodGUI::destroy()
+{
+ delete this;
+}
+
+void DATVDemodGUI::setName(const QString& strName)
+{
+ setObjectName(strName);
+}
+
+QString DATVDemodGUI::getName() const
+{
+ return objectName();
+}
+
+qint64 DATVDemodGUI::getCenterFrequency() const
+{
+ return m_objChannelMarker.getCenterFrequency();
+}
+
+void DATVDemodGUI::setCenterFrequency(qint64 intCenterFrequency)
+{
+ m_objChannelMarker.setCenterFrequency(intCenterFrequency);
+ applySettings();
+}
+
+void DATVDemodGUI::resetToDefaults()
+{
+ blockApplySettings(true);
+
+ ui->chkAllowDrift->setChecked(false);
+ ui->chkFastlock->setChecked(true);
+ ui->chkHDLC->setChecked(false);
+ ui->chkHardMetric->setChecked(false);
+ ui->chkResample->setChecked(false);
+ ui->chkViterbi->setChecked(false);
+
+ ui->cmbFEC->setCurrentIndex(0);
+ ui->cmbModulation->setCurrentIndex(0);
+ ui->cmbStandard->setCurrentIndex(0);
+
+ ui->spiNotchFilters->setValue(1);
+ ui->prgSynchro->setValue(0);
+
+ ui->lblStatus->setText("");
+
+ ui->spiBandwidth->setValue(512000);
+ ui->spiSymbolRate->setValue(250000);
+
+ blockApplySettings(false);
+
+ applySettings();
+}
+
+QByteArray DATVDemodGUI::serialize() const
+{
+ SimpleSerializer s(1);
+
+ s.writeS32(1, m_objChannelMarker.getCenterFrequency());
+ s.writeU32(2, m_objChannelMarker.getColor().rgb());
+
+ s.writeBool(3, ui->chkAllowDrift->isChecked());
+ s.writeBool(4, ui->chkFastlock->isChecked());
+ s.writeBool(5, ui->chkHDLC->isChecked());
+ s.writeBool(6, ui->chkHardMetric->isChecked());
+ s.writeBool(7, ui->chkResample->isChecked());
+ s.writeBool(8, ui->chkViterbi->isChecked());
+
+ s.writeS32(9, ui->cmbFEC->currentIndex());
+ s.writeS32(10, ui->cmbModulation->currentIndex());
+ s.writeS32(11, ui->cmbStandard->currentIndex());
+
+ s.writeS32(12, ui->spiNotchFilters->value());
+ s.writeS32(13, ui->spiBandwidth->value());
+ s.writeS32(14, ui->spiSymbolRate->value());
+
+ return s.final();
+}
+
+bool DATVDemodGUI::deserialize(const QByteArray& arrData)
+{
+ SimpleDeserializer d(arrData);
+
+ if (!d.isValid())
+ {
+ resetToDefaults();
+ return false;
+ }
+
+ if (d.getVersion() == 1)
+ {
+ QByteArray bytetmp;
+ uint32_t u32tmp;
+ int tmp;
+ bool booltmp;
+
+ blockApplySettings(true);
+ m_objChannelMarker.blockSignals(true);
+
+ d.readS32(1, &tmp, 0);
+ m_objChannelMarker.setCenterFrequency(tmp);
+
+ if (d.readU32(2, &u32tmp))
+ {
+ m_objChannelMarker.setColor(u32tmp);
+ }
+ else
+ {
+ m_objChannelMarker.setColor(Qt::magenta);
+ }
+
+ d.readBool(3, &booltmp, false);
+ ui->chkAllowDrift->setChecked(booltmp);
+
+ d.readBool(4, &booltmp, false);
+ ui->chkFastlock->setChecked(booltmp);
+
+ d.readBool(5, &booltmp, false);
+ ui->chkHDLC->setChecked(booltmp);
+
+ d.readBool(6, &booltmp, false);
+ ui->chkHardMetric->setChecked(booltmp);
+
+ d.readBool(7, &booltmp, false);
+ ui->chkResample->setChecked(booltmp);
+
+ d.readBool(8, &booltmp, false);
+ ui->chkViterbi->setChecked(booltmp);
+
+
+ d.readS32(9, &tmp, 0);
+ ui->cmbFEC->setCurrentIndex(tmp);
+
+ d.readS32(10, &tmp, 0);
+ ui->cmbModulation->setCurrentIndex(tmp);
+
+ d.readS32(11, &tmp, 0);
+ ui->cmbStandard->setCurrentIndex(tmp);
+
+ d.readS32(12, &tmp, 1);
+ ui->spiNotchFilters->setValue(tmp);
+
+ d.readS32(13, &tmp, 1024000);
+ ui->spiBandwidth->setValue(tmp);
+
+ d.readS32(14, &tmp, 250000);
+ ui->spiSymbolRate->setValue(tmp);
+
+
+ blockApplySettings(false);
+ m_objChannelMarker.blockSignals(false);
+
+ applySettings();
+ return true;
+ }
+ else
+ {
+ resetToDefaults();
+ return false;
+ }
+}
+
+bool DATVDemodGUI::handleMessage(const Message& objMessage)
+{
+ return false;
+}
+
+void DATVDemodGUI::channelMarkerChangedByCursor()
+{
+ if(m_intCenterFrequency!=m_objChannelMarker.getCenterFrequency())
+ {
+ m_intCenterFrequency=m_objChannelMarker.getCenterFrequency();
+ applySettings();
+ }
+}
+
+void DATVDemodGUI::channelMarkerHighlightedByCursor()
+{
+ setHighlighted(m_objChannelMarker.getHighlighted());
+}
+
+void DATVDemodGUI::channelSampleRateChanged()
+{
+ qDebug("DATVDemodGUI::channelSampleRateChanged");
+ applySettings();
+}
+
+void DATVDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
+{
+}
+
+void DATVDemodGUI::onMenuDoubleClicked()
+{
+ /*
+ if (!m_blnBasicSettingsShown)
+ {
+ m_blnBasicSettingsShown = true;
+ BasicChannelSettingsWidget* bcsw = new BasicChannelSettingsWidget(&m_objChannelMarker, this);
+ bcsw->show();
+ }
+ */
+}
+
+//DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI, QWidget* objParent) :
+DATVDemodGUI::DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* objParent) :
+ RollupWidget(objParent),
+ ui(new Ui::DATVDemodGUI),
+ m_objPluginAPI(objPluginAPI),
+ m_deviceUISet(deviceUISet),
+ m_objChannelMarker(this),
+ m_blnBasicSettingsShown(false),
+ m_blnDoApplySettings(true)
+{
+ ui->setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose, true);
+ connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
+ //connect(this, SIGNAL(menuDoubleClickEvent()), this, SLOT(onMenuDoubleClicked()));
+
+ //m_objDATVDemod = new DATVDemod();
+ m_objDATVDemod = (DATVDemod*) rxChannel;
+ m_objDATVDemod->setMessageQueueToGUI(getInputMessageQueue());
+
+ m_objDATVDemod->SetDATVScreen(ui->screenTV);
+
+ connect(m_objDATVDemod->SetVideoRender(ui->screenTV_2),&DATVideostream::onDataPackets,this,&DATVDemodGUI::on_StreamDataAvailable);
+
+
+ //connect(m_objChannelizer, SIGNAL(inputSampleRateChanged()), this, SLOT(channelSampleRateChanged()));
+
+
+ //m_objPluginAPI->addThreadedSink(m_objThreadedChannelizer);
+ //connect(&m_objPluginAPI->getMainWindow()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
+
+ m_intPreviousDecodedData=0;
+ m_intLastDecodedData=0;
+ m_intLastSpeed=0;
+ m_intReadyDecodedData=0;
+ m_blnButtonPlayClicked=false;
+ m_objTimer.setInterval(1000);
+ connect(&m_objTimer, SIGNAL(timeout()), this, SLOT(tick()));
+ m_objTimer.start();
+
+
+ m_objChannelMarker.blockSignals(true);
+ m_objChannelMarker.setColor(Qt::magenta);
+ m_objChannelMarker.setBandwidth(6000000);
+ m_objChannelMarker.setCenterFrequency(0);
+ m_objChannelMarker.blockSignals(false);
+ m_objChannelMarker.setVisible(true);
+ //connect(&m_objChannelMarker, SIGNAL(changed()), this, SLOT(viewChanged()));
+
+ connect(&m_objChannelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor()));
+ connect(&m_objChannelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
+
+ m_deviceUISet->registerRxChannelInstance(DATVDemod::m_channelIdURI, this);
+ m_deviceUISet->addChannelMarker(&m_objChannelMarker);
+ m_deviceUISet->addRollupWidget(this);
+
+ //ui->screenTV->connectTimer(m_objPluginAPI->getMainWindow()->getMasterTimer());
+
+ ui->pushButton_3->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
+
+ resetToDefaults(); // does applySettings()
+
+}
+
+DATVDemodGUI::~DATVDemodGUI()
+{
+
+ m_deviceUISet->removeRxChannelInstance(this);
+ delete m_objDATVDemod;
+ delete ui;
+
+}
+
+void DATVDemodGUI::blockApplySettings(bool blnBlock)
+{
+ m_blnDoApplySettings = !blnBlock;
+}
+
+void DATVDemodGUI::applySettings()
+{
+ QString strStandard;
+ QString strModulation;
+ QString strFEC;
+
+ DATVModulation enmSelectedModulation;
+ dvb_version enmVersion;
+ code_rate enmFEC;
+
+ if (m_blnDoApplySettings)
+ {
+
+
+ DATVDemod::MsgConfigureChannelizer *msgChan = DATVDemod::MsgConfigureChannelizer::create(m_objChannelMarker.getCenterFrequency());
+ m_objDATVDemod->getInputMessageQueue()->push(msgChan);
+
+ //Bandwidth and center frequency
+ m_objChannelMarker.setBandwidth(ui->spiBandwidth->value());
+ //m_objChannelizer->configure(m_objChannelizer->getInputMessageQueue(), m_objChannelizer->getInputSampleRate(), m_objChannelMarker.getCenterFrequency());
+
+ setTitleColor(m_objChannelMarker.getColor());
+
+ //DATV parameters: cmbStandard cmbModulation cmbFEC spiBandwidth spiSymbolRate spiNotchFilters chkAllowDrift chkFastlock chkHDLC chkHardMetric chkResample chkViterbi
+
+ strStandard = ui->cmbStandard->currentText();
+
+ if(strStandard=="DVB-S")
+ {
+ enmVersion=DVB_S;
+ }
+ else if (strStandard=="DVB-S2")
+ {
+ enmVersion=DVB_S2;
+ }
+ else
+ {
+ enmVersion=DVB_S;
+ }
+
+
+ //BPSK, QPSK, PSK8, APSK16, APSK32, APSK64E, QAM16, QAM64, QAM256
+
+ strModulation = ui->cmbModulation->currentText();
+
+ if(strModulation=="BPSK")
+ {
+ enmSelectedModulation=BPSK;
+ }
+ else if(strModulation=="QPSK")
+ {
+ enmSelectedModulation=QPSK;
+ }
+ else if(strModulation=="8PSK")
+ {
+ enmSelectedModulation=PSK8;
+ }
+ else if(strModulation=="16APSK")
+ {
+ enmSelectedModulation=APSK16;
+ }
+ else if(strModulation=="32APSK")
+ {
+ enmSelectedModulation=APSK32;
+ }
+ else if(strModulation=="64APSKE")
+ {
+ enmSelectedModulation=APSK64E;
+ }
+ else if(strModulation=="16QAM")
+ {
+ enmSelectedModulation=QAM16;
+ }
+ else if(strModulation=="64QAM")
+ {
+ enmSelectedModulation=QAM64;
+ }
+ else if(strModulation=="256QAM")
+ {
+ enmSelectedModulation=QAM256;
+ }
+ else
+ {
+ enmSelectedModulation=BPSK;
+ }
+
+
+ strFEC = ui->cmbFEC->currentText();
+
+ if(strFEC=="1/2")
+ {
+ enmFEC=FEC12;
+ }
+ else if(strFEC=="2/3")
+ {
+ enmFEC=FEC23;
+ }
+ else if(strFEC=="3/4")
+ {
+ enmFEC=FEC34;
+ }
+ else if(strFEC=="5/6")
+ {
+ enmFEC=FEC56;
+ }
+ else if(strFEC=="7/8")
+ {
+ enmFEC=FEC78;
+ }
+ else if(strFEC=="4/5")
+ {
+ enmFEC=FEC45;
+ }
+ else if(strFEC=="8/9")
+ {
+ enmFEC=FEC89;
+ }
+ else if(strFEC=="9/10")
+ {
+ enmFEC=FEC910;
+ }
+ else
+ {
+ enmFEC=FEC12;
+ }
+
+
+ m_objDATVDemod->configure(m_objDATVDemod->getInputMessageQueue(),
+ m_objChannelMarker.getBandwidth(),
+ m_objChannelMarker.getCenterFrequency(),
+ enmVersion,
+ enmSelectedModulation,
+ enmFEC,
+ ui->spiSymbolRate->value(),
+ ui->spiNotchFilters->value(),
+ ui->chkAllowDrift->isChecked(),
+ ui->chkFastlock->isChecked(),
+ ui->chkHDLC->isChecked(),
+ ui->chkHardMetric->isChecked(),
+ ui->chkResample->isChecked(),
+ ui->chkViterbi->isChecked());
+
+ qDebug() << "DATVDemodGUI::applySettings:"
+ << " .inputSampleRate: " << 0 /*m_objChannelizer->getInputSampleRate()*/
+ << " m_objDATVDemod.sampleRate: " << m_objDATVDemod->GetSampleRate();
+
+
+ }
+}
+
+void DATVDemodGUI::leaveEvent(QEvent*)
+{
+ blockApplySettings(true);
+ m_objChannelMarker.setHighlighted(false);
+ blockApplySettings(false);
+}
+
+void DATVDemodGUI::enterEvent(QEvent*)
+{
+ blockApplySettings(true);
+ m_objChannelMarker.setHighlighted(true);
+ blockApplySettings(false);
+}
+
+void DATVDemodGUI::tick()
+{
+ if((m_intLastDecodedData-m_intPreviousDecodedData)>=0)
+ {
+ m_intLastSpeed = 8*(m_intLastDecodedData-m_intPreviousDecodedData);
+ ui->lblRate->setText(QString("Speed: %1b/s").arg(formatBytes(m_intLastSpeed)));
+ }
+
+ m_intPreviousDecodedData = m_intLastDecodedData;
+
+ //Try to start video rendering
+ m_objDATVDemod->PlayVideo(false);
+
+ return;
+}
+
+void DATVDemodGUI::on_cmbStandard_currentIndexChanged(const QString &arg1)
+{
+ applySettings();
+}
+
+void DATVDemodGUI::on_cmbModulation_currentIndexChanged(const QString &arg1)
+{
+ QString strModulation;
+ QString strFEC;
+
+ strFEC = ui->cmbFEC->currentText();
+
+ strModulation = ui->cmbModulation->currentText();
+
+ if(strModulation=="16APSK")
+ {
+ if((strFEC!="2/3")
+ && (strFEC!="3/4")
+ && (strFEC!="4/5")
+ && (strFEC!="5/6")
+ && (strFEC!="4/6")
+ && (strFEC!="8/9")
+ && (strFEC!="9/10"))
+ {
+ //Reset modulation to 2/3
+
+ ui->cmbFEC->setCurrentIndex(1);
+ }
+ else
+ {
+ applySettings();
+ }
+ }
+ else if(strModulation=="32APSK")
+ {
+ if((strFEC!="3/4")
+ && (strFEC!="4/5")
+ && (strFEC!="5/6")
+ && (strFEC!="8/9")
+ && (strFEC!="9/10"))
+ {
+ //Reset modulation to 3/4
+
+ ui->cmbFEC->setCurrentIndex(2);
+ }
+ else
+ {
+ applySettings();
+ }
+ }
+ else
+ {
+ applySettings();
+ }
+
+}
+
+void DATVDemodGUI::on_cmbFEC_currentIndexChanged(const QString &arg1)
+{
+ QString strFEC;
+
+ strFEC = ui->cmbFEC->currentText();
+
+ if(ui->cmbModulation->currentText()=="16APSK")
+ {
+ if((strFEC!="2/3")
+ && (strFEC!="3/4")
+ && (strFEC!="4/5")
+ && (strFEC!="5/6")
+ && (strFEC!="4/6")
+ && (strFEC!="8/9")
+ && (strFEC!="9/10"))
+ {
+ //Reset modulation to BPSK
+
+ ui->cmbModulation->setCurrentIndex(0);
+ }
+ else
+ {
+ applySettings();
+ }
+ }
+ else if(ui->cmbModulation->currentText()=="32APSK")
+ {
+ if((strFEC!="3/4")
+ && (strFEC!="4/5")
+ && (strFEC!="5/6")
+ && (strFEC!="8/9")
+ && (strFEC!="9/10"))
+ {
+ //Reset modulation to BPSK
+
+ ui->cmbModulation->setCurrentIndex(0);
+ }
+ else
+ {
+ applySettings();
+ }
+ }
+ else
+ {
+ applySettings();
+ }
+
+}
+
+void DATVDemodGUI::on_chkViterbi_clicked()
+{
+ applySettings();
+}
+
+void DATVDemodGUI::on_chkHardMetric_clicked()
+{
+ applySettings();
+}
+
+/*
+void DATVDemodGUI::on_pushButton_clicked()
+{
+ applySettings();
+}
+*/
+
+void DATVDemodGUI::on_pushButton_2_clicked()
+{
+ resetToDefaults();
+}
+
+/*
+void DATVDemodGUI::on_spiSampleRate_valueChanged(int arg1)
+{
+ applySettings();
+}
+*/
+
+void DATVDemodGUI::on_spiSymbolRate_valueChanged(int arg1)
+{
+ applySettings();
+}
+
+void DATVDemodGUI::on_spiNotchFilters_valueChanged(int arg1)
+{
+ applySettings();
+}
+
+void DATVDemodGUI::on_chkHDLC_clicked()
+{
+ applySettings();
+}
+
+void DATVDemodGUI::on_chkAllowDrift_clicked()
+{
+ applySettings();
+}
+
+void DATVDemodGUI::on_chkResample_clicked()
+{
+ applySettings();
+}
+
+void DATVDemodGUI::on_pushButton_3_clicked()
+{
+
+ m_blnButtonPlayClicked=true;
+
+ if(m_objDATVDemod!=NULL)
+ {
+ m_objDATVDemod->PlayVideo(true);
+ }
+}
+
+/*
+void DATVDemodGUI::on_mediaStateChanged(QMediaPlayer::State state)
+{
+ switch(state)
+ {
+ case QMediaPlayer::PlayingState:
+ ui->pushButton_3->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
+ break;
+ default:
+ ui->pushButton_3->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
+ break;
+ }
+
+ ui->lblReadStatus->setText(QString("%1").arg(strLitteralState));
+
+}
+*/
+
+void DATVDemodGUI::on_pushButton_4_clicked()
+{
+ ui->screenTV_2->SetFullScreen(true);
+
+}
+
+void DATVDemodGUI::on_mouseEvent(QMouseEvent* obj)
+{
+}
+
+QString DATVDemodGUI::formatBytes(qint64 intBytes)
+{
+ if(intBytes<1024)
+ {
+ return QString("%1").arg(intBytes);
+ }
+ else if(intBytes<1024*1024)
+ {
+ return QString("%1 K").arg((float)(10*intBytes/1024)/10.0f);
+ }
+ else if(intBytes<1024*1024*1024)
+ {
+ return QString("%1 M").arg((float)(10*intBytes/(1024*1024))/10.0f);
+ }
+
+ return QString("%1 G").arg((float)(10*intBytes/(1024*1024*1024))/10.0f);
+}
+
+
+void DATVDemodGUI::on_StreamDataAvailable(int *intPackets, int *intBytes, int *intPercent, qint64 *intTotalReceived)
+{
+ ui->lblStatus->setText(QString("Decod: %1B").arg(formatBytes(*intTotalReceived)));
+ m_intLastDecodedData = *intTotalReceived;
+
+ if((*intPercent)<100)
+ {
+ ui->prgSynchro->setValue(*intPercent);
+ }
+ else
+ {
+ ui->prgSynchro->setValue(100);
+ }
+
+ m_intReadyDecodedData = *intBytes;
+
+}
+
+void DATVDemodGUI::on_spiBandwidth_valueChanged(int arg1)
+{
+ applySettings();
+}
+
+
+void DATVDemodGUI::on_chkFastlock_clicked()
+{
+ applySettings();
+}
diff --git a/plugins/channelrx/demoddatv/datvdemodgui.h b/plugins/channelrx/demoddatv/datvdemodgui.h
new file mode 100644
index 000000000..1f1052d09
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvdemodgui.h
@@ -0,0 +1,147 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// using LeanSDR Framework (C) 2016 F4DAV //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_DATVDEMODGUI_H
+#define INCLUDE_DATVDEMODGUI_H
+
+#include "gui/rollupwidget.h"
+#include
+#include "dsp/channelmarker.h"
+#include "dsp/movingaverage.h"
+
+#include "datvdemod.h"
+
+#include
+#include
+#include
+
+
+class PluginAPI;
+class DeviceUISet;
+class BasebandSampleSink;
+//class DeviceSourceAPI;
+//class ThreadedBasebandSampleSink;
+class DownChannelizer;
+//class DATVDemod;
+
+namespace Ui
+{
+ class DATVDemodGUI;
+}
+
+class DATVDemodGUI : public RollupWidget, public PluginInstanceGUI
+{
+ Q_OBJECT
+
+public:
+ static DATVDemodGUI* create(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
+ virtual void destroy();
+
+ void setName(const QString& strName);
+ QString getName() const;
+ virtual qint64 getCenterFrequency() const;
+ virtual void setCenterFrequency(qint64 intCenterFrequency);
+
+ void resetToDefaults();
+ QByteArray serialize() const;
+ bool deserialize(const QByteArray& arrData);
+
+ virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
+
+ virtual bool handleMessage(const Message& objMessage);
+
+ static const QString m_strChannelID;
+
+private slots:
+ void channelMarkerChangedByCursor();
+ void channelMarkerHighlightedByCursor();
+
+ void channelSampleRateChanged();
+ void onWidgetRolled(QWidget* widget, bool rollDown);
+ void onMenuDoubleClicked();
+ void tick();
+
+ void on_cmbStandard_currentIndexChanged(const QString &arg1);
+ void on_cmbModulation_currentIndexChanged(const QString &arg1);
+ void on_cmbFEC_currentIndexChanged(const QString &arg1);
+ void on_chkViterbi_clicked();
+ void on_chkHardMetric_clicked();
+ //void on_pushButton_clicked();
+
+ void on_pushButton_2_clicked();
+
+ //void on_spiSampleRate_valueChanged(int arg1);
+
+ void on_spiSymbolRate_valueChanged(int arg1);
+
+ void on_spiNotchFilters_valueChanged(int arg1);
+
+ void on_chkHDLC_clicked();
+
+ void on_chkAllowDrift_clicked();
+
+ void on_chkResample_clicked();
+
+ void on_pushButton_3_clicked();
+
+ void on_pushButton_4_clicked();
+
+ void on_mouseEvent(QMouseEvent* obj);
+ void on_StreamDataAvailable(int *intPackets, int *intBytes, int *intPercent, qint64 *intTotalReceived);
+
+ void on_spiBandwidth_valueChanged(int arg1);
+
+
+ void on_chkFastlock_clicked();
+
+private:
+ Ui::DATVDemodGUI* ui;
+ PluginAPI* m_objPluginAPI;
+ DeviceUISet* m_deviceUISet;
+
+ //DeviceSourceAPI* m_objDeviceAPI;
+ ChannelMarker m_objChannelMarker;
+ ThreadedBasebandSampleSink* m_objThreadedChannelizer;
+ DownChannelizer* m_objChannelizer;
+ DATVDemod* m_objDATVDemod;
+ MessageQueue m_inputMessageQueue;
+ int m_intCenterFrequency;
+
+ QTimer m_objTimer;
+ qint64 m_intPreviousDecodedData;
+ qint64 m_intLastDecodedData;
+ qint64 m_intLastSpeed;
+ int m_intReadyDecodedData;
+
+ bool m_blnBasicSettingsShown;
+ bool m_blnDoApplySettings;
+ bool m_blnButtonPlayClicked;
+
+ //explicit DATVDemodGUI(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI, QWidget* objParent = NULL);
+ explicit DATVDemodGUI(PluginAPI* objPluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* objParent = 0);
+ virtual ~DATVDemodGUI();
+
+ void blockApplySettings(bool blnBlock);
+ void applySettings();
+ QString formatBytes(qint64 intBytes);
+
+ void leaveEvent(QEvent*);
+ void enterEvent(QEvent*);
+};
+
+#endif // INCLUDE_DATVDEMODGUI_H
diff --git a/plugins/channelrx/demoddatv/datvdemodgui.ui b/plugins/channelrx/demoddatv/datvdemodgui.ui
new file mode 100644
index 000000000..4b374e721
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvdemodgui.ui
@@ -0,0 +1,637 @@
+
+
+ DATVDemodGUI
+
+
+
+ 0
+ 0
+ 512
+ 520
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 512
+ 520
+
+
+
+
+ 512
+ 520
+
+
+
+
+ Sans Serif
+ 9
+
+
+
+ Qt::StrongFocus
+
+
+ DATV Demodulator
+
+
+
+
+ 10
+ 0
+ 496
+ 250
+
+
+
+
+ 496
+ 250
+
+
+
+
+ 496
+ 250
+
+
+
+ DATV Settings
+
+
+
+
+ 0
+ 20
+ 222
+ 222
+
+
+
+
+ QLayout::SetMinimumSize
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 220
+ 220
+
+
+
+
+ 220
+ 220
+
+
+
+
+
+
+
+
+
+
+
+
+ 230
+ 20
+ 261
+ 221
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+
+ 10
+ 10
+ 81
+ 21
+
+
+ -
+
+ DVB-S
+
+
+ -
+
+ DVB-S2
+
+
+
+
+
+
+ 10
+ 40
+ 80
+ 21
+
+
+ -
+
+ BPSK
+
+
+ -
+
+ QPSK
+
+
+ -
+
+ 8PSK
+
+
+ -
+
+ 16APSK
+
+
+ -
+
+ 32APSK
+
+
+ -
+
+ 64APSKe
+
+
+ -
+
+ 16QAM
+
+
+ -
+
+ 64QAM
+
+
+ -
+
+ 256QAM
+
+
+
+
+
+
+ 10
+ 70
+ 80
+ 21
+
+
+ -
+
+ 1/2
+
+
+ -
+
+ 2/3
+
+
+ -
+
+ 3/4
+
+
+ -
+
+ 5/6
+
+
+ -
+
+ 7/8
+
+
+ -
+
+ 4/5
+
+
+ -
+
+ 8/9
+
+
+ -
+
+ 9/10
+
+
+
+
+
+
+ 10
+ 100
+ 101
+ 20
+
+
+
+ FAST LOCK
+
+
+
+
+
+ 140
+ 140
+ 81
+ 20
+
+
+
+ VITERBI
+
+
+
+
+
+ 10
+ 120
+ 111
+ 20
+
+
+
+ HARD METRIC
+
+
+
+
+
+ 100
+ 40
+ 61
+ 21
+
+
+
+ Symbols/s
+
+
+
+
+
+ 100
+ 10
+ 71
+ 21
+
+
+
+ Bandwidth
+
+
+
+
+
+ 10
+ 140
+ 101
+ 20
+
+
+
+ HDLC
+
+
+
+
+
+ 140
+ 100
+ 111
+ 20
+
+
+
+ ALLOW DRIFT
+
+
+
+
+
+ 170
+ 70
+ 81
+ 23
+
+
+
+ 32
+
+
+
+
+
+ 100
+ 70
+ 71
+ 21
+
+
+
+ Notch filter
+
+
+
+
+
+ 140
+ 120
+ 85
+ 20
+
+
+
+ RESAMPLE
+
+
+
+
+
+ 70
+ 190
+ 181
+ 20
+
+
+
+ 0
+
+
+
+
+
+ 10
+ 170
+ 111
+ 16
+
+
+
+ -
+
+
+
+
+
+ 230
+ 140
+ 21
+ 22
+
+
+
+ R
+
+
+
+
+
+ 170
+ 40
+ 81
+ 23
+
+
+
+ 1
+
+
+ 1024000000
+
+
+ 1000
+
+
+
+
+
+ 170
+ 10
+ 81
+ 23
+
+
+
+ 1000
+
+
+ 1024000000
+
+
+ 1000
+
+
+
+
+
+ 130
+ 170
+ 121
+ 16
+
+
+
+ -
+
+
+
+
+
+ 10
+ 190
+ 61
+ 15
+
+
+
+ Buffer:
+
+
+
+
+
+
+
+ 10
+ 260
+ 496
+ 240
+
+
+
+
+ 496
+ 240
+
+
+
+
+ 496
+ 240
+
+
+
+ VIDEO Stream
+
+
+
+
+ 0
+ 20
+ 358
+ 211
+
+
+
+
+ QLayout::SetMinimumSize
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 356
+ 200
+
+
+
+
+ 356
+ 200
+
+
+
+
+
+
+
+
+
+
+
+
+ 360
+ 20
+ 131
+ 211
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
+
+ 10
+ 10
+ 111
+ 27
+
+
+
+ Video
+
+
+
+
+
+ 10
+ 50
+ 111
+ 27
+
+
+
+ Full Screen
+
+
+
+
+
+ 10
+ 120
+ 111
+ 16
+
+
+
+ -
+
+
+
+
+
+ 10
+ 90
+ 111
+ 16
+
+
+
+ -
+
+
+
+
+
+
+
+ RollupWidget
+ QWidget
+
+ 1
+
+
+ DATVScreen
+ QWidget
+
+
+
+ DATVideoRender
+ QWidget
+
+ 1
+
+
+
+
+
diff --git a/plugins/channelrx/demoddatv/datvdemodplugin.cpp b/plugins/channelrx/demoddatv/datvdemodplugin.cpp
new file mode 100644
index 000000000..4b52fbc09
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvdemodplugin.cpp
@@ -0,0 +1,72 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// using LeanSDR Framework (C) 2016 F4DAV //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+
+#include
+#include
+#include "plugin/pluginapi.h"
+
+#include "datvdemodgui.h"
+#include "datvdemodplugin.h"
+
+const PluginDescriptor DATVDemodPlugin::m_ptrPluginDescriptor =
+{
+ QString("DATV Demodulator"),
+ QString("3.2.0"),
+ QString("(c) F4HKW for SDRAngel using LeanSDR framework (c) F4DAV"),
+ QString("https://github.com/f4exb/sdrangel"),
+ true,
+ QString("https://github.com/f4exb/sdrangel")
+};
+
+DATVDemodPlugin::DATVDemodPlugin(QObject* ptrParent) :
+ QObject(ptrParent),
+ m_ptrPluginAPI(NULL)
+{
+
+}
+
+const PluginDescriptor& DATVDemodPlugin::getPluginDescriptor() const
+{
+ return m_ptrPluginDescriptor;
+
+}
+
+void DATVDemodPlugin::initPlugin(PluginAPI* ptrPluginAPI)
+{
+ m_ptrPluginAPI = ptrPluginAPI;
+
+ // register DATV demodulator
+ m_ptrPluginAPI->registerRxChannel(DATVDemod::m_channelIdURI, DATVDemod::m_channelId, this);
+
+}
+
+PluginInstanceGUI* DATVDemodPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
+{
+ return DATVDemodGUI::create(m_ptrPluginAPI, deviceUISet, rxChannel);
+}
+
+BasebandSampleSink* DATVDemodPlugin::createRxChannelBS(DeviceSourceAPI *deviceAPI)
+{
+ return new DATVDemod(deviceAPI);
+}
+
+ChannelSinkAPI* DATVDemodPlugin::createRxChannelCS(DeviceSourceAPI *deviceAPI)
+{
+ return new DATVDemod(deviceAPI);
+}
diff --git a/plugins/channelrx/demoddatv/datvdemodplugin.h b/plugins/channelrx/demoddatv/datvdemodplugin.h
new file mode 100644
index 000000000..f2f0ca3ec
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvdemodplugin.h
@@ -0,0 +1,53 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// using LeanSDR Framework (C) 2016 F4DAV //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_DATVPLUGIN_H
+#define INCLUDE_DATVPLUGIN_H
+
+#include
+#include "plugin/plugininterface.h"
+
+class DeviceUISet;
+class BasebandSampleSink;
+
+class DATVDemodPlugin : public QObject, PluginInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(PluginInterface)
+ Q_PLUGIN_METADATA(IID "sdrangel.channel.demoddatv")
+
+public:
+ explicit DATVDemodPlugin(QObject* ptrParent = NULL);
+
+ const PluginDescriptor& getPluginDescriptor() const;
+ void initPlugin(PluginAPI* ptrPluginAPI);
+
+ virtual PluginInstanceGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
+ virtual BasebandSampleSink* createRxChannelBS(DeviceSourceAPI *deviceAPI);
+ virtual ChannelSinkAPI* createRxChannelCS(DeviceSourceAPI *deviceAPI);
+
+
+private:
+ static const PluginDescriptor m_ptrPluginDescriptor;
+
+ PluginAPI* m_ptrPluginAPI;
+
+
+};
+
+#endif // INCLUDE_DATVPLUGIN_H
diff --git a/plugins/channelrx/demoddatv/datvideorender.cpp b/plugins/channelrx/demoddatv/datvideorender.cpp
new file mode 100644
index 000000000..c16cc4a0a
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvideorender.cpp
@@ -0,0 +1,557 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "datvideorender.h"
+
+DATVideoRender::DATVideoRender(QWidget * parent):
+ DATVScreen(parent)
+{
+ installEventFilter(this);
+ m_blnIsFullScreen=false;
+ m_blnRunning=false;
+
+ m_blnIsFFMPEGInitialized=false;
+ m_blnIsOpen=false;
+ m_objFormatCtx=NULL;
+ m_objDecoderCtx=NULL;
+ m_objSwsCtx=NULL;
+ m_intVideoStreamIndex=-1;
+
+ m_intCurrentRenderWidth=-1;
+ m_intCurrentRenderHeight=-1;
+
+ m_objFrame=NULL;
+ m_intFrameCount=-1;
+}
+
+bool DATVideoRender::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::MouseButtonRelease)
+ {
+ SetFullScreen(false);
+ return true;
+ }
+ else
+ {
+ // standard event processing
+ return QObject::eventFilter(obj, event);
+ }
+}
+
+
+void DATVideoRender::SetFullScreen(bool blnFullScreen)
+{
+ if(m_blnIsFullScreen==blnFullScreen)
+ {
+ return;
+ }
+
+ if(blnFullScreen==true)
+ {
+ setWindowFlags(Qt::Window);
+ setWindowState(Qt::WindowFullScreen);
+ show();
+
+ m_blnIsFullScreen=true;
+ }
+ else
+ {
+ setWindowFlags(Qt::Widget);
+ setWindowState(Qt::WindowNoState);
+ show();
+
+ m_blnIsFullScreen=false;
+ }
+}
+
+
+static int ReadFunction(void *opaque, uint8_t *buf, int buf_size)
+{
+ QIODevice* objStream = reinterpret_cast(opaque);
+ int intNbBytes = objStream->read((char*)buf, buf_size);
+ return intNbBytes;
+}
+
+static int64_t SeekFunction(void* opaque, int64_t offset, int whence)
+{
+ QIODevice* objStream = reinterpret_cast(opaque);
+
+ if (whence == AVSEEK_SIZE)
+ {
+ return -1;
+ }
+
+ if (objStream->isSequential())
+ {
+ return -1;
+ }
+
+ if (objStream->seek(offset)==false)
+ {
+ return -1;
+ }
+
+ return objStream->pos();
+}
+
+bool DATVideoRender::InitializeFFMPEG()
+{
+ MetaData.CodecID=-1;
+ MetaData.PID=-1;
+ MetaData.Program="";
+ MetaData.Stream="";
+ MetaData.Width=-1;
+ MetaData.Height=-1;
+ MetaData.BitRate=-1;
+ MetaData.Channels=-1;
+ MetaData.CodecDescription= "";
+
+ if(m_blnIsFFMPEGInitialized==true)
+ {
+ return false;
+ }
+
+ avcodec_register_all();
+ av_register_all();
+
+ av_log_set_level(AV_LOG_FATAL);
+ //av_log_set_level(AV_LOG_ERROR);
+
+ m_blnIsFFMPEGInitialized=true;
+
+ return true;
+}
+
+bool DATVideoRender::PreprocessStream()
+{
+ AVDictionary *objOpts = NULL;
+ AVCodec *objCodec = NULL;
+
+ int intRet=-1;
+ char *objBuffer=NULL;
+
+ //Identify stream
+
+ if (avformat_find_stream_info(m_objFormatCtx, NULL) < 0)
+ {
+ avformat_close_input(&m_objFormatCtx);
+ m_objFormatCtx=NULL;
+
+ qDebug() << "DATVideoProcess::PreprocessStream cannot find stream info";
+ return false;
+ }
+
+ //Find video stream
+ intRet = av_find_best_stream(m_objFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
+
+ if (intRet < 0)
+ {
+ avformat_close_input(&m_objFormatCtx);
+ qDebug() << "DATVideoProcess::PreprocessStream cannot find video stream";
+ return false;
+ }
+
+ m_intVideoStreamIndex = intRet;
+
+ //Prepare Codec and extract meta data
+
+ m_objDecoderCtx = m_objFormatCtx->streams[m_intVideoStreamIndex]->codec;
+
+ //Meta Data
+
+ MetaData.PID = m_objFormatCtx->streams[m_intVideoStreamIndex]->id;
+ MetaData.CodecID = m_objDecoderCtx->codec_id;
+
+ MetaData.Program="";
+ MetaData.Stream="";
+
+ if(m_objFormatCtx->programs)
+ {
+ objBuffer=NULL;
+
+ av_dict_get_string(m_objFormatCtx->programs[m_intVideoStreamIndex]->metadata,&objBuffer,':','\n');
+ if(objBuffer!=NULL)
+ {
+ MetaData.Program = QString("%1").arg(objBuffer);
+ }
+ }
+
+ objBuffer=NULL;
+
+ av_dict_get_string(m_objFormatCtx->streams[m_intVideoStreamIndex]->metadata,&objBuffer,':','\n');
+
+ if(objBuffer!=NULL)
+ {
+ MetaData.Stream = QString("%1").arg(objBuffer);
+ }
+
+ //Decoder
+ objCodec = avcodec_find_decoder(m_objDecoderCtx->codec_id);
+ if(objCodec==NULL)
+ {
+ avformat_close_input(&m_objFormatCtx);
+ m_objFormatCtx=NULL;
+
+ qDebug() << "DATVideoProcess::PreprocessStream cannot find associated CODEC";
+ return false;
+ }
+
+ av_dict_set(&objOpts, "refcounted_frames", "1", 0);
+
+ if (avcodec_open2(m_objDecoderCtx, objCodec, &objOpts) < 0)
+ {
+ avformat_close_input(&m_objFormatCtx);
+ m_objFormatCtx=NULL;
+
+ qDebug() << "DATVideoProcess::PreprocessStream cannot open associated CODEC";
+ return false;
+ }
+
+ //Allocate Frame
+ m_objFrame = av_frame_alloc();
+
+ if (!m_objFrame)
+ {
+ avformat_close_input(&m_objFormatCtx);
+ m_objFormatCtx=NULL;
+
+ qDebug() << "DATVideoProcess::PreprocessStream cannot allocate frame";
+ return false;
+ }
+
+ m_intFrameCount=0;
+
+
+ MetaData.Width=m_objDecoderCtx->width;
+ MetaData.Height=m_objDecoderCtx->height;
+ MetaData.BitRate=m_objDecoderCtx->bit_rate;
+ MetaData.Channels=m_objDecoderCtx->channels;
+ MetaData.CodecDescription= QString("%1").arg(objCodec->long_name);
+
+ return true;
+}
+
+bool DATVideoRender::OpenStream(DATVideostream *objDevice)
+{
+ int intIOBufferSize = 32768;
+ unsigned char * ptrIOBuffer = NULL;
+ AVIOContext * objIOCtx = NULL;
+
+ if(m_blnRunning==true)
+ {
+ return false;
+ }
+
+ //Only once execution
+ m_blnRunning=true;
+
+ if(objDevice==NULL)
+ {
+ qDebug() << "DATVideoProcess::OpenStream QIODevice is NULL";
+
+ return false;
+ }
+
+ if(m_blnIsOpen==true)
+ {
+ qDebug() << "DATVideoProcess::OpenStream already open";
+
+ return false;
+ }
+
+ InitializeFFMPEG();
+
+ if(!m_blnIsFFMPEGInitialized)
+ {
+ qDebug() << "DATVideoProcess::OpenStream FFMPEG not initialized";
+
+ return false;
+ }
+
+ if(!objDevice->open(QIODevice::ReadOnly))
+ {
+ qDebug() << "DATVideoProcess::OpenStream cannot open QIODevice";
+
+ return false;
+ }
+
+ //Connect QIODevice to FFMPEG Reader
+
+ m_objFormatCtx = avformat_alloc_context();
+
+ if(m_objFormatCtx==NULL)
+ {
+ qDebug() << "DATVideoProcess::OpenStream cannot alloc format FFMPEG context";
+
+ return false;
+ }
+
+ ptrIOBuffer = (unsigned char *)av_malloc(intIOBufferSize+ FF_INPUT_BUFFER_PADDING_SIZE);
+
+ objIOCtx = avio_alloc_context( ptrIOBuffer,
+ intIOBufferSize,
+ 0,
+ reinterpret_cast(objDevice),
+ &ReadFunction,
+ NULL,
+ &SeekFunction);
+
+ m_objFormatCtx->pb = objIOCtx;
+ m_objFormatCtx->flags |= AVFMT_FLAG_CUSTOM_IO;
+
+
+ if (avformat_open_input(&m_objFormatCtx, NULL , NULL, NULL) < 0)
+ {
+ qDebug() << "DATVideoProcess::OpenStream cannot open stream";
+
+ return false;
+ }
+
+
+ if(!PreprocessStream())
+ {
+ return false;
+ }
+
+
+ m_blnIsOpen=true;
+
+ m_blnRunning=false;
+
+ return true;
+}
+
+bool DATVideoRender::RenderStream()
+{
+ AVPacket objPacket;
+ int intGotFrame;
+ bool blnNeedRenderingSetup;
+
+ if(m_blnIsOpen=false)
+ {
+ qDebug() << "DATVideoProcess::RenderStream Stream not open";
+
+ return false;
+ }
+
+ if(m_blnRunning==true)
+ {
+ return false;
+ }
+
+ //Only once execution
+ m_blnRunning=true;
+
+ //********** Rendering **********
+
+ if (av_read_frame(m_objFormatCtx, &objPacket) < 0)
+ {
+ qDebug() << "DATVideoProcess::RenderStream reading packet error";
+
+ m_blnRunning=false;
+ return false;
+ }
+
+ //Video channel
+ if (objPacket.stream_index == m_intVideoStreamIndex)
+ {
+ memset(m_objFrame, 0, sizeof(AVFrame));
+ av_frame_unref(m_objFrame);
+
+ intGotFrame=0;
+
+ if(avcodec_decode_video2( m_objDecoderCtx, m_objFrame, &intGotFrame, &objPacket)<0)
+ {
+ qDebug() << "DATVideoProcess::RenderStream decoding packet error";
+
+ m_blnRunning=false;
+ return false;
+ }
+
+ if(intGotFrame)
+ {
+ //Rendering and RGB Converter setup
+
+ blnNeedRenderingSetup=(m_intFrameCount==0);
+ blnNeedRenderingSetup|=(m_objSwsCtx==NULL);
+
+ if((m_intCurrentRenderWidth!=m_objFrame->width) || (m_intCurrentRenderHeight!=m_objFrame->height))
+ {
+ blnNeedRenderingSetup=true;
+ }
+
+ if(blnNeedRenderingSetup)
+ {
+ if(m_objSwsCtx!=NULL)
+ {
+ sws_freeContext(m_objSwsCtx);
+ m_objSwsCtx=NULL;
+ }
+
+ //Convertisseur YUV -> RGB
+ m_objSwsCtx = sws_alloc_context();
+
+ av_opt_set_int(m_objSwsCtx,"srcw",m_objFrame->width,0);
+ av_opt_set_int(m_objSwsCtx,"srch",m_objFrame->height,0);
+ av_opt_set_int(m_objSwsCtx,"src_format",m_objFrame->format,0);
+
+ av_opt_set_int(m_objSwsCtx,"dstw",m_objFrame->width,0);
+ av_opt_set_int(m_objSwsCtx,"dsth",m_objFrame->height,0);
+ av_opt_set_int(m_objSwsCtx,"dst_format",AV_PIX_FMT_RGB24 ,0);
+
+ av_opt_set_int(m_objSwsCtx,"sws_flag",SWS_FAST_BILINEAR /* SWS_BICUBIC*/,0);
+
+ if(sws_init_context(m_objSwsCtx, NULL, NULL)<0)
+ {
+ qDebug() << "DATVideoProcess::RenderStream cannont init video data converter";
+
+ m_objSwsCtx=NULL;
+
+ m_blnRunning=false;
+ return false;
+
+ }
+
+ if((m_intCurrentRenderHeight>0) && (m_intCurrentRenderWidth>0))
+ {
+ //av_freep(&m_pbytDecodedData[0]);
+ //av_freep(&m_pintDecodedLineSize[0]);
+ }
+
+ if(av_image_alloc(m_pbytDecodedData, m_pintDecodedLineSize,m_objFrame->width, m_objFrame->height, AV_PIX_FMT_RGB24, 1)<0)
+ {
+ qDebug() << "DATVideoProcess::RenderStream cannont init video image buffer";
+
+ sws_freeContext(m_objSwsCtx);
+ m_objSwsCtx=NULL;
+
+ m_blnRunning=false;
+ return false;
+
+ }
+
+ //Rendering device setup
+
+ resizeDATVScreen(m_objFrame->width,m_objFrame->height);
+ update();
+ resetImage();
+
+ m_intCurrentRenderWidth=m_objFrame->width;
+ m_intCurrentRenderHeight=m_objFrame->height;
+
+ MetaData.Width = m_objFrame->width;
+ MetaData.Height = m_objFrame->height;
+ }
+
+ //Frame rendering
+
+ if(sws_scale(m_objSwsCtx, m_objFrame->data, m_objFrame->linesize, 0, m_objFrame->height, m_pbytDecodedData, m_pintDecodedLineSize)<0)
+ {
+ qDebug() << "DATVideoProcess::RenderStream error converting video frame to RGB";
+
+ m_blnRunning=false;
+ return false;
+ }
+
+ renderImage(m_pbytDecodedData[0]);
+
+ av_frame_unref(m_objFrame);
+
+ m_intFrameCount ++;
+ }
+
+ }
+
+ av_free_packet(&objPacket);
+
+ //********** Rendering **********
+
+ m_blnRunning=false;
+
+ //AVDictionaryEntry *objRslt= av_dict_get(fmt_ctx->programs[video_stream_index]->metadata,"service_provider",NULL,0);
+ //char objErrBuf[1024];
+ //memset(objErrBuf,0,1024);
+ //av_strerror(ret,objErrBuf,1024);
+
+ return true;
+}
+
+bool DATVideoRender::CloseStream(QIODevice *objDevice)
+{
+
+ if(m_blnRunning==true)
+ {
+ return false;
+ }
+
+ //Only once execution
+ m_blnRunning=true;
+
+ if(!objDevice)
+ {
+ qDebug() << "DATVideoProcess::CloseStream QIODevice is NULL";
+
+ return false;
+ }
+
+ if(m_blnIsOpen=false)
+ {
+ qDebug() << "DATVideoProcess::CloseStream Stream not open";
+
+ return false;
+ }
+
+ if(!m_objFormatCtx)
+ {
+ qDebug() << "DATVideoProcess::CloseStream FFMEG Context is not initialized";
+
+ return false;
+ }
+
+ avformat_close_input(&m_objFormatCtx);
+ m_objFormatCtx=NULL;
+
+ if(m_objDecoderCtx)
+ {
+ avcodec_close(m_objDecoderCtx);
+ m_objDecoderCtx=NULL;
+ }
+
+
+ if(m_objFrame)
+ {
+ av_frame_unref(m_objFrame);
+ av_frame_free(&m_objFrame);
+ }
+
+ if(m_objSwsCtx!=NULL)
+ {
+ sws_freeContext(m_objSwsCtx);
+ m_objSwsCtx=NULL;
+ }
+
+ objDevice->close();
+
+ m_blnIsOpen=false;
+
+ m_blnRunning=false;
+
+ m_intCurrentRenderWidth=-1;
+ m_intCurrentRenderHeight=-1;
+
+ return true;
+}
diff --git a/plugins/channelrx/demoddatv/datvideorender.h b/plugins/channelrx/demoddatv/datvideorender.h
new file mode 100644
index 000000000..a6fb1860d
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvideorender.h
@@ -0,0 +1,194 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef DATVIDEORENDER_H
+#define DATVIDEORENDER_H
+
+#include
+#include
+#include
+#include
+
+#include "datvscreen.h"
+#include "datvideostream.h"
+
+extern "C"
+{
+#include "libavcodec/avcodec.h"
+#include "libavformat/avformat.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "libswscale/swscale.h"
+
+}
+
+struct DataTSMetaData2
+{
+ int PID;
+ int CodecID;
+ QString Program;
+ QString Stream;
+
+ int Width;
+ int Height;
+ int BitRate;
+ int Channels;
+ QString CodecDescription;
+
+ DataTSMetaData2()
+ {
+ PID=-1;
+ CodecID=-1;
+ Program="";
+ Stream="";
+
+ Width=-1;
+ Height=-1;
+ BitRate=-1;
+ Channels=-1;
+ CodecDescription="";
+ }
+};
+
+class DATVideoRender : public DATVScreen
+{
+ Q_OBJECT
+public:
+ explicit DATVideoRender(QWidget * parent);
+ void SetFullScreen(bool blnFullScreen);
+
+ bool OpenStream(DATVideostream *objDevice);
+ bool RenderStream();
+ bool CloseStream(QIODevice *objDevice);
+
+ struct DataTSMetaData2 MetaData;
+
+private:
+ bool m_blnRunning;
+ bool m_blnIsFullScreen;
+
+ bool m_blnIsFFMPEGInitialized;
+ bool m_blnIsOpen;
+
+ SwsContext *m_objSwsCtx;
+ AVFormatContext *m_objFormatCtx;
+ AVCodecContext *m_objDecoderCtx;
+ AVFrame *m_objFrame;
+
+ uint8_t *m_pbytDecodedData[4];
+ int m_pintDecodedLineSize[4];
+
+ int m_intFrameCount;
+ int m_intVideoStreamIndex;
+
+ int m_intCurrentRenderWidth;
+ int m_intCurrentRenderHeight;
+
+ bool InitializeFFMPEG();
+ bool PreprocessStream();
+
+
+
+protected:
+ virtual bool eventFilter(QObject *obj, QEvent *event);
+
+signals:
+
+public slots:
+
+};
+
+class DATVideoRenderThread: public QThread
+{
+
+ public:
+ DATVideoRenderThread()
+ {
+ m_objRenderer = NULL;
+ m_objStream = NULL;
+ m_blnRenderingVideo=false;
+ }
+
+ DATVideoRenderThread(DATVideoRender *objRenderer, DATVideostream *objStream)
+ {
+ m_objRenderer = objRenderer;
+ m_objStream = objStream;
+ m_blnRenderingVideo=false;
+ }
+
+ void setStreamAndRenderer(DATVideoRender *objRenderer, DATVideostream *objStream)
+ {
+ m_objRenderer = objRenderer;
+ m_objStream = objStream;
+ m_blnRenderingVideo=false;
+
+ }
+
+ void run()
+ {
+ if(m_blnRenderingVideo==true)
+ {
+ return;
+ }
+
+ if((m_objRenderer==NULL) || (m_objStream==NULL))
+ {
+ return ;
+ }
+
+ m_blnRenderingVideo=false;
+
+ if(m_objRenderer->OpenStream(m_objStream))
+ {
+ printf("PID: %d W: %d H: %d Codec: %s Data: %s %s\r\n",m_objRenderer->MetaData.PID
+ ,m_objRenderer->MetaData.Width
+ ,m_objRenderer->MetaData.Height
+ ,m_objRenderer->MetaData.CodecDescription.toStdString().c_str()
+ ,m_objRenderer->MetaData.Program.toStdString().c_str()
+ ,m_objRenderer->MetaData.Stream.toStdString().c_str());
+
+ m_blnRenderingVideo=true;
+ }
+
+ while((m_objRenderer->RenderStream()) && (m_blnRenderingVideo==true))
+ {
+ }
+
+ m_objRenderer->CloseStream(m_objStream);
+
+ }
+
+ void stopRendering()
+ {
+ m_blnRenderingVideo=false;
+ }
+
+
+ private:
+
+ DATVideoRender *m_objRenderer;
+ DATVideostream *m_objStream;
+ bool m_blnRenderingVideo;
+};
+
+#endif // DATVIDEORENDER_H
diff --git a/plugins/channelrx/demoddatv/datvideostream.cpp b/plugins/channelrx/demoddatv/datvideostream.cpp
new file mode 100644
index 000000000..88807f353
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvideostream.cpp
@@ -0,0 +1,223 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "datvideostream.h"
+#include
+
+DATVideostream::DATVideostream():
+ m_objMutex(QMutex::NonRecursive)
+{
+ cleanUp();
+ m_intTotalReceived=0;
+ m_intPacketReceived=0;
+ m_intMemoryLimit = DefaultMemoryLimit;
+ MultiThreaded=false;
+
+ m_objeventLoop.connect(this,SIGNAL(onDataAvailable()), &m_objeventLoop, SLOT(quit()),Qt::QueuedConnection);
+}
+
+DATVideostream::~DATVideostream()
+{
+ m_objeventLoop.disconnect(this,SIGNAL(onDataAvailable()), &m_objeventLoop, SLOT(quit()));
+ cleanUp();
+}
+
+void DATVideostream::cleanUp()
+{
+ if(m_objFIFO.size()>0)
+ {
+ m_objFIFO.clear();
+ }
+
+ if(m_objeventLoop.isRunning())
+ {
+ m_objeventLoop.exit();
+ }
+
+ m_objMutex.unlock();
+
+ m_intBytesAvailable=0;
+ m_intBytesWaiting=0;
+ m_intQueueWaiting=0;
+ m_intPercentBuffer=0;
+}
+
+bool DATVideostream::setMemoryLimit(int intMemoryLimit)
+{
+ if(intMemoryLimit<=0)
+ {
+ return false;
+ }
+
+ m_intMemoryLimit=intMemoryLimit;
+
+ return true;
+}
+
+int DATVideostream::pushData(const char * chrData, int intSize)
+{
+ if(intSize<=0)
+ {
+ return 0;
+ }
+
+ m_objMutex.lock();
+
+ m_intPacketReceived ++;
+
+ m_intBytesWaiting += intSize;
+ if(m_intBytesWaiting>m_intMemoryLimit)
+ {
+ m_intBytesWaiting -= m_objFIFO.dequeue().size();
+ }
+
+ m_objFIFO.enqueue(QByteArray(chrData,intSize));
+ m_intBytesAvailable = m_objFIFO.head().size();
+ m_intTotalReceived += intSize;
+
+ m_intQueueWaiting=m_objFIFO.count();
+
+ m_objMutex.unlock();
+
+ if((m_objeventLoop.isRunning())
+ && (m_intQueueWaiting>=MinStackSize))
+ {
+ emit onDataAvailable();
+ }
+
+ if(m_intPacketReceived%MinStackSize==1)
+ {
+
+ m_intPercentBuffer = (100*m_intBytesWaiting)/m_intMemoryLimit;
+ if(m_intPercentBuffer>100)
+ {
+ m_intPercentBuffer=100;
+ }
+
+ emit onDataPackets(&m_intQueueWaiting, &m_intBytesWaiting, &m_intPercentBuffer, &m_intTotalReceived);
+ }
+
+ return intSize;
+}
+
+bool DATVideostream::isSequential() const
+{
+ return true;
+}
+
+qint64 DATVideostream::bytesAvailable() const
+{
+ return m_intBytesAvailable;
+}
+
+void DATVideostream::close()
+{
+ QIODevice::close();
+ cleanUp();
+}
+
+bool DATVideostream::open(OpenMode mode)
+{
+ //cleanUp();
+ return QIODevice::open(mode);
+}
+
+//PROTECTED
+
+qint64 DATVideostream::readData(char *data, qint64 len)
+{
+ QByteArray objCurrentArray;
+ int intEffectiveLen=0;
+ int intExpectedLen=0;
+
+ intExpectedLen = (int) len;
+
+ if(intExpectedLen<=0)
+ {
+ return 0;
+ }
+
+ if(m_objeventLoop.isRunning())
+ {
+ return 0;
+ }
+
+ m_objMutex.lock();
+
+ //DATA in FIFO ? -> Waiting for DATA
+ if((m_objFIFO.isEmpty()) || (m_objFIFO.count(). //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef DATVIDEOSTREAM_H
+#define DATVIDEOSTREAM_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MinStackSize 4
+#define DefaultMemoryLimit 2048000
+
+class DATVideostream : public QIODevice
+{
+ Q_OBJECT
+
+public:
+ DATVideostream();
+ ~DATVideostream();
+
+ bool MultiThreaded;
+
+ int pushData(const char * chrData, int intSize);
+ bool setMemoryLimit(int intMemoryLimit);
+
+ virtual bool isSequential() const;
+ virtual qint64 bytesAvailable() const;
+ virtual void close();
+ virtual bool open(OpenMode mode);
+
+ QQueue m_objFIFO;
+
+signals:
+
+ void onDataAvailable();
+ void onDataPackets(int *intDataPackets, int *intDataBytes, int *intPercentBuffer,qint64 *intTotalReceived);
+
+protected:
+
+ virtual qint64 readData(char *data, qint64 len);
+ virtual qint64 writeData(const char *data, qint64 len);
+ virtual qint64 readLineData(char *data, qint64 maxSize);
+
+private:
+
+ QEventLoop m_objeventLoop;
+ QMutex m_objMutex;
+ int m_intMemoryLimit;
+ int m_intBytesAvailable;
+ int m_intBytesWaiting;
+ int m_intQueueWaiting;
+ int m_intPercentBuffer;
+ qint64 m_intTotalReceived;
+ qint64 m_intPacketReceived;
+
+ void cleanUp();
+};
+
+#endif // DATVIDEOSTREAM_H
diff --git a/plugins/channelrx/demoddatv/datvscreen.cpp b/plugins/channelrx/demoddatv/datvscreen.cpp
new file mode 100644
index 000000000..ea74c249b
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvscreen.cpp
@@ -0,0 +1,206 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// //
+// OpenGL interface modernization. //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+#include
+#include
+#include
+#include
+#include "datvscreen.h"
+
+#include
+#include
+
+DATVScreen::DATVScreen(QWidget* parent) :
+ QGLWidget(parent), m_objMutex(QMutex::NonRecursive)
+{
+ setAttribute(Qt::WA_OpaquePaintEvent);
+ connect(&m_objTimer, SIGNAL(timeout()), this, SLOT(tick()));
+ m_objTimer.start(40); // capped at 25 FPS
+
+ m_chrLastData = NULL;
+ m_blnConfigChanged = false;
+ m_blnDataChanged = false;
+ m_blnGLContextInitialized = false;
+
+ //Par défaut
+ m_intAskedCols = DATV_COLS;
+ m_intAskedRows = DATV_ROWS;
+
+}
+
+DATVScreen::~DATVScreen()
+{
+ cleanup();
+}
+
+QRgb* DATVScreen::getRowBuffer(int intRow)
+{
+ if (m_blnGLContextInitialized == false)
+ {
+ return NULL;
+ }
+
+ return m_objGLShaderArray.GetRowBuffer(intRow);
+}
+
+void DATVScreen::renderImage(unsigned char * objData)
+{
+ m_chrLastData = objData;
+ m_blnDataChanged = true;
+ //update();
+}
+
+void DATVScreen::resetImage()
+{
+ m_objGLShaderArray.ResetPixels();
+}
+
+void DATVScreen::resizeDATVScreen(int intCols, int intRows)
+{
+ m_intAskedCols = intCols;
+ m_intAskedRows = intRows;
+}
+
+void DATVScreen::initializeGL()
+{
+ m_objMutex.lock();
+
+ QOpenGLContext *objGlCurrentContext = QOpenGLContext::currentContext();
+
+ if (objGlCurrentContext)
+ {
+ if (QOpenGLContext::currentContext()->isValid())
+ {
+ qDebug() << "DATVScreen::initializeGL: context:"
+ << " major: " << (QOpenGLContext::currentContext()->format()).majorVersion()
+ << " minor: " << (QOpenGLContext::currentContext()->format()).minorVersion()
+ << " ES: " << (QOpenGLContext::currentContext()->isOpenGLES() ? "yes" : "no");
+ }
+ else
+ {
+ qDebug() << "DATVScreen::initializeGL: current context is invalid";
+ }
+ }
+ else
+ {
+ qCritical() << "DATVScreen::initializeGL: no current context";
+ return;
+ }
+
+ QSurface *objSurface = objGlCurrentContext->surface();
+
+ if (objSurface == NULL)
+ {
+ qCritical() << "DATVScreen::initializeGL: no surface attached";
+ return;
+ }
+ else
+ {
+ if (objSurface->surfaceType() != QSurface::OpenGLSurface)
+ {
+ qCritical() << "DATVScreen::initializeGL: surface is not an OpenGLSurface: "
+ << objSurface->surfaceType()
+ << " cannot use an OpenGL context";
+ return;
+ }
+ else
+ {
+ qDebug() << "DATVScreen::initializeGL: OpenGL surface:"
+ << " class: " << (objSurface->surfaceClass() == QSurface::Window ? "Window" : "Offscreen");
+ }
+ }
+
+ connect(objGlCurrentContext, &QOpenGLContext::aboutToBeDestroyed, this,
+ &DATVScreen::cleanup); // TODO: when migrating to QOpenGLWidget
+
+ m_blnGLContextInitialized = true;
+
+ m_objMutex.unlock();
+}
+
+void DATVScreen::resizeGL(int intWidth, int intHeight)
+{
+ QOpenGLFunctions *ptrF = QOpenGLContext::currentContext()->functions();
+ ptrF->glViewport(0, 0, intWidth, intHeight);
+ m_blnConfigChanged = true;
+}
+
+void DATVScreen::paintGL()
+{
+ if (!m_objMutex.tryLock(2))
+ return;
+
+ m_blnDataChanged = false;
+
+ if ((m_intAskedCols != 0) && (m_intAskedRows != 0))
+ {
+ m_objGLShaderArray.InitializeGL(m_intAskedCols, m_intAskedRows);
+ m_intAskedCols = 0;
+ m_intAskedRows = 0;
+ }
+
+ m_objGLShaderArray.RenderPixels(m_chrLastData);
+
+ m_objMutex.unlock();
+}
+
+void DATVScreen::mousePressEvent(QMouseEvent* event)
+{
+}
+
+void DATVScreen::tick()
+{
+ if (m_blnDataChanged) {
+ update();
+ }
+}
+
+void DATVScreen::connectTimer(const QTimer& objTimer)
+{
+ qDebug() << "DATVScreen::connectTimer";
+ disconnect(&m_objTimer, SIGNAL(timeout()), this, SLOT(tick()));
+ connect(&objTimer, SIGNAL(timeout()), this, SLOT(tick()));
+ m_objTimer.stop();
+}
+
+void DATVScreen::cleanup()
+{
+ if (m_blnGLContextInitialized)
+ {
+ m_objGLShaderArray.Cleanup();
+ }
+}
+
+bool DATVScreen::selectRow(int intLine)
+{
+ if (m_blnGLContextInitialized)
+ {
+ return m_objGLShaderArray.SelectRow(intLine);
+ }
+}
+
+bool DATVScreen::setDataColor(int intCol, int intRed, int intGreen, int intBlue)
+{
+ if (m_blnGLContextInitialized)
+ {
+ return m_objGLShaderArray.SetDataColor(intCol,
+ qRgb(intRed, intGreen, intBlue));
+ }
+}
diff --git a/plugins/channelrx/demoddatv/datvscreen.h b/plugins/channelrx/demoddatv/datvscreen.h
new file mode 100644
index 000000000..05c2ceb68
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvscreen.h
@@ -0,0 +1,96 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// //
+// OpenGL interface modernization. //
+// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_DATVSCREEN_H
+#define INCLUDE_DATVSCREEN_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "dsp/dsptypes.h"
+#include "glshaderarray.h"
+#include "gui/glshadertextured.h"
+#include "util/export.h"
+#include "util/bitfieldindex.h"
+
+
+
+class QPainter;
+
+
+
+class SDRANGEL_API DATVScreen: public QGLWidget
+{
+ Q_OBJECT
+
+public:
+
+ DATVScreen(QWidget* parent = NULL);
+ ~DATVScreen();
+
+ void resizeDATVScreen(int intCols, int intRows);
+ void renderImage(unsigned char * objData);
+ QRgb* getRowBuffer(int intRow);
+ void resetImage();
+
+ bool selectRow(int intLine);
+ bool setDataColor(int intCol,int intRed, int intGreen, int intBlue);
+
+ void connectTimer(const QTimer& timer);
+
+ //Valeurs par défaut
+ static const int DATV_COLS=256;
+ static const int DATV_ROWS=256;
+
+signals:
+ void traceSizeChanged(int);
+ void sampleRateChanged(int);
+
+private:
+ bool m_blnGLContextInitialized;
+ int m_intAskedCols;
+ int m_intAskedRows;
+
+
+ // state
+ QTimer m_objTimer;
+ QMutex m_objMutex;
+ bool m_blnDataChanged;
+ bool m_blnConfigChanged;
+
+ GLShaderArray m_objGLShaderArray;
+
+ void initializeGL();
+ void resizeGL(int width, int height);
+ void paintGL();
+
+ void mousePressEvent(QMouseEvent*);
+
+ unsigned char *m_chrLastData;
+
+protected slots:
+ void cleanup();
+ void tick();
+};
+
+#endif // INCLUDE_DATVSCREEN_H
diff --git a/plugins/channelrx/demoddatv/datvvideoplayer.h b/plugins/channelrx/demoddatv/datvvideoplayer.h
new file mode 100644
index 000000000..3ea33e368
--- /dev/null
+++ b/plugins/channelrx/demoddatv/datvvideoplayer.h
@@ -0,0 +1,55 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4HKW //
+// for F4EXB / SDRAngel //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef DATVVIDEOPLAYER_H
+#define DATVVIDEOPLAYER_H
+
+#include "leansdr/framework.h"
+#include "datvideostream.h"
+
+namespace leansdr
+{
+ template struct datvvideoplayer : runnable
+ {
+ datvvideoplayer(scheduler *sch, pipebuf &_in, DATVideostream * objVideoStream) :
+ runnable(sch, _in.name),
+ in(_in),
+ m_objVideoStream(objVideoStream)
+ {
+ }
+
+ void run()
+ {
+ int size = in.readable() * sizeof(T);
+ if ( ! size ) return;
+
+ int nw = m_objVideoStream->pushData((const char *)in.rd(),size);
+
+ if ( ! nw ) fatal("pipe");
+ if ( nw < 0 ) fatal("write");
+ if ( nw % sizeof(T) ) fatal("partial write");
+ in.read(nw/sizeof(T));
+
+ }
+ private:
+ pipereader in;
+ DATVideostream * m_objVideoStream;
+ };
+
+}
+
+#endif // DATVVIDEOPLAYER_H
diff --git a/plugins/channelrx/demoddatv/demoddatv.pro b/plugins/channelrx/demoddatv/demoddatv.pro
new file mode 100644
index 000000000..a0b3cd352
--- /dev/null
+++ b/plugins/channelrx/demoddatv/demoddatv.pro
@@ -0,0 +1,67 @@
+#--------------------------------------------------------
+#
+# Pro file for Android and Windows builds with Qt Creator
+#
+#--------------------------------------------------------
+
+TEMPLATE = lib
+CONFIG += plugin
+
+QT += core gui multimedia multimediawidgets widgets opengl
+
+TARGET = demoddatv
+
+DEFINES += USE_SSE2=1
+QMAKE_CXXFLAGS += -msse2
+DEFINES += USE_SSE4_1=1
+QMAKE_CXXFLAGS += -msse4.1
+QMAKE_CXXFLAGS += -std=c++11
+
+INCLUDEPATH += $$PWD
+INCLUDEPATH += ../../../sdrbase
+INCLUDEPATH += ../../../sdrgui
+
+CONFIG(Release):build_subdir = release
+CONFIG(Debug):build_subdir = debug
+
+CONFIG(MINGW32):INCLUDEPATH += "D:\boost_1_58_0"
+CONFIG(MINGW64):INCLUDEPATH += "D:\boost_1_58_0"
+CONFIG(macx):INCLUDEPATH += "../../../../../boost_1_64_0"
+
+SOURCES += datvdemod.cpp\
+ datvdemodgui.cpp\
+ datvdemodplugin.cpp\
+ datvscreen.cpp \
+ glshaderarray.cpp \
+ datvideostream.cpp \
+ datvideorender.cpp
+
+HEADERS += datvdemod.h\
+ datvdemodgui.h\
+ datvdemodplugin.h\
+ datvscreen.h \
+ leansdr/convolutional.h \
+ leansdr/dsp.h \
+ leansdr/dvb.h \
+ leansdr/filtergen.h \
+ leansdr/framework.h \
+ leansdr/generic.h \
+ leansdr/hdlc.h \
+ leansdr/iess.h \
+ leansdr/math.h \
+ leansdr/rs.h \
+ leansdr/sdr.h \
+ leansdr/viterbi.h \
+ datvconstellation.h \
+ glshaderarray.h \
+ datvvideoplayer.h \
+ datvideostream.h \
+ datvideorender.h
+
+FORMS += datvdemodgui.ui
+
+LIBS += -L../../../sdrbase/$${build_subdir} -lsdrbase
+LIBS += -L../../../sdrgui/$${build_subdir} -lsdrgui
+LIBS += -lavutil -lswscale -lavdevice -lavformat -lavcodec -lswresample
+
+RESOURCES = ../../../sdrbase/resources/res.qrc
diff --git a/plugins/channelrx/demoddatv/glshaderarray.cpp b/plugins/channelrx/demoddatv/glshaderarray.cpp
new file mode 100644
index 000000000..bb4bae585
--- /dev/null
+++ b/plugins/channelrx/demoddatv/glshaderarray.cpp
@@ -0,0 +1,301 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 F4HKW //
+// for F4EXB / SDRAngel //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "glshaderarray.h"
+
+const QString GLShaderArray::m_strVertexShaderSourceArray = QString(
+ "uniform highp mat4 uMatrix;\n"
+ "attribute highp vec4 vertex;\n"
+ "attribute highp vec2 texCoord;\n"
+ "varying mediump vec2 texCoordVar;\n"
+ "void main() {\n"
+ " gl_Position = uMatrix * vertex;\n"
+ " texCoordVar = texCoord;\n"
+ "}\n");
+
+const QString GLShaderArray::m_strFragmentShaderSourceColored = QString(
+ "uniform lowp sampler2D uTexture;\n"
+ "varying mediump vec2 texCoordVar;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(uTexture, texCoordVar);\n"
+ "}\n");
+
+GLShaderArray::GLShaderArray()
+{
+ m_objProgram = 0;
+ m_objImage = 0;
+ m_objTexture = 0;
+ m_intCols = 0;
+ m_intRows = 0;
+ m_blnInitialized = false;
+ m_objCurrentRow = 0;
+
+ m_objTextureLoc = 0;
+ m_objColorLoc = 0;
+ m_objMatrixLoc = 0;
+}
+
+GLShaderArray::~GLShaderArray()
+{
+ Cleanup();
+}
+
+void GLShaderArray::InitializeGL(int intCols, int intRows)
+{
+ QMatrix4x4 objQMatrix;
+
+ m_blnInitialized = false;
+
+ m_intCols = 0;
+ m_intRows = 0;
+
+ m_objCurrentRow = 0;
+
+ if (m_objProgram == 0)
+ {
+ m_objProgram = new QOpenGLShaderProgram();
+
+ if (!m_objProgram->addShaderFromSourceCode(QOpenGLShader::Vertex,
+ m_strVertexShaderSourceArray))
+ {
+ qDebug() << "GLShaderArray::initializeGL: error in vertex shader: "
+ << m_objProgram->log();
+ }
+
+ if (!m_objProgram->addShaderFromSourceCode(QOpenGLShader::Fragment,
+ m_strFragmentShaderSourceColored))
+ {
+ qDebug()
+ << "GLShaderArray::initializeGL: error in fragment shader: "
+ << m_objProgram->log();
+ }
+
+ m_objProgram->bindAttributeLocation("vertex", 0);
+
+ if (!m_objProgram->link())
+ {
+ qDebug() << "GLShaderArray::initializeGL: error linking shader: "
+ << m_objProgram->log();
+ }
+
+ m_objProgram->bind();
+ m_objProgram->setUniformValue(m_objMatrixLoc, objQMatrix);
+ m_objProgram->setUniformValue(m_objTextureLoc, 0);
+ m_objProgram->release();
+ }
+
+ m_objMatrixLoc = m_objProgram->uniformLocation("uMatrix");
+ m_objTextureLoc = m_objProgram->uniformLocation("uTexture");
+ m_objColorLoc = m_objProgram->uniformLocation("uColour");
+
+ if (m_objTexture != 0)
+ {
+ delete m_objTexture;
+ m_objTexture = 0;
+ }
+
+ //Image container
+ m_objImage = new QImage(intCols, intRows, QImage::Format_RGBA8888);
+ m_objImage->fill(QColor(0, 0, 0));
+
+ m_objTexture = new QOpenGLTexture(*m_objImage);
+ m_objTexture->setMinificationFilter(QOpenGLTexture::Linear);
+ m_objTexture->setMagnificationFilter(QOpenGLTexture::Linear);
+ m_objTexture->setWrapMode(QOpenGLTexture::ClampToEdge);
+
+ m_intCols = intCols;
+ m_intRows = intRows;
+
+ m_blnInitialized = true;
+
+}
+
+QRgb * GLShaderArray::GetRowBuffer(int intRow)
+{
+ if (m_blnInitialized == false)
+ {
+ return 0;
+ }
+
+ if (m_objImage == 0)
+ {
+ return 0;
+ }
+
+ if (intRow > m_intRows)
+ {
+ return 0;
+ }
+
+ return (QRgb *) m_objImage->scanLine(intRow);
+}
+
+void GLShaderArray::RenderPixels(unsigned char *chrData)
+{
+ QOpenGLFunctions *ptrF;
+ int intI;
+ int intJ;
+ int intNbVertices = 6;
+
+ QMatrix4x4 objQMatrix;
+
+ GLfloat arrVertices[] =
+ // 2 3
+ // 1 4
+ //1 2 3 3 4 1
+ { -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f };
+
+ GLfloat arrTextureCoords[] =
+ // 1 4
+ // 2 3
+ //1 2 3 3 4 1
+ { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f };
+
+ QRgb *ptrLine;
+
+ if (m_blnInitialized == false)
+ {
+ return;
+ }
+
+ if (m_objImage == 0)
+ {
+ return;
+ }
+
+ if (chrData != 0)
+ {
+ for (intJ = 0; intJ < m_intRows; intJ++)
+ {
+ ptrLine = (QRgb *) m_objImage->scanLine(intJ);
+
+ for (intI = 0; intI < m_intCols; intI++)
+ {
+
+ *ptrLine = qRgb((int) (*(chrData+2)), (int) (*(chrData+1)), (int) (*chrData));
+ ptrLine++;
+
+ chrData+=3;
+ }
+ }
+ }
+
+ //Affichage
+ ptrF = QOpenGLContext::currentContext()->functions();
+
+ m_objProgram->bind();
+
+ m_objProgram->setUniformValue(m_objMatrixLoc, objQMatrix);
+ m_objProgram->setUniformValue(m_objTextureLoc, 0);
+
+ m_objTexture->bind();
+
+ ptrF->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_intCols, m_intRows, GL_RGBA,
+ GL_UNSIGNED_BYTE, m_objImage->bits());
+
+ ptrF->glEnableVertexAttribArray(0); // vertex
+ ptrF->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, arrVertices);
+
+ ptrF->glEnableVertexAttribArray(1); // texture coordinates
+ ptrF->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, arrTextureCoords);
+
+ ptrF->glDrawArrays(GL_TRIANGLES, 0, intNbVertices);
+
+ //cleanup
+ ptrF->glDisableVertexAttribArray(0);
+ ptrF->glDisableVertexAttribArray(1);
+
+ //*********************//
+
+ m_objTexture->release();
+ m_objProgram->release();
+
+}
+
+void GLShaderArray::ResetPixels()
+{
+ if (m_objImage != 0)
+ {
+ m_objImage->fill(0);
+ }
+}
+
+void GLShaderArray::Cleanup()
+{
+ m_blnInitialized = false;
+
+ m_intCols = 0;
+ m_intRows = 0;
+
+ m_objCurrentRow = 0;
+
+ if (m_objProgram)
+ {
+ delete m_objProgram;
+ m_objProgram = 0;
+ }
+
+ if (m_objTexture != 0)
+ {
+ delete m_objTexture;
+ m_objTexture = 0;
+ }
+
+ if (m_objImage != 0)
+ {
+ delete m_objImage;
+ m_objImage = 0;
+ }
+}
+
+bool GLShaderArray::SelectRow(int intLine)
+{
+ bool blnRslt = false;
+
+ if (m_blnInitialized)
+ {
+ if ((intLine < m_intRows) && (intLine >= 0))
+ {
+ m_objCurrentRow = (QRgb *) m_objImage->scanLine(intLine);
+ blnRslt = true;
+ }
+ else
+ {
+ m_objCurrentRow = 0;
+ }
+ }
+
+ return blnRslt;
+}
+
+bool GLShaderArray::SetDataColor(int intCol, QRgb objColor)
+{
+ bool blnRslt = false;
+
+ if (m_blnInitialized)
+ {
+ if ((intCol < m_intCols) && (intCol >= 0) && (m_objCurrentRow != 0))
+ {
+ m_objCurrentRow[intCol] = objColor;
+ blnRslt = true;
+ }
+ }
+
+
+ return blnRslt;
+}
+
diff --git a/plugins/channelrx/demoddatv/glshaderarray.h b/plugins/channelrx/demoddatv/glshaderarray.h
new file mode 100644
index 000000000..4c34969f3
--- /dev/null
+++ b/plugins/channelrx/demoddatv/glshaderarray.h
@@ -0,0 +1,76 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 F4HKW //
+// for F4EXB / SDRAngel //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program 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 General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_GUI_GLSHADERARRAY_H_
+#define INCLUDE_GUI_GLSHADERARRAY_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class QOpenGLShaderProgram;
+class QMatrix4x4;
+class QVector4D;
+
+class GLShaderArray
+{
+public:
+ GLShaderArray();
+ ~GLShaderArray();
+
+ void InitializeGL(int intCols, int intRows);
+ void ResizeContainer(int intCols, int intRows);
+ void Cleanup();
+ QRgb *GetRowBuffer(int intRow);
+ void RenderPixels(unsigned char *chrData);
+ void ResetPixels();
+
+ bool SelectRow(int intLine);
+ bool SetDataColor(int intCol,QRgb objColor);
+
+
+protected:
+
+ QOpenGLShaderProgram *m_objProgram;
+ int m_objMatrixLoc;
+ int m_objTextureLoc;
+ int m_objColorLoc;
+ static const QString m_strVertexShaderSourceArray;
+ static const QString m_strFragmentShaderSourceColored;
+
+ QImage *m_objImage=NULL;
+ QOpenGLTexture *m_objTexture=NULL;
+
+ int m_intCols;
+ int m_intRows;
+
+ QRgb * m_objCurrentRow;
+
+ bool m_blnInitialized;
+};
+
+#endif /* INCLUDE_GUI_GLSHADERARRAY_H_ */
diff --git a/plugins/channelrx/demoddatv/leansdr/convolutional.h b/plugins/channelrx/demoddatv/leansdr/convolutional.h
new file mode 100644
index 000000000..2b6e909d9
--- /dev/null
+++ b/plugins/channelrx/demoddatv/leansdr/convolutional.h
@@ -0,0 +1,257 @@
+#ifndef LEANSDR_CONVOLUTIONAL_H
+#define LEANSDR_CONVOLUTIONAL_H
+
+namespace leansdr {
+
+ // ALGEBRAIC DECONVOLUTION
+
+ // QPSK 1/2 only.
+ // This is a straightforward implementation, provided for reference.
+ // deconvol_poly2 is functionally equivalent and much faster.
+
+ template
+ struct deconvol_poly {
+ typedef u8 hardsymbol;
+
+ // Support soft of float input
+ inline u8 SYMVAL(const hardsymbol *s) { return *s; }
+ inline u8 SYMVAL(const softsymbol *s) { return s->symbol; }
+
+ typedef u8 decoded_byte;
+
+ deconvol_poly() : hist(0) { }
+
+ // Remap and deconvolve [nb*8] symbols into [nb] bytes.
+ // Return estimated number of bit errors.
+
+ int run(const Tin *pin, const u8 remap[], decoded_byte *pout, int nb) {
+ int nerrors = 0;
+ int halfway = nb / 2;
+ for ( ; nb--; ++pout ) {
+ decoded_byte byte = 0;
+ for ( int bit=8; bit--; ++pin) {
+ hist = (hist<<2) | remap[SYMVAL(pin)];
+ byte = (byte<<1) | parity(hist&POLY_DECONVOL);
+ if ( nb < halfway )
+ nerrors += parity(hist&POLY_ERRORS);
+ }
+ *pout = byte;
+ }
+ return nerrors;
+ }
+
+ private:
+ Thist hist;
+
+ }; // deconvol_poly
+
+
+ // ALGEBRAIC DECONVOLUTION, OPTIMIZED
+
+ // QPSK 1/2 only.
+ // Functionally equivalent to deconvol_poly,
+ // but processing 32 bits in parallel.
+
+ template
+ struct deconvol_poly2 {
+ typedef u8 hardsymbol;
+
+ // Support instanciation of template with soft of float input
+ inline u8 SYMVAL(const hardsymbol *s) { return *s; }
+ inline u8 SYMVAL(const softsymbol *s) { return s->symbol; }
+
+ typedef u8 decoded_byte;
+
+ deconvol_poly2() : inI(0), inQ(0) { }
+
+ // Remap and deconvolve [nb*8] symbols into [nb] bytes.
+ // Return estimated number of bit errors.
+
+ int run(const Tin *pin, const u8 remap[], decoded_byte *pout, int nb) {
+ if ( nb & (sizeof(Thist)-1) )
+ fail("Must deconvolve sizeof(Thist) bytes at a time");
+ nb /= sizeof(Thist);
+ unsigned long nerrors = 0;
+ int halfway = nb / 2;
+ Thist histI=inI, histQ=inQ;
+ for ( ; nb--; ) {
+ // This is where we convolve bits in parallel.
+ Thist wd = 0; // decoded bits
+ Thist we = 0; // error bits (should be 0)
+#if 0
+ // Trust gcc to unroll and evaluate the bit tests at compile-time.
+ for ( int bit=sizeof(Thist)*8; bit--; ++pin ) {
+ u8 iq = remap[SYMVAL(pin)];
+ histI = (histI<<1) | (iq>>1);
+ histQ = (histQ<<1) | (iq&1);
+ if ( POLY_DECONVOL & ((Tpoly)2<<(2*bit)) ) wd ^= histI;
+ if ( POLY_DECONVOL & ((Tpoly)1<<(2*bit)) ) wd ^= histQ;
+ if ( POLY_ERRORS & ((Tpoly)2<<(2*bit)) ) we ^= histI;
+ if ( POLY_ERRORS & ((Tpoly)1<<(2*bit)) ) we ^= histQ;
+ }
+#else
+ // Unroll manually.
+#define LOOP(bit) { \
+ u8 iq = remap[SYMVAL(pin)]; \
+ histI = (histI<<1) | (iq>>1); \
+ histQ = (histQ<<1) | (iq&1); \
+ if ( POLY_DECONVOL & ((Tpoly)2<<(2*bit)) ) wd ^= histI; \
+ if ( POLY_DECONVOL & ((Tpoly)1<<(2*bit)) ) wd ^= histQ; \
+ if ( POLY_ERRORS & ((Tpoly)2<<(2*bit)) ) we ^= histI; \
+ if ( POLY_ERRORS & ((Tpoly)1<<(2*bit)) ) we ^= histQ; \
+ ++pin; \
+ }
+ // Don't shift by more than the operand width
+ switch ( sizeof(Thist)*8 ) {
+#if 0 // Not needed yet - avoid compiler warnings
+ case 64:
+ LOOP(63); LOOP(62); LOOP(61); LOOP(60);
+ LOOP(59); LOOP(58); LOOP(57); LOOP(56);
+ LOOP(55); LOOP(54); LOOP(53); LOOP(52);
+ LOOP(51); LOOP(50); LOOP(49); LOOP(48);
+ LOOP(47); LOOP(46); LOOP(45); LOOP(44);
+ LOOP(43); LOOP(42); LOOP(41); LOOP(40);
+ LOOP(39); LOOP(38); LOOP(37); LOOP(36);
+ LOOP(35); LOOP(34); LOOP(33); LOOP(32);
+ // Fall-through
+#endif
+ case 32:
+ LOOP(31); LOOP(30); LOOP(29); LOOP(28);
+ LOOP(27); LOOP(26); LOOP(25); LOOP(24);
+ LOOP(23); LOOP(22); LOOP(21); LOOP(20);
+ LOOP(19); LOOP(18); LOOP(17); LOOP(16);
+ // Fall-through
+ case 16:
+ LOOP(15); LOOP(14); LOOP(13); LOOP(12);
+ LOOP(11); LOOP(10); LOOP( 9); LOOP( 8);
+ // Fall-through
+ case 8:
+ LOOP( 7); LOOP( 6); LOOP( 5); LOOP( 4);
+ LOOP( 3); LOOP( 2); LOOP( 1); LOOP( 0);
+ break;
+ default:
+ fail("Thist not supported");
+ }
+#undef LOOP
+#endif
+ switch ( sizeof(Thist)*8 ) {
+#if 0 // Not needed yet - avoid compiler warnings
+ case 64:
+ *pout++ = wd >> 56;
+ *pout++ = wd >> 48;
+ *pout++ = wd >> 40;
+ *pout++ = wd >> 32;
+ // Fall-through
+#endif
+ case 32:
+ *pout++ = wd >> 24;
+ *pout++ = wd >> 16;
+ // Fall-through
+ case 16:
+ *pout++ = wd >> 8;
+ // Fall-through
+ case 8:
+ *pout++ = wd;
+ break;
+ default:
+ fail("Thist not supported");
+ }
+ // Count errors when the shift registers are full
+ if ( nb < halfway ) nerrors += hamming_weight(we);
+ }
+ inI = histI;
+ inQ = histQ;
+ return nerrors;
+ }
+ private:
+ Thist inI, inQ;
+ }; // deconvol_poly2
+
+
+ // CONVOLUTIONAL ENCODER
+
+ // QPSK 1/2 only.
+
+ template
+ struct convol_poly2 {
+ typedef u8 uncoded_byte;
+ typedef u8 hardsymbol;
+
+ convol_poly2() : hist(0) { }
+
+ // Convolve [count] bytes into [count*8] symbols, and remap.
+
+ void run(const uncoded_byte *pin, const u8 remap[],
+ hardsymbol *pout, int count) {
+ for ( ; count--; ++pin ) {
+ uncoded_byte b = *pin;
+ for ( int bit=8; bit--; ++pout ) {
+ hist = (hist>>1) | (((b>>bit)&1)<<6);
+ u8 s = (parity(hist&POLY1)<<1) | parity(hist&POLY2);
+ *pout = remap[s];
+ }
+ }
+ }
+ private:
+ Thist hist;
+ }; // convol_poly2
+
+ // Generic BPSK..256QAM and puncturing
+
+ template
+ struct convol_multipoly {
+ typedef u8 uncoded_byte;
+ typedef u8 hardsymbol;
+ int bits_in, bits_out, bps;
+ const Thist *polys; // [bits_out]
+
+ convol_multipoly()
+ : bits_in(0), bits_out(0), bps(0),
+ hist(0), nhist(0), sersymb(0), nsersymb(0)
+ { }
+
+ void encode(const uncoded_byte *pin, hardsymbol *pout, int count) {
+ if ( !bits_in || !bits_out || !bps )
+ fatal("convol_multipoly not configured");
+ hardsymbol symbmask = (1<>1) | ((Thist)((b>>bit)&1)<<(HISTSIZE-1));
+ ++nhist;
+ if ( nhist == bits_in ) {
+ for ( int p=0; p= bps ) {
+ hardsymbol s = (sersymb >> (nsersymb-bps)) & symbmask;
+ *pout++ = s;
+ nsersymb -= bps;
+ }
+ }
+ }
+ }
+ // Ensure deterministic output size
+ // TBD We can relax this
+ if ( nhist || nsersymb ) fatal("partial run");
+ }
+ private:
+ Thist hist;
+ int nhist;
+ Thist sersymb;
+ int nsersymb;
+ }; // convol_multipoly
+
+} // namespace
+
+#endif // LEANSDR_CONVOLUTIONAL_H
diff --git a/plugins/channelrx/demoddatv/leansdr/dsp.h b/plugins/channelrx/demoddatv/leansdr/dsp.h
new file mode 100644
index 000000000..cc66d73c5
--- /dev/null
+++ b/plugins/channelrx/demoddatv/leansdr/dsp.h
@@ -0,0 +1,351 @@
+#ifndef LEANSDR_DSP_H
+#define LEANSDR_DSP_H
+
+#include
+#include "leansdr/framework.h"
+#include "leansdr/math.h"
+
+namespace leansdr {
+
+ //////////////////////////////////////////////////////////////////////
+ // DSP blocks
+ //////////////////////////////////////////////////////////////////////
+
+ // [cconverter] converts complex streams between numric types,
+ // with optional ofsetting and rational scaling.
+ template
+ struct cconverter : runnable {
+ cconverter(scheduler *sch, pipebuf< complex > &_in,
+ pipebuf< complex > &_out)
+ : runnable(sch, "cconverter"),
+ in(_in), out(_out) {
+ }
+ void run() {
+ unsigned long count = min(in.readable(), out.writable());
+ complex *pin=in.rd(), *pend=pin+count;
+ complex *pout = out.wr();
+ for ( ; pinre = Zout + (pin->re-(Tin)Zin)*Gn/Gd;
+ pout->im = Zout + (pin->im-(Tin)Zin)*Gn/Gd;
+ }
+ in.read(count);
+ out.written(count);
+ }
+ private:
+ pipereader< complex > in;
+ pipewriter< complex > out;
+ };
+
+ template
+ struct cfft_engine {
+ const int n;
+ cfft_engine(int _n) : n(_n), invsqrtn(1.0/sqrt(n)) {
+ // Compute log2(n)
+ logn = 0;
+ for ( int t=n; t>1; t>>=1 ) ++logn;
+ // Bit reversal
+ bitrev = new int[n];
+ for ( int i=0; i>b)&1);
+ }
+ // Float constants
+ omega = new complex[n];
+ omega_rev = new complex[n];
+ for ( int i=0; i *data, bool reverse=false) {
+ // Bit-reversal permutation
+ for ( int i=0; i tmp=data[i]; data[i]=data[r]; data[r]=tmp; }
+ }
+ complex *om = reverse ? omega_rev : omega;
+ // Danielson-Lanczos
+ for ( int i=0; i &w = om[k*dom];
+ complex &dqk = data[q+k];
+ complex x(w.re*dqk.re - w.im*dqk.im,
+ w.re*dqk.im + w.im*dqk.re);
+ data[q+k].re = data[p+k].re - x.re;
+ data[q+k].im = data[p+k].im - x.im;
+ data[p+k].re = data[p+k].re + x.re;
+ data[p+k].im = data[p+k].im + x.im;
+ }
+ }
+ }
+ if ( reverse ) {
+ float invn = 1.0 / n;
+ for ( int i=0; i *omega, *omega_rev;
+ float invsqrtn;
+ };
+
+ template
+ struct adder : runnable {
+ adder(scheduler *sch,
+ pipebuf &_in1, pipebuf &_in2, pipebuf &_out)
+ : runnable(sch, "adder"),
+ in1(_in1), in2(_in2), out(_out) {
+ }
+ void run() {
+ int n = out.writable();
+ if ( in1.readable() < n ) n = in1.readable();
+ if ( in2.readable() < n ) n = in2.readable();
+ T *pin1=in1.rd(), *pin2=in2.rd(), *pout=out.wr(), *pend=pout+n;
+ while ( pout < pend ) *pout++ = *pin1++ + *pin2++;
+ in1.read(n);
+ in2.read(n);
+ out.written(n);
+ }
+ private:
+ pipereader in1, in2;
+ pipewriter out;
+ };
+
+ template
+ struct scaler : runnable {
+ Tscale scale;
+ scaler(scheduler *sch, Tscale _scale,
+ pipebuf &_in, pipebuf &_out)
+ : runnable(sch, "scaler"),
+ scale(_scale),
+ in(_in), out(_out) {
+ }
+ void run() {
+ unsigned long count = min(in.readable(), out.writable());
+ Tin *pin=in.rd(), *pend=pin+count;
+ Tout *pout = out.wr();
+ for ( ; pin in;
+ pipewriter out;
+ };
+
+ // [awgb_c] generates complex white gaussian noise.
+
+ template
+ struct wgn_c : runnable {
+ wgn_c(scheduler *sch, pipebuf< complex > &_out)
+ : runnable(sch, "awgn"), stddev(1.0), out(_out) {
+ }
+ void run() {
+ int n = out.writable();
+ complex *pout=out.wr(), *pend=pout+n;
+ while ( pout < pend ) {
+ // TAOCP
+ float x, y, r2;
+ do {
+ x = 2*drand48() - 1;
+ y = 2*drand48() - 1;
+ r2 = x*x + y*y;
+ } while ( r2==0 || r2>=1 );
+ float k = sqrtf(-logf(r2)/r2) * stddev;
+ pout->re = k*x;
+ pout->im = k*y;
+ ++pout;
+ }
+ out.written(n);
+ }
+ float stddev;
+ private:
+ pipewriter< complex > out;
+ };
+
+ template
+ struct naive_lowpass : runnable {
+ naive_lowpass(scheduler *sch, pipebuf &_in, pipebuf &_out, int _w)
+ : runnable(sch, "lowpass"), in(_in), out(_out), w(_w) {
+ }
+
+ void run() {
+ if ( in.readable() < w ) return;
+ unsigned long count = min(in.readable()-w, out.writable());
+ T *pin=in.rd(), *pend=pin+count;
+ T *pout = out.wr();
+ float k = 1.0 / w;
+ for ( ; pin in;
+ pipewriter out;
+ int w;
+ };
+
+ template
+ struct fir_filter : runnable {
+ fir_filter(scheduler *sch, int _ncoeffs, Tc *_coeffs,
+ pipebuf &_in, pipebuf &_out,
+ unsigned int _decim=1)
+ : runnable(sch, "fir_filter"),
+ ncoeffs(_ncoeffs), coeffs(_coeffs),
+ in(_in), out(_out),
+ decim(_decim),
+ freq_tap(NULL), tap_multiplier(1), freq_tol(0.1) {
+ shifted_coeffs = new T[ncoeffs];
+ set_freq(0);
+ }
+
+ void run() {
+ if ( in.readable() < ncoeffs ) return;
+
+ if ( freq_tap ) {
+ float new_freq = *freq_tap * tap_multiplier;
+ if ( fabs(current_freq-new_freq) > freq_tol ) {
+ if ( sch->verbose )
+ fprintf(stderr, "Shifting filter %f -> %f\n",
+ current_freq, new_freq);
+ set_freq(new_freq);
+ }
+ }
+
+ unsigned long count = min((in.readable()-ncoeffs)/decim,
+ out.writable());
+ T *pin=in.rd()+ncoeffs, *pend=pin+count*decim, *pout=out.wr();
+ // TBD use coeffs when current_freq=0 (fewer mults if float)
+ for ( ; pin in;
+ pipewriter out;
+ unsigned int decim;
+
+ T *shifted_coeffs;
+ float current_freq;
+ void set_freq(float f) {
+ for ( int i=0; i
+ struct fir_resampler : runnable {
+ fir_resampler(scheduler *sch, int _ncoeffs, Tc *_coeffs,
+ pipebuf &_in, pipebuf &_out,
+ int _interp=1, int _decim=1)
+ : runnable(sch, "fir_resampler"),
+ ncoeffs(_ncoeffs), coeffs(_coeffs),
+ interp(_interp), decim(_decim),
+ in(_in), out(_out,interp),
+ freq_tap(NULL), tap_multiplier(1), freq_tol(0.1)
+ {
+ if ( decim != 1 ) fail("fir_resampler: decim not implemented"); // TBD
+ shifted_coeffs = new T[ncoeffs];
+ set_freq(0);
+ }
+
+ void run() {
+ if ( in.readable() < ncoeffs ) return;
+
+ if ( freq_tap ) {
+ float new_freq = *freq_tap * tap_multiplier;
+ if ( fabs(current_freq-new_freq) > freq_tol ) {
+ if ( sch->verbose )
+ fprintf(stderr, "Shifting filter %f -> %f\n",
+ current_freq, new_freq);
+ set_freq(new_freq);
+ }
+ }
+
+ if ( in.readable()*interp < ncoeffs ) return;
+ unsigned long count = min((in.readable()*interp-ncoeffs)/interp,
+ out.writable()/interp);
+ int latency = (ncoeffs+interp) / interp;
+ T *pin=in.rd()+latency, *pend=pin+count, *pout=out.wr();
+ // TBD use coeffs when current_freq=0 (fewer mults if float)
+ for ( ; pin in;
+ pipewriter out;
+
+ public:
+ float *freq_tap;
+ float tap_multiplier;
+ float freq_tol;
+
+ private:
+ T *shifted_coeffs;
+ float current_freq;
+ void set_freq(float f) {
+ for ( int i=0; i
+
+#include "leansdr/viterbi.h"
+#include "leansdr/convolutional.h"
+#include "leansdr/sdr.h"
+#include "leansdr/rs.h"
+
+namespace leansdr {
+
+ static const int SIZE_RSPACKET = 204;
+ static const int MPEG_SYNC = 0x47;
+ static const int MPEG_SYNC_INV = (MPEG_SYNC^0xff);
+ static const int MPEG_SYNC_CORRUPTED = 0x55;
+
+ // Generic deconvolution
+
+ enum code_rate {
+ FEC12, FEC23, FEC46, FEC34, FEC56, FEC78, // DVB-S
+ FEC45, FEC89, FEC910, // DVB-S2
+ FEC_MAX
+ };
+
+ // Customize APSK radii according to code rate
+
+ inline cstln_lut<256> * make_dvbs2_constellation(cstln_lut<256>::predef c,
+ code_rate r) {
+ float gamma1=1, gamma2=1, gamma3=1;
+ switch ( c ) {
+ case cstln_lut<256>::APSK16:
+ // EN 302 307, section 5.4.3, Table 9
+ switch ( r ) {
+ case FEC23:
+ case FEC46: gamma1 = 3.15; break;
+ case FEC34: gamma1 = 2.85; break;
+ case FEC45: gamma1 = 2.75; break;
+ case FEC56: gamma1 = 2.70; break;
+ case FEC89: gamma1 = 2.60; break;
+ case FEC910: gamma1 = 2.57; break;
+ default: fail("Code rate not supported with APSK16");
+ }
+ break;
+ case cstln_lut<256>::APSK32:
+ // EN 302 307, section 5.4.4, Table 10
+ switch ( r ) {
+ case FEC34: gamma1 = 2.84; gamma2 = 5.27; break;
+ case FEC45: gamma1 = 2.72; gamma2 = 4.87; break;
+ case FEC56: gamma1 = 2.64; gamma2 = 4.64; break;
+ case FEC89: gamma1 = 2.54; gamma2 = 4.33; break;
+ case FEC910: gamma1 = 2.53; gamma2 = 4.30; break;
+ default: fail("Code rate not supported with APSK32");
+ }
+ break;
+ case cstln_lut<256>::APSK64E:
+ // EN 302 307-2, section 5.4.5, Table 13f
+ gamma1 = 2.4; gamma2 = 4.3; gamma3 = 7;
+ break;
+ default:
+ break;
+ }
+ return new cstln_lut<256>(c, gamma1, gamma2, gamma3);
+ }
+
+ // EN 300 421, section 4.4.3, table 2 Punctured code, G1=0171, G2=0133
+ static const int DVBS_G1 = 0171;
+ static const int DVBS_G2 = 0133;
+
+// G1 = 0b1111001
+// G2 = 0b1011011
+//
+// G1 = [ 1 1 1 1 0 0 1 ]
+// G2 = [ 1 0 1 1 0 1 1 ]
+//
+// C = [ G2 ;
+// G1 ;
+// 0 G2 ;
+// 0 G1 ;
+// 0 0 G2 ;
+// 0 0 G1 ]
+//
+// C = [ 1 0 1 1 0 1 1 0 0 0 0 0 0 ;
+// 1 1 1 1 0 0 1 0 0 0 0 0 0 ;
+// 0 1 0 1 1 0 1 1 0 0 0 0 0 ;
+// 0 1 1 1 1 0 0 1 0 0 0 0 0 ;
+// 0 0 1 0 1 1 0 1 1 0 0 0 0 ;
+// 0 0 1 1 1 1 0 0 1 0 0 0 0 ;
+// 0 0 0 1 0 1 1 0 1 1 0 0 0 ;
+// 0 0 0 1 1 1 1 0 0 1 0 0 0 ;
+// 0 0 0 0 1 0 1 1 0 1 1 0 0 ;
+// 0 0 0 0 1 1 1 1 0 0 1 0 0 ;
+// 0 0 0 0 0 1 0 1 1 0 1 1 0 ;
+// 0 0 0 0 0 1 1 1 1 0 0 1 0 ;
+// 0 0 0 0 0 0 1 0 1 1 0 1 1 ;
+// 0 0 0 0 0 0 1 1 1 1 0 0 1 ]
+//
+// IQ = [ Q1; I1; ... Q10; I10 ] = C * S
+//
+// D * C == [ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 ]
+//
+// D = [ 0 1 0 1 1 1 0 1 1 1 0 0 0 0]
+// D = 0x3ba
+
+ template
+ struct deconvol_sync : runnable {
+ deconvol_sync(scheduler *sch,
+ pipebuf &_in,
+ pipebuf &_out,
+ uint32_t gX, uint32_t gY,
+ uint32_t pX, uint32_t pY)
+ : runnable(sch, "deconvol_sync"),
+ fastlock(false),
+ in(_in), out(_out,SIZE_RSPACKET),
+ skip(0) {
+ conv = new uint32_t[2];
+ conv[0] = gX;
+ conv[1] = gY;
+ nG = 2;
+ punct = new uint32_t[2];
+ punct[0] = pX;
+ punct[1] = pY;
+ punctperiod = 0;
+ punctweight = 0;
+ for ( int i=0; i<2; ++i ) {
+ int nbits = log2(punct[i]) + 1;
+ if ( nbits > punctperiod ) punctperiod = nbits;
+ punctweight += hamming_weight(punct[i]);
+ }
+ if ( sch->verbose )
+ fprintf(stderr, "puncturing %d/%d\n", punctperiod, punctweight);
+ deconv = new iq_t[punctperiod];
+ deconv2 = new iq_t[punctperiod];
+ inverse_convolution();
+ init_syncs();
+ locked = &syncs[0];
+ }
+
+ typedef uint64_t signal_t;
+ typedef uint64_t iq_t;
+
+ static int log2(uint64_t x) {
+ int n = -1;
+ for ( ; x; ++n,x>>=1 ) ;
+ return n;
+ }
+
+ iq_t convolve(signal_t s) {
+ int sbits = log2(s) + 1;
+ iq_t iq = 0;
+ unsigned char state = 0;
+ for ( int b=sbits-1; b>=0; --b ) { // Feed into convolver, MSB first
+ unsigned char bit = (s>>b) & 1;
+ state = (state>>1) | (bit<<6); // Shift register
+ for ( int j=0; j *best ) return;
+ if ( nprefix > sizeof(prefix)*8 ) return;
+ int solved = 1;
+ for ( int b=0; b>b)&1) ) {
+ // Current candidate does not solve this column.
+ if ( (response[b]>>nprefix) == 0 )
+ // No more bits to trace back.
+ return;
+ solved = 0;
+ }
+ }
+ if ( solved ) { *best = prefix; return; }
+ solve_rec(prefix, nprefix+1, exp, best);
+ solve_rec(prefix|((iq_t)1<debug ) {
+ for ( int b=0; b sizeof(iq_t)*8 )
+ fail("Bug: traceback exceeds register size");
+ if ( log2(deconv[b])+1 > traceback )
+ fail("traceback insufficient for deconvolution");
+ if ( log2(deconv2[b])+1 > traceback )
+ fail("traceback insufficient for deconvolution (alt)");
+ }
+ }
+
+ static const int NSYNCS = 4;
+
+ struct sync_t {
+ u8 lut[2][2]; // lut[(re>0)?1:0][(im>0)?1:0] = 0b000000IQ
+ iq_t in;
+ int n_in;
+ signal_t out;
+ int n_out;
+ // Auxiliary shift register for fastlock
+ iq_t in2;
+ int n_in2, n_out2;
+ } syncs[NSYNCS];
+
+ void init_syncs() {
+ // EN 300 421, section 4.5, Figure 5 QPSK constellation
+ // Four rotations * two conjugations.
+ // 180° rotation is detected as polarity inversion in mpeg_sync.
+ for ( int sync_id=0; sync_id 1 byte
+ // 2/3 12 symbols -> 2 bytes
+ // 3/4 16 symbols -> 3 bytes
+ // 5/6 24 symbols -> 5 bytes
+ // 7/8 32 symbols -> 7 bytes
+
+ inline Tbyte readbyte(sync_t *s, softsymbol *&p) {
+ while ( s->n_out < 8 ) {
+ iq_t iq = s->in;
+ while ( s->n_in < traceback ) {
+ u8 iqbits = s->lut[(p->symbol&2)?1:0][p->symbol&1];
+ ++p;
+ iq = (iq<<2) | iqbits;
+ s->n_in += 2;
+ }
+ s->in = iq;
+ for ( int b=punctperiod-1; b>=0; --b ) {
+ u8 bit = parity(iq&deconv[b]);
+ s->out = (s->out<<1) | bit;
+ }
+ s->n_out += punctperiod;
+ s->n_in -= punctweight;
+ }
+ Tbyte res = (s->out >> (s->n_out-8)) & 255;
+ s->n_out -= 8;
+ return res;
+ }
+
+ inline unsigned long readerrors(sync_t *s, softsymbol *&p) {
+ unsigned long res = 0;
+ while ( s->n_out2 < 8 ) {
+ iq_t iq = s->in2;
+ while ( s->n_in2 < traceback ) {
+ u8 iqbits = s->lut[(p->symbol&2)?1:0][p->symbol&1];
+ ++p;
+ iq = (iq<<2) | iqbits;
+ s->n_in2 += 2;
+ }
+ s->in2 = iq;
+ for ( int b=punctperiod-1; b>=0; --b ) {
+ u8 bit = parity(iq&deconv[b]);
+ u8 bit2 = parity(iq&deconv2[b]);
+ if ( bit2 != bit ) ++res;
+ }
+ s->n_out2 += punctperiod;
+ s->n_in2 -= punctweight;
+ }
+ s->n_out2 -= 8;
+ return res;
+ }
+
+ void run_decoding() {
+ in.read(skip);
+ skip = 0;
+
+ // 8 byte margin to fill the deconvolver
+ if ( in.readable() < 64 ) return;
+ int maxrd = (in.readable()-64) / (punctweight/2) * punctperiod / 8;
+ int maxwr = out.writable();
+ int n = (maxrddebug )
+ fprintf(stderr, "{%d->%d}\n",
+ (int)(locked-syncs), (int)(best-syncs));
+ locked = best;
+ }
+ // If deconvolution bit error rate > 33%, try next sample alignment
+ if ( errors_best > n*8/3 ) {
+ // fprintf(stderr, ">");
+ skip = 1;
+ }
+ }
+
+ softsymbol *pin=in.rd(), *pin0=pin;
+ Tbyte *pout=out.wr(), *pout0=pout;
+ while ( n-- )
+ *pout++ = readbyte(locked, pin);
+ in.read(pin-pin0);
+ out.written(pout-pout0);
+ }
+
+ pipereader in;
+ pipewriter out;
+ // DECONVOL
+ int nG;
+ uint32_t *conv; // [nG] Convolution polynomials; MSB is newest
+ uint32_t *punct; // [nG] Puncturing pattern
+ int punctperiod, punctweight;
+ iq_t *deconv; // [punctperiod] Deconvolution polynomials
+ iq_t *deconv2; // [punctperiod] Alternate polynomials (for fastlock)
+ sync_t *locked;
+ int skip;
+
+ };
+
+ typedef deconvol_sync deconvol_sync_simple;
+
+ inline deconvol_sync_simple *make_deconvol_sync_simple(scheduler *sch,
+ pipebuf &_in,
+ pipebuf &_out,
+ enum code_rate rate) {
+ // EN 300 421, section 4.4.3 Inner coding
+ uint32_t pX, pY;
+ switch ( rate ) {
+ case FEC12:
+ pX = 0x1; // 1
+ pY = 0x1; // 1
+ break;
+ case FEC23:
+ case FEC46:
+ pX = 0xa; // 1010 (Handle as FEC4/6, no half-symbols)
+ pY = 0xf; // 1111
+ break;
+ case FEC34:
+ pX = 0x5; // 101
+ pY = 0x6; // 110
+ break;
+ case FEC56:
+ pX = 0x15; // 10101
+ pY = 0x1a; // 11010
+ break;
+ case FEC78:
+ pX = 0x45; // 1000101
+ pY = 0x7a; // 1111010
+ break;
+ default:
+ //fail("Code rate not implemented");
+ // For testing DVB-S2 constellations.
+ fprintf(stderr, "Code rate not implemented; proceeding anyway\n");
+ pX = pY = 1;
+ }
+ return new deconvol_sync_simple(sch, _in, _out, DVBS_G1, DVBS_G2, pX, pY);
+ }
+
+
+ // CONVOLUTIONAL ENCODER
+
+ static const uint16_t polys_fec12[] = {
+ DVBS_G1, DVBS_G2 // X1Y1
+ };
+ static const uint16_t polys_fec23[] = {
+ DVBS_G1, DVBS_G2, DVBS_G2<<1 // X1Y1Y2
+ };
+ // Same code rate as 2/3, usable with QPSK
+ static const uint16_t polys_fec46[] = {
+ DVBS_G1, DVBS_G2, DVBS_G2<<1, // X1Y1Y2
+ DVBS_G1<<2, DVBS_G2<<2, DVBS_G2<<3 // X3Y3Y4
+ };
+ static const uint16_t polys_fec34[] = {
+ DVBS_G1, DVBS_G2, // X1Y1
+ DVBS_G2<<1, DVBS_G1<<2 // Y2X3
+ };
+ static const uint16_t polys_fec45[] = { // Non standard
+ DVBS_G1, DVBS_G2, // X1Y1
+ DVBS_G2<<1, DVBS_G1<<2, // Y2X3
+ DVBS_G1<<3 // X4
+ };
+ static const uint16_t polys_fec56[] = {
+ DVBS_G1, DVBS_G2, // X1Y1
+ DVBS_G2<<1, DVBS_G1<<2, // Y2X3
+ DVBS_G2<<3, DVBS_G1<<4 // Y4X5
+ };
+ static const uint16_t polys_fec78[] = {
+ DVBS_G1, DVBS_G2, // X1Y1
+ DVBS_G2<<1, DVBS_G2<<2, // Y2Y3
+ DVBS_G2<<3, DVBS_G1<<4, // Y4X5
+ DVBS_G2<<5, DVBS_G1<<6 // Y6X7
+ };
+
+ // FEC parameters, for convolutional coding only (not S2).
+ static struct fec_spec {
+ int bits_in; // Entering the convolutional coder
+ int bits_out; // Exiting the convolutional coder
+ const uint16_t *polys; // [bits_out]
+ } fec_specs[FEC_MAX] = {
+ [FEC12] = { 1, 2, polys_fec12 },
+ [FEC23] = { 2, 3, polys_fec23 },
+ [FEC46] = { 4, 6, polys_fec46 },
+ [FEC34] = { 3, 4, polys_fec34 },
+ [FEC56] = { 5, 6, polys_fec56 },
+ [FEC78] = { 7, 8, polys_fec78 },
+ [FEC45] = { 4, 5, polys_fec45 }, // Non-standard
+ };
+
+ struct dvb_convol : runnable {
+ typedef u8 uncoded_byte;
+ typedef u8 hardsymbol;
+ dvb_convol(scheduler *sch,
+ pipebuf &_in,
+ pipebuf &_out,
+ code_rate fec,
+ int bits_per_symbol)
+ : runnable(sch, "dvb_convol"),
+ in(_in), out(_out,64) // BPSK 7/8: 7 bytes in, 64 symbols out
+ {
+ fec_spec *fs = &fec_specs[fec];
+ if ( ! fs->bits_in ) fail("Unexpected FEC");
+ convol.bits_in = fs->bits_in;
+ convol.bits_out = fs->bits_out;
+ convol.polys = fs->polys;
+ convol.bps = bits_per_symbol;
+ // FEC must output a whole number of IQ symbols
+ if ( convol.bits_out % convol.bps )
+ fail("Code rate not suitable for this constellation");
+ }
+
+ void run() {
+ int count = min(in.readable(), out.writable()*convol.bps/
+ convol.bits_out*convol.bits_in/8);
+ // Process in multiples of the puncturing period and of 8 bits.
+ int chunk = convol.bits_in;
+ count = (count/chunk) * chunk;
+ convol.encode(in.rd(), out.wr(), count);
+ in.read(count);
+ int nout = count*8/convol.bits_in*convol.bits_out/convol.bps;
+ out.written(nout);
+ }
+ private:
+ pipereader in;
+ pipewriter out;
+ convol_multipoly convol;
+ }; // dvb_convol
+
+
+ // NEW ALGEBRAIC DECONVOLUTION
+
+ // QPSK 1/2 only;
+ // With DVB-S polynomials hardcoded.
+
+ template
+ struct dvb_deconvol_sync : runnable {
+ typedef u8 decoded_byte;
+
+ int resync_period;
+
+ static const int chunk_size = 64; // At least 2*sizeof(Thist)/8
+
+ dvb_deconvol_sync(scheduler *sch,
+ pipebuf &_in,
+ pipebuf &_out)
+ : runnable(sch, "deconvol_sync_multipoly"),
+ resync_period(32),
+ in(_in), out(_out,chunk_size),
+ resync_phase(0)
+ {
+ init_syncs();
+ locked = &syncs[0];
+ }
+
+ void run() {
+
+ while ( in.readable() >= chunk_size*8 &&
+ out.writable() >= chunk_size ) {
+ int errors_best = 1 << 30;
+ sync_t *best = NULL;
+ for ( sync_t *s=syncs; sdeconv.run(pin, s->lut, pout, chunk_size);
+ if ( nerrors < errors_best ) { errors_best=nerrors; best=s; }
+ }
+ in.read(chunk_size*8);
+ out.written(chunk_size);
+ if ( best != locked ) {
+ if ( sch->debug ) fprintf(stderr, "%%%d", (int)(best-syncs));
+ locked = best;
+ }
+ if ( ++resync_phase >= resync_period ) resync_phase = 0;
+ } // Work to do
+
+ } // run()
+
+ private:
+ pipereader in;
+ pipewriter out;
+ int resync_phase;
+
+ static const int NSYNCS = 4;
+
+ struct sync_t {
+ deconvol_poly2 deconv;
+ u8 lut[4]; // TBD Swap and flip bits in the polynomials instead.
+ } syncs[NSYNCS];
+
+ sync_t *locked;
+
+ void init_syncs() {
+ for ( int s=0; s dvb_deconvol_sync_soft;
+ typedef dvb_deconvol_sync dvb_deconvol_sync_hard;
+
+
+ // BIT ALIGNMENT AND MPEG SYNC DETECTION
+
+ template
+ struct mpeg_sync : runnable {
+ int scan_syncs, want_syncs;
+ unsigned long lock_timeout;
+ bool fastlock;
+ int resync_period;
+
+ mpeg_sync(scheduler *sch,
+ pipebuf &_in,
+ pipebuf &_out,
+ deconvol_sync *_deconv,
+ pipebuf *_state_out=NULL,
+ pipebuf *_locktime_out=NULL)
+ : runnable(sch, "sync_detect"),
+ scan_syncs(8), want_syncs(4),
+ lock_timeout(4),
+ fastlock(false),
+ resync_period(1),
+ in(_in), out(_out, SIZE_RSPACKET*(scan_syncs+1)),
+ deconv(_deconv),
+ polarity(0),
+ resync_phase(0),
+ bitphase(0), synchronized(false),
+ next_sync_count(0),
+ report_state(true) {
+ state_out = _state_out ? new pipewriter(*_state_out) : NULL;
+ locktime_out =
+ _locktime_out ? new pipewriter(*_locktime_out) : NULL;
+ }
+
+ void run() {
+ if ( report_state && state_out && state_out->writable()>=1 ) {
+ // Report unlocked state on first invocation.
+ state_out->write(0);
+ report_state = false;
+ }
+ if ( synchronized )
+ run_decoding();
+ else {
+ if ( fastlock ) run_searching_fast(); else run_searching();
+ }
+ }
+
+ void run_searching() {
+ bool next_sync = false;
+ int chunk = SIZE_RSPACKET * scan_syncs;
+ while ( in.readable() >= chunk+1 && // Need 1 ahead for bit shifting
+ out.writable() >= chunk && // Use as temp buffer
+ ( !state_out || state_out->writable()>=1 ) ) {
+ if ( search_sync() ) return;
+ in.read(chunk);
+ // Switch to next bit alignment
+ ++bitphase;
+ if ( bitphase == 8 ) {
+ bitphase = 0;
+ next_sync = true;
+ }
+ }
+
+ if ( next_sync ) {
+ // No lock this time
+ ++next_sync_count;
+ if ( next_sync_count >= 3 ) {
+ // After a few cycles without a lock, resync the deconvolver.
+ next_sync_count = 0;
+ if ( deconv ) deconv->next_sync();
+ }
+ }
+ }
+
+ void run_searching_fast() {
+ int chunk = SIZE_RSPACKET * scan_syncs;
+ while ( in.readable() >= chunk+1 && // Need 1 ahead for bit shifting
+ out.writable() >= chunk && // Use as temp buffer
+ ( !state_out || state_out->writable()>=1 ) ) {
+ if ( resync_phase == 0 ) {
+ // Try all bit alighments
+ for ( bitphase=0; bitphase<=7; ++bitphase ) {
+ if ( search_sync() ) return;
+ }
+ }
+ in.read(SIZE_RSPACKET);
+ if ( ++resync_phase >= resync_period ) resync_phase = 0;
+ }
+ }
+
+ bool search_sync() {
+ int chunk = SIZE_RSPACKET * scan_syncs;
+ // Bit-shift [scan_sync] packets according to current [bitphase]
+ Tbyte *pin = in.rd(), *pend = pin+chunk;
+ Tbyte *pout = out.wr();
+ unsigned short w = *pin++;
+ for ( ; pin<=pend; ++pin,++pout ) {
+ w = (w<<8) | *pin;
+ *pout = w >> bitphase;
+ }
+ // Search for [want_sync] start codes at all 204 offsets
+ for ( int i=0; i