mirror of
https://github.com/eddyem/small_tel.git
synced 2026-06-21 11:26:30 +03:00
start new 10-micron stellarium daemon
This commit is contained in:
64
Daemons/10micron_stellarium/CMakeLists.txt
Normal file
64
Daemons/10micron_stellarium/CMakeLists.txt
Normal file
@@ -0,0 +1,64 @@
|
||||
cmake_minimum_required(VERSION 4.0)
|
||||
|
||||
set(PROJ mountdaemon_10micron)
|
||||
project(${PROJ})
|
||||
|
||||
set(MAJOR_VERSION "0")
|
||||
set(MID_VERSION "1")
|
||||
set(MINOR_VERSION "1")
|
||||
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
||||
|
||||
enable_language(C)
|
||||
message("VER: ${VERSION}")
|
||||
|
||||
# options
|
||||
option(DEBUG "Compile in debug mode" OFF)
|
||||
|
||||
# default flags
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -std=c23")
|
||||
if(DEBUG)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g3 -ggdb -fno-builtin-strlen -Werror")
|
||||
add_definitions(-DEBUG)
|
||||
set(CMAKE_BUILD_TYPE DEBUG)
|
||||
set(CMAKE_VERBOSE_MAKEFILE "ON")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -march=native -fdata-sections -ffunction-sections -flto=auto")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -flto=auto")
|
||||
set(CMAKE_BUILD_TYPE RELEASE)
|
||||
endif()
|
||||
|
||||
message("Build type: ${CMAKE_BUILD_TYPE}")
|
||||
|
||||
set(CMAKE_COLOR_MAKEFILE ON)
|
||||
|
||||
# here is one of two variants: all .c in directory or .c files in list
|
||||
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
||||
|
||||
###### pkgconfig ######
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(MODULES REQUIRED usefull_macros>=0.3.2 erfa>=2.0.0)
|
||||
|
||||
# change wrong behaviour with install prefix
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local")
|
||||
else()
|
||||
message("Change default install path to /usr/local")
|
||||
set(CMAKE_INSTALL_PREFIX "/usr/local")
|
||||
endif()
|
||||
message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
# executable file
|
||||
add_executable(${PROJ} ${SOURCES})
|
||||
# -I
|
||||
target_include_directories(${PROJ} PUBLIC ${MODULES_INCLUDE_DIRS})
|
||||
# -L
|
||||
target_link_directories(${PROJ} PUBLIC ${MODULES_LIBRARY_DIRS})
|
||||
# -l
|
||||
target_link_libraries(${PROJ} ${MODULES_LIBRARIES} -lweather -lm)
|
||||
# -D
|
||||
add_definitions(-D_XOPEN_SOURCE=1234 -D_DEFAULT_SOURCE -D_GNU_SOURCE
|
||||
-DPACKAGE_VERSION=\"${VERSION}\" -DMINOR_VERSION=\"${MINOR_VERSION}\"
|
||||
-DMID_VERSION=\"${MID_VERSION}\" -DMAJOR_VERSION=\"${MAJOR_VESION}\"
|
||||
)
|
||||
|
||||
# Installation of the program
|
||||
INSTALL(TARGETS ${PROJ} DESTINATION "bin")
|
||||
@@ -1,23 +0,0 @@
|
||||
PROGRAM = stellariumdaemon
|
||||
LDFLAGS = -lerfa -pthread
|
||||
SRCS = $(wildcard *.c)
|
||||
CC = gcc
|
||||
DEFINES = -D_GNU_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=1111
|
||||
#DEFINES += -DEBUG
|
||||
CXX = gcc
|
||||
CFLAGS = -Wall -Werror -Wextra -Wno-trampolines $(DEFINES)
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
all : $(PROGRAM)
|
||||
$(PROGRAM) : $(OBJS)
|
||||
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(PROGRAM)
|
||||
|
||||
# some addition dependencies
|
||||
# %.o: %.c
|
||||
# $(CC) $(LDFLAGS) $(CFLAGS) $< -o $@
|
||||
#$(SRCS) : %.c : %.h $(INDEPENDENT_HEADERS)
|
||||
# @touch $@
|
||||
|
||||
clean:
|
||||
/bin/rm -f *.o *~
|
||||
depend:
|
||||
$(CXX) -MM $(CXX.SRCS)
|
||||
@@ -1,6 +0,0 @@
|
||||
Stellarium control of 10-micron mount
|
||||
|
||||
|
||||
Special commands in terminal mode:
|
||||
PAUSE - pause output to port from everywhere except terminal thread
|
||||
CONTINUE - allow to write to port for everyone
|
||||
@@ -1 +0,0 @@
|
||||
-std=c17
|
||||
@@ -1,160 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 4.12.3, 2021-06-10T00:51:20. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
<value type="QByteArray">{7bd84e39-ca37-46d3-be9d-99ebea85bc0d}</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
<value type="int">0</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
|
||||
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||
<value type="QString" key="language">QmlJS</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
|
||||
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
|
||||
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanIndentation">false</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
|
||||
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{65a14f9e-e008-4c1b-89df-4eaa4774b6e3}</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/tmp/astrosib/Doc/C-sources/10micron_stellarium</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">all</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
|
||||
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
|
||||
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
|
||||
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">clean</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
|
||||
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
|
||||
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
|
||||
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="QString" key="RunConfiguration.Arguments"></value>
|
||||
<value type="bool" key="RunConfiguration.Arguments.multi">false</value>
|
||||
<value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
|
||||
<value type="QString" key="RunConfiguration.WorkingDirectory.default"></value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="int">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
</qtcreator>
|
||||
@@ -1,165 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 4.10.1, 2020-02-24T16:18:36. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
<value type="QByteArray">{7bd84e39-ca37-46d3-be9d-99ebea85bc0d}</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
<value type="int">0</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
|
||||
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||
<value type="QString" key="language">QmlJS</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
|
||||
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
|
||||
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanIndentation">false</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
|
||||
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{65a14f9e-e008-4c1b-89df-4eaa4774b6e3}</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/tmp/astrosib/Doc/C-sources/10micron_stellarium</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">all</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
|
||||
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
|
||||
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
|
||||
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">clean</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
|
||||
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
|
||||
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
|
||||
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">По умолчанию</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Конфигурация развёртывания</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Особая программа</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="RunConfiguration.Arguments"></value>
|
||||
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
|
||||
<value type="QString" key="RunConfiguration.WorkingDirectory.default"></value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="int">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
</qtcreator>
|
||||
@@ -1,167 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 4.8.2, 2020-02-22T18:11:42. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
<value type="QByteArray">{cf63021e-ef53-49b0-b03b-2f2570cdf3b6}</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
<value type="int">0</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
|
||||
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||
<value type="QString" key="language">QmlJS</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
|
||||
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">false</value>
|
||||
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||
<value type="int" key="EditorConfiguration.Utf8BomBehavior">2</value>
|
||||
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||
<value type="bool" key="EditorConfiguration.inEntireDocument">true</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||
<valuemap type="QVariantMap"/>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{91347f2c-5221-46a7-80b1-0a054ca02f79}</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/tmp/as/Doc/C-sources/10micron_stellarium</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">all</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
|
||||
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
|
||||
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
|
||||
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">clean</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
|
||||
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
|
||||
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
|
||||
<value type="bool" key="GenericProjectManager.GenericMakeStep.OverrideMakeflags">false</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">По умолчанию</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Установка</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Конфигурация установки</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
|
||||
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
|
||||
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Особая программа</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="RunConfiguration.Arguments"></value>
|
||||
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
|
||||
<value type="QString" key="RunConfiguration.WorkingDirectory.default"></value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="int">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">20</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">20</value>
|
||||
</data>
|
||||
</qtcreator>
|
||||
@@ -1,17 +0,0 @@
|
||||
cmdlnopts.c
|
||||
cmdlnopts.h
|
||||
daemon.c
|
||||
emulation.c
|
||||
emulation.h
|
||||
libsofa.c
|
||||
libsofa.h
|
||||
main.c
|
||||
main.h
|
||||
parseargs.c
|
||||
parseargs.h
|
||||
socket.c
|
||||
socket.h
|
||||
telescope.c
|
||||
telescope.h
|
||||
usefull_macro.c
|
||||
usefull_macro.h
|
||||
@@ -1 +0,0 @@
|
||||
.
|
||||
343
Daemons/10micron_stellarium/angles.c
Normal file
343
Daemons/10micron_stellarium/angles.c
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* This file is part of the mountdaemon_10micron project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <erfa.h>
|
||||
#include <erfam.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "angles.h"
|
||||
|
||||
static placeData_t place = {.slong = 0.7232763200, .slat = 0.7618977414, .salt = 2070.};
|
||||
static almDut_t AlmDut = {0};
|
||||
|
||||
// set/get polar/DUT1 data
|
||||
bool setDUT(almDut_t *D){
|
||||
if(!D){
|
||||
WARNX("setDUT(): no args");
|
||||
return false;
|
||||
}
|
||||
if(fabs(D->DUT1) > 1.){
|
||||
WARNX("setDUT(): DUT1 too large (%g)", D->DUT1);
|
||||
return false;
|
||||
}
|
||||
if(fabs(D->px) > 1000. || fabs(D->py) > 1000.){
|
||||
WARNX("setDUT(): bad polar coordinates (%g, %g)", D->px, D->py);
|
||||
return false;
|
||||
}
|
||||
AlmDut = *D;
|
||||
return true;
|
||||
}
|
||||
|
||||
void getDUT(almDut_t *D){
|
||||
if(!D) return;
|
||||
*D = AlmDut;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief setPlaceData - correct place data to given values
|
||||
* @param longitude - degrees, (-180, 180], minus to west
|
||||
* @param latitude - degrees, [-90, 90], minus to south
|
||||
* @param altitude - meters
|
||||
* @return false if some of data wrong
|
||||
*/
|
||||
bool setPlaceData(double longitude, double latitude, double altitude){
|
||||
if(longitude <= -180. || longitude > 180.){
|
||||
WARNX("setPlaceData(): bad longitude (%g)", longitude);
|
||||
return false;
|
||||
}
|
||||
if(latitude < -90. || latitude > 90.){
|
||||
WARNX("setPlaceData(): bad latitude (%g)", latitude);
|
||||
return false;
|
||||
}
|
||||
if(altitude < -500. || altitude > 9000.){
|
||||
WARNX("setPlaceData(): bad altitude (%g)", altitude);
|
||||
return false;
|
||||
}
|
||||
place.salt = altitude;
|
||||
place.slat = latitude;
|
||||
place.slong = longitude;
|
||||
return true;
|
||||
}
|
||||
|
||||
void getPlaceData(placeData_t *pd){
|
||||
if(!pd) return;
|
||||
*pd = place;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert RA/DEC to string in forman RA: HH:MM:SS.SS, DEC: DD:MM:SS.S
|
||||
*/
|
||||
char *radec2str(double ra, double dec, char buf[RADEC_STR_MAXLEN]){
|
||||
char sign = '+';
|
||||
if(dec < 0){
|
||||
sign = '-';
|
||||
dec = -dec;
|
||||
}
|
||||
|
||||
int h = (int)ra;
|
||||
ra -= h; ra *= 60.;
|
||||
int m = (int)ra;
|
||||
ra -= m; ra *= 60.;
|
||||
|
||||
int d = (int) dec;
|
||||
dec -= d; dec *= 60.;
|
||||
int dm = (int)dec;
|
||||
dec -= dm; dec *= 60.;
|
||||
snprintf(buf, RADEC_STR_MAXLEN, "%d:%d:%.2f %c%d:%d:%.1f", h,m,ra, sign,d,dm,dec);
|
||||
buf[RADEC_STR_MAXLEN-1] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
// normalize RA/DEC to [0..24) for RA and [-90, 90] for DEC
|
||||
void norm_RA(double *ra){
|
||||
if(!ra) return;
|
||||
if(*ra >= 0. && *ra < 24.) return;
|
||||
double r = fmod(*ra, 24.);
|
||||
if(r < 0.) r += 24.;
|
||||
*ra = r;
|
||||
}
|
||||
void norm_RADEC(double *ra, double *dec){
|
||||
if(!ra || !dec) return;
|
||||
if(*dec >= -90. && *dec <= 90.){ // need only check RA
|
||||
norm_RA(ra);
|
||||
return;
|
||||
}
|
||||
// 1: convert to (-180..+180)
|
||||
norm_angle180(dec);
|
||||
// 2: fix dec & ra together
|
||||
if(*dec > 90.) *dec = 180. - *dec;
|
||||
else *dec = -180. - *dec;
|
||||
*ra += 12.;
|
||||
norm_RA(ra);
|
||||
}
|
||||
// normalize angle to (-180, 180]
|
||||
void norm_angle180(double *a){
|
||||
if(!a) return;
|
||||
double d = fmod(*a + 180.0, 360.0);
|
||||
if(d < 0.) d += 360.0;
|
||||
else d -= 180;
|
||||
*a = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief hor2eq - convert horizontal coordinates to polar
|
||||
* @param h (i) - horizontal coordinates
|
||||
* @param pc (o) - polar coordinates
|
||||
* @param sidTime - sidereal time
|
||||
*/
|
||||
void hor2eq(horizCrds_t *h, polarCrds_t *pc, double sidTime){
|
||||
if(!h || !pc) return;
|
||||
eraAe2hd(h->az, ERFA_DPI/2. - h->zd, place.slat, &pc->ha, &pc->dec); // A,H -> HA,DEC; phi - site latitude
|
||||
pc->ra = sidTime - pc->ha;
|
||||
pc->eo = 0.;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief eq2horH - convert polar coordinates to horizontal
|
||||
* @param pc (i) - polar coordinates (only HA used)
|
||||
* @param h (o) - horizontal coordinates
|
||||
* @param sidTime - sidereal time
|
||||
*/
|
||||
void eq2horH(polarCrds_t *pc, horizCrds_t *h){
|
||||
if(!h || !pc) return;
|
||||
double alt;
|
||||
eraHd2ae(pc->ha, pc->dec, place.slat, &h->az, &alt);
|
||||
h->zd = ERFA_DPI/2. - alt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief eq2hor - convert polar coordinates to horizontal
|
||||
* @param pc (i) - polar coordinates (only RA used)
|
||||
* @param h (o) - horizontal coordinates
|
||||
* @param sidTime - sidereal time
|
||||
*/
|
||||
void eq2hor(polarCrds_t *pc, horizCrds_t *h, double sidTime){
|
||||
if(!h || !pc) return;
|
||||
double ha = sidTime - pc->ra + pc->eo;
|
||||
double alt;
|
||||
eraHd2ae(ha, pc->dec, place.slat, &h->az, &alt);
|
||||
h->zd = ERFA_DPI/2. - alt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief r2sHMS - convert angle in radians into string "'HH:MM:SS.SS'"
|
||||
* @param radians - angle
|
||||
* @param hms (o) - string
|
||||
* @param len - length of hms
|
||||
*/
|
||||
void r2sHMS(double radians, char *hms, int len){
|
||||
char pm;
|
||||
int i[4];
|
||||
eraA2tf(2, radians, &pm, i);
|
||||
snprintf(hms, len, "'%c%02d:%02d:%02d.%02d'", pm, i[0],i[1],i[2],i[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief r2sDMS - convert angle in radians into string "'DD:MM:SS.S'"
|
||||
* @param radians - angle
|
||||
* @param dms (o) - string
|
||||
* @param len - length of hms
|
||||
*/
|
||||
void r2sDMS(double radians, char *dms, int len){
|
||||
char pm;
|
||||
int i[4];
|
||||
eraA2af(1, radians, &pm, i);
|
||||
snprintf(dms, len, "'%c%02d:%02d:%02d.%d'", pm, i[0],i[1],i[2],i[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get_MJDt - calculate MJD of date from argument
|
||||
* @param tval (i) - given date (or NULL for current)
|
||||
* @param MJD (o) - time (or NULL just to check)
|
||||
* @return true if all OK
|
||||
*/
|
||||
bool get_MJDt(struct timeval *tval, sMJD_t *MJD){
|
||||
struct tm tms;
|
||||
double tSeconds;
|
||||
if(!MJD) return false;
|
||||
if(!tval){
|
||||
struct timespec ts;
|
||||
if(clock_gettime(CLOCK_REALTIME, &ts)){
|
||||
WARN("clock_gettime()");
|
||||
return false;
|
||||
}
|
||||
gmtime_r(&ts.tv_sec, &tms);
|
||||
tSeconds = tms.tm_sec + ((double)ts.tv_nsec)/1e9;
|
||||
}else{
|
||||
gmtime_r(&tval->tv_sec, &tms);
|
||||
tSeconds = tms.tm_sec + ((double)tval->tv_usec)/1e6;
|
||||
}
|
||||
int y, m, d;
|
||||
y = 1900 + tms.tm_year;
|
||||
m = tms.tm_mon + 1;
|
||||
d = tms.tm_mday;
|
||||
double utc1, utc2;
|
||||
/* UTC date. */
|
||||
if(eraDtf2d("UTC", y, m, d, tms.tm_hour, tms.tm_min, tSeconds, &utc1, &utc2) < 0) return false;
|
||||
if(!MJD) return false;
|
||||
MJD->MJD = utc1 - ERFA_DJM0 + utc2;
|
||||
MJD->utc1 = utc1;
|
||||
MJD->utc2 = utc2;
|
||||
//DBG("UTC(m): %g, %.8f\n", utc1 - 2400000.5, utc2);
|
||||
if(eraUtctai(utc1, utc2, &MJD->tai1, &MJD->tai2)) return false;
|
||||
//DBG("TAI");
|
||||
if(eraTaitt(MJD->tai1, MJD->tai2, &MJD->tt1, &MJD->tt2)) return false;
|
||||
//DBG("TT");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get_LST - calculate local siderial time
|
||||
* @param mjd (i) - date/time for LST (utc1 & tt used)
|
||||
* @param dUT1 - (UT1-UTC)
|
||||
* @param slong - site longitude (radians)
|
||||
* @param LST (o) - local sidereal time (radians)
|
||||
* @return true if all OK
|
||||
*/
|
||||
bool get_LST(sMJD_t *mjd, double dUT1, double slong, double *LST){
|
||||
double ut11, ut12;
|
||||
sMJD_t Mjd;
|
||||
if(!LST) return false;
|
||||
if(!mjd){
|
||||
if(!get_MJDt(NULL, &Mjd)) return false;
|
||||
}else Mjd = *mjd;
|
||||
if(eraUtcut1(Mjd.utc1, Mjd.utc2, dUT1, &ut11, &ut12)) return false;
|
||||
double ST = eraGst06a(ut11, ut12, Mjd.tt1, Mjd.tt2);
|
||||
ST += slong;
|
||||
if(ST > ERFA_D2PI) ST -= ERFA_D2PI;
|
||||
else if(ST < 0.) ST += ERFA_D2PI;
|
||||
*LST = ST;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get_ObsPlace - calculate observed place (without PM etc) for given date @550nm
|
||||
* @param tval (i) - time
|
||||
* @param p2000 (i) - polar coordinates for J2000 (only ra/dec used), ICRS (catalog)
|
||||
* @param pnow (o) - polar coordinates for given epoch (or NULL)
|
||||
* @param hnow (o) - horizontal coordinates for given epoch (or NULL)
|
||||
* @return true if all OK
|
||||
*/
|
||||
bool get_ObsPlace(struct timeval *tval, polarCrds_t *p2000, polarCrds_t *pnow, horizCrds_t *hnow){
|
||||
double pr = 0.0; // RA proper motion (radians/year; Note 2)
|
||||
double pd = 0.0; // Dec proper motion (radians/year)
|
||||
double px = 0.0; // parallax (arcsec)
|
||||
double rv = 0.0; // radial velocity (km/s, positive if receding)
|
||||
sMJD_t MJD;
|
||||
if(!tval || !p2000) return false;
|
||||
if(get_MJDt(tval, &MJD)) return false;
|
||||
/* Effective wavelength (microns) */
|
||||
double wl = 0.55;
|
||||
/* ICRS to observed. */
|
||||
double aob, zob, hob, dob, rob, eo;
|
||||
double p = 1000., t = 0., h = place.salt;
|
||||
weather_data_t weath;
|
||||
if(get_weather_data(&weath)){
|
||||
p = 1.3332239 * weath.pressure;
|
||||
t = weath.exttemp;
|
||||
}else WARNX("Can't get weather - use default values");
|
||||
if(eraAtco13(p2000->ra, p2000->dec,
|
||||
pr, pd, px, rv,
|
||||
MJD.utc1, MJD.utc2,
|
||||
AlmDut.DUT1,
|
||||
place.slong, place.slat, place.salt,
|
||||
AlmDut.px, AlmDut.py,
|
||||
p, t, h,
|
||||
wl,
|
||||
&aob, &zob,
|
||||
&hob, &dob, &rob, &eo)) return false;
|
||||
if(pnow){
|
||||
pnow->eo = eo;
|
||||
pnow->ha = hob;
|
||||
pnow->ra = rob;
|
||||
pnow->dec = dob;
|
||||
}
|
||||
if(hnow){
|
||||
hnow->az = aob;
|
||||
hnow->zd = zob;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
typedef enum {
|
||||
WEATHER_GOOD = 0, // may start observations
|
||||
WEATHER_BAD = 1, // cannot start but can continue if want
|
||||
WEATHER_TERRIBLE = 2, // close & park: wind, precipitation, humidity etc.
|
||||
WEATHER_PROHIBITED = 3, // force closing & parking; power off equipment, ready to power off computer
|
||||
} weather_condition_t;
|
||||
|
||||
typedef struct {
|
||||
weather_condition_t weather; // conditions: field "WEATHER"
|
||||
float windmax; // maximal wind for last hour, m/s: "WINDMAX1"
|
||||
float wind; // current wind speed, m/s: "WIND"
|
||||
float clouds; // sky "quality" (>2500 - OK): "CLOUDS"
|
||||
float exttemp; // external temperature, degC: "EXTTEMP"
|
||||
float pressure; // atm. pressure, mmHg: "PRESSURE"
|
||||
float humidity; // humidity, percents: "HUMIDITY"
|
||||
int rain; // ==1 when rainy: "PRECIP"
|
||||
int forceoff; // force power off (AC power lost or lightning)
|
||||
time_t last_update; // value of "TMEAS"
|
||||
} weather_data_t;
|
||||
|
||||
int get_weather_data(weather_data_t *data);
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the StelD project.
|
||||
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
* This file is part of the mountdaemon_10micron project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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
|
||||
@@ -15,23 +15,25 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef LIBSOFA_H__
|
||||
#define LIBSOFA_H__
|
||||
|
||||
#include <erfa.h>
|
||||
#include <erfam.h>
|
||||
#include <sys/time.h>
|
||||
#include <weather_data.h>
|
||||
|
||||
// JD2451544.5 == 2000.0
|
||||
#define MJD2000 (51544)
|
||||
#define RADEC_STR_MAXLEN 72
|
||||
|
||||
#define RAD2DEG(angle) (angle * ERFA_DR2D)
|
||||
#define DEG2RAD(angle) (angle * ERFA_DD2R)
|
||||
#define RAD2HRS(angle) (angle * 24. / ERFA_DPI)
|
||||
#define HRS2RAD(hour) (hour * ERFA_DPI / 24.)
|
||||
|
||||
typedef struct{
|
||||
double utc1; double utc2; // UTC JD, commonly used MJD = utc1+utc2-2400000.5
|
||||
double MJD;
|
||||
double tai1; double tai2; // TAI JD
|
||||
double tt1; double tt2; // TT JD
|
||||
} sMJD;
|
||||
} sMJD_t;
|
||||
|
||||
// polar coordinates & equation of origins (all in radians)
|
||||
typedef struct{
|
||||
@@ -39,48 +41,49 @@ typedef struct{
|
||||
double dec; // declination
|
||||
double ra; // right ascension
|
||||
double eo; // equation of origins
|
||||
} polarCrds;
|
||||
} polarCrds_t;
|
||||
|
||||
// horizontal coordinates (all in radians)
|
||||
typedef struct{
|
||||
double az; // azimuth, 0 @ south, positive clockwise
|
||||
double zd; // zenith distance
|
||||
} horizCrds;
|
||||
} horizCrds_t;
|
||||
|
||||
// observational place coordinates and altitude; all coordinates are in radians!
|
||||
typedef struct{
|
||||
double slong; // longitude
|
||||
double slat; // lattitude
|
||||
double slong; // longitude, radians
|
||||
double slat; // lattitude, radians
|
||||
double salt; // altitude, m
|
||||
} placeData;
|
||||
|
||||
// place weather data
|
||||
typedef struct{
|
||||
double relhum; // rel. humidity, 0..100%
|
||||
double pres; // atm. pressure (mmHg)
|
||||
double tc; // temperature, degrC
|
||||
double rain; // rain value (0..1)
|
||||
double clouds; // clouds (0 - bad, >2500 - good)
|
||||
double wind; // wind speed, m/s
|
||||
double time; // measurements time
|
||||
} localWeather;
|
||||
} placeData_t;
|
||||
|
||||
// DUT/polar almanach data
|
||||
// TODO: add function to read this data from almanach
|
||||
typedef struct{
|
||||
double DUT1; // UT1-UTC, sec
|
||||
double px; // polar coordinates, arcsec
|
||||
double py;
|
||||
} almDut;
|
||||
} almDut_t;
|
||||
|
||||
char *radec2str(double ra, double dec, char buf[RADEC_STR_MAXLEN]);
|
||||
void norm_RA(double *ra);
|
||||
void norm_RADEC(double *ra, double *dec);
|
||||
void norm_angle180(double *a);
|
||||
|
||||
void hor2eq(horizCrds_t *h, polarCrds_t *pc, double sidTime);
|
||||
void eq2horH(polarCrds_t *pc, horizCrds_t *h);
|
||||
void eq2hor(polarCrds_t *pc, horizCrds_t *h, double sidTime);
|
||||
|
||||
void r2sHMS(double radians, char *hms, int len);
|
||||
void r2sDMS(double radians, char *hms, int len);
|
||||
void hor2eq(horizCrds *h, polarCrds *pc, double sidTime);
|
||||
void eq2horH(polarCrds *pc, horizCrds *h);
|
||||
void eq2hor(polarCrds *pc, horizCrds *h, double sidTime);
|
||||
int get_MJDt(struct timeval *tval, sMJD *MJD);
|
||||
int get_LST(sMJD *mjd, double dUT1, double slong, double *LST);
|
||||
int get_ObsPlace(struct timeval *tval, polarCrds *p2000, localWeather *weath, polarCrds *pnow, horizCrds *hnow);
|
||||
almDut *getDUT();
|
||||
localWeather *getWeath();
|
||||
placeData *getPlace();
|
||||
#endif // LIBSOFA_H__
|
||||
void hor2eq(horizCrds_t *h, polarCrds_t *pc, double sidTime);
|
||||
void eq2horH(polarCrds_t *pc, horizCrds_t *h);
|
||||
void eq2hor(polarCrds_t *pc, horizCrds_t *h, double sidTime);
|
||||
bool get_MJDt(struct timeval *tval, sMJD_t *MJD);
|
||||
bool get_LST(sMJD_t *mjd, double dUT1, double slong, double *LST);
|
||||
bool get_ObsPlace(struct timeval *tval, polarCrds_t *p2000, polarCrds_t *pnow, horizCrds_t *hnow);
|
||||
|
||||
bool setDUT(almDut_t *D);
|
||||
void getDUT(almDut_t *D);
|
||||
|
||||
bool setPlaceData(double longitude, double latitude, double altitude);
|
||||
void getPlaceData(placeData_t *pd);
|
||||
140
Daemons/10micron_stellarium/args.c
Normal file
140
Daemons/10micron_stellarium/args.c
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* This file is part of the mountdaemon_10micron project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "args.h"
|
||||
#include "server.h"
|
||||
|
||||
#define DEFAULT_COMDEV "/dev/ttyUSB0"
|
||||
// TCP socket port
|
||||
#define DEFAULT_PORT ":10000"
|
||||
#define DEFAULT_CMDNODE "localhost:10001"
|
||||
// default PID filename:
|
||||
#define DEFAULT_PIDFILE "/tmp/mountdaemon_10micron.pid"
|
||||
// default file with headers
|
||||
#define DEFAULT_FITSHDR "/tmp/10micron.fitsheader"
|
||||
// baudrate - 115200
|
||||
#define DEFAULT_SERSPEED 115200
|
||||
// serial polling timeout - 100ms
|
||||
#define DEFAULT_SERTMOUT 100000
|
||||
// mount name
|
||||
#define DEFAULT_MOUNT_NAME "10Micron GM4000HPS"
|
||||
|
||||
// non-config file options
|
||||
static int help = 0;
|
||||
static char *conffile = NULL;
|
||||
|
||||
// default command line options
|
||||
static parameters_t G = {0};
|
||||
// default config options
|
||||
static parameters_t Conf = {0};
|
||||
|
||||
#define STR_HELPER(x) #x
|
||||
#define STR(x) STR_HELPER(x)
|
||||
|
||||
#define OPTIONS(Stor) \
|
||||
{"device", NEED_ARG, NULL, 'd', arg_string, APTR(&Stor.device), "serial device name"}, \
|
||||
{"emulation",NO_ARGS, NULL, 'e', arg_int, APTR(&Stor.emulation), "run in emulation mode"}, \
|
||||
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&Stor.logfile), "file to save logs"}, \
|
||||
{"hdrfile", NEED_ARG, NULL, 'o', arg_string, APTR(&Stor.crdsfile), "file to save FITS-header with coordinates and time (default: " DEFAULT_FITSHDR ")"}, \
|
||||
{"pidfile", NEED_ARG, NULL, 0, arg_string, APTR(&Stor.pidfile), "pidfile (default: " DEFAULT_PIDFILE ")"}, \
|
||||
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&Stor.port), "port for stellaruim's connect (default: " DEFAULT_PORT ")"}, \
|
||||
{"cmdport", NEED_ARG, NULL, 'P', arg_string, APTR(&Stor.cmdnode), "port or UNIX-socket path to connect for command console (default: " DEFAULT_CMDNODE ")"}, \
|
||||
{"sleept", NEED_ARG, NULL, 't', arg_int, APTR(&Stor.sleept), "time of servers' sleeping in main cycle, us (default: " STR(DEFAULT_SLEEP_T) " us)"}, \
|
||||
{"isunix", NO_ARGS, NULL, 'U', arg_int, APTR(&Stor.isunix), "use UNIX-socket for cmdport"}, \
|
||||
{"sertmout",NEED_ARG, NULL, 'T', arg_int, APTR(&Stor.sertmout), "serial timeout, us (default: " STR(DEFAULT_SERTMOUT) " us)"}, \
|
||||
{"serspeed",NEED_ARG, NULL, 'S', arg_int, APTR(&Stor.serspeed), "serial speed (default: " STR(DEFAULT_SERSPEED) ")"}, \
|
||||
{"maxclients",NEED_ARG, NULL, 0, arg_int, APTR(&Stor.maxclients),"max amount of clients connected to one socket (default: " STR(DEFAULT_MAXCLIENTS) ")"}, \
|
||||
{"mountname",NEED_ARG, NULL, 0, arg_string, APTR(&Stor.mountname), "mount name (default: " DEFAULT_MOUNT_NAME ")"}, \
|
||||
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&Stor.verbose), "verbose level (each -v increases it)"}, \
|
||||
|
||||
|
||||
static sl_option_t cmdlnopts[] = {
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), "show this help"},
|
||||
{"config", NEED_ARG, NULL, 'c', arg_string, APTR(&conffile), "configuration file (command line options are in advantage)"},
|
||||
OPTIONS(G)
|
||||
end_option
|
||||
};
|
||||
|
||||
// --help and --conf - options that should be parsed first
|
||||
static sl_option_t confopts[] = {
|
||||
OPTIONS(Conf)
|
||||
end_option
|
||||
};
|
||||
|
||||
static void chkstr(char **conf, char **cmd, const char *dflt){
|
||||
if(!*conf){
|
||||
if(!*cmd && dflt){
|
||||
DBG("Set default parameter: %s", dflt);
|
||||
*cmd = strdup(dflt);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(!*cmd){
|
||||
DBG("Conf-only parameter %s", *conf);
|
||||
*cmd = strdup(*conf);
|
||||
}
|
||||
FREE(*conf); // don't forget to clear
|
||||
}
|
||||
|
||||
static void chkint(int *conf, int *cmd, int dflt){
|
||||
if(!*conf){
|
||||
if(!*cmd) *cmd = dflt;
|
||||
return;
|
||||
}
|
||||
if(!*cmd){
|
||||
DBG("Conf-only parameter %d", *conf);
|
||||
*cmd = *conf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define STRCHK(field) chkstr(&Conf.field, &G.field, NULL)
|
||||
#define STRCHKD(field, def) chkstr(&Conf.field, &G.field, def)
|
||||
#define INTCHK(field) chkint(&Conf.field, &G.field, 0)
|
||||
#define INTCHKD(field, def) chkint(&Conf.field, &G.field, def)
|
||||
|
||||
parameters_t *parse_cmdline(int *argc, char ***argv){
|
||||
sl_parseargs(argc, argv, cmdlnopts);
|
||||
if(help) sl_showhelp(-1, cmdlnopts);
|
||||
if(!conffile) return &G;
|
||||
// fix for conffile
|
||||
if(!sl_conf_readopts(conffile, confopts)){ // show conf help
|
||||
sl_conf_showhelp(-1, confopts);
|
||||
return NULL;
|
||||
}
|
||||
// now fix for command line and check all:
|
||||
DBG("Check all args");
|
||||
STRCHKD(device, DEFAULT_COMDEV);
|
||||
STRCHKD(port, DEFAULT_PORT);
|
||||
STRCHKD(cmdnode, DEFAULT_CMDNODE);
|
||||
STRCHKD(pidfile, DEFAULT_PIDFILE);
|
||||
STRCHK(logfile);
|
||||
STRCHKD(crdsfile, DEFAULT_FITSHDR);
|
||||
STRCHKD(mountname, DEFAULT_MOUNT_NAME);
|
||||
INTCHK(emulation);
|
||||
INTCHK(verbose);
|
||||
INTCHKD(sleept, DEFAULT_SLEEP_T);
|
||||
INTCHK(isunix);
|
||||
INTCHKD(sertmout, DEFAULT_SERTMOUT);
|
||||
INTCHKD(serspeed, DEFAULT_SERSPEED);
|
||||
INTCHKD(maxclients, DEFAULT_MAXCLIENTS);
|
||||
return &G;
|
||||
}
|
||||
38
Daemons/10micron_stellarium/args.h
Normal file
38
Daemons/10micron_stellarium/args.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the mountdaemon_10micron project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef struct{
|
||||
char *device; // serial device name
|
||||
char *port; // port to connect
|
||||
char *cmdnode; // port for commands sending
|
||||
char *pidfile; // name of PID file
|
||||
char *logfile; // logging to this file
|
||||
char *crdsfile; // file where FITS-header should be written
|
||||
char *mountname; // set mount name for FITS-header
|
||||
int emulation; // run in emulation mode
|
||||
int verbose; // verbose level
|
||||
int sleept; // server's sleeping in main cycle time
|
||||
int isunix; // use UNIX-socket for command port
|
||||
int sertmout; // serial timeout, us
|
||||
int serspeed; // serial speed, baud
|
||||
int maxclients; // max amount of clients connected to one socket
|
||||
} parameters_t;
|
||||
|
||||
parameters_t *parse_cmdline(int *argc, char ***argv);
|
||||
@@ -1,110 +0,0 @@
|
||||
/* geany_encoding=koi8-r
|
||||
* cmdlnopts.c - the only function that parse cmdln args and returns glob parameters
|
||||
*
|
||||
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <math.h>
|
||||
#include "cmdlnopts.h"
|
||||
#include "parseargs.h"
|
||||
#include "usefull_macro.h"
|
||||
|
||||
/*
|
||||
* here are global parameters initialisation
|
||||
*/
|
||||
int help;
|
||||
glob_pars G;
|
||||
glob_pars *GP = NULL;
|
||||
|
||||
#define DEFAULT_COMDEV "/dev/ttyUSB0"
|
||||
// port for connections
|
||||
#define DEFAULT_PORT "10000"
|
||||
#define DEFAULT_DBGPORT "10001"
|
||||
// weather server port and name
|
||||
#define DEFAULT_WSPORT (12345)
|
||||
#define DEFAULT_WSNAME "robotel1.sao.ru"
|
||||
// default PID filename:
|
||||
#define DEFAULT_PIDFILE "/tmp/stellariumdaemon.pid"
|
||||
// default file with headers
|
||||
#define DEFAULT_FITSHDR "/tmp/10micron.fitsheader"
|
||||
|
||||
// DEFAULTS
|
||||
// default global parameters
|
||||
glob_pars const Gdefault = {
|
||||
.device = DEFAULT_COMDEV,
|
||||
.port = DEFAULT_PORT,
|
||||
.dbgport = DEFAULT_DBGPORT,
|
||||
.pidfile = DEFAULT_PIDFILE,
|
||||
.crdsfile = DEFAULT_FITSHDR,
|
||||
.emulation = 0,
|
||||
.weathserver = DEFAULT_WSNAME,
|
||||
.weathport = DEFAULT_WSPORT,
|
||||
.logfile = NULL // don't save logs
|
||||
};
|
||||
|
||||
/*
|
||||
* Define command line options by filling structure:
|
||||
* name has_arg flag val type argptr help
|
||||
*/
|
||||
myoption cmdlnopts[] = {
|
||||
// common options
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||
{"device", NEED_ARG, NULL, 'd', arg_string, APTR(&G.device), _("serial device name (default: " DEFAULT_COMDEV ")")},
|
||||
{"emulation",NO_ARGS, NULL, 'e', arg_int, APTR(&G.emulation), _("run in emulation mode")},
|
||||
//{"hostname",NEED_ARG, NULL, 'H', arg_string, APTR(&G.hostname), _("hostname to connect (default: localhost)")},
|
||||
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs")},
|
||||
{"hdrfile", NEED_ARG, NULL, 'o', arg_string, APTR(&G.crdsfile), _("file to save FITS-header with coordinates and time")},
|
||||
{"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")},
|
||||
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&G.port), _("port to connect (default: " DEFAULT_PORT ")")},
|
||||
{"dbgport", NEED_ARG, NULL, 'D', arg_string, APTR(&G.dbgport), _("port to connect for debug console (default: " DEFAULT_DBGPORT ")")},
|
||||
{"wport", NEED_ARG, NULL, 'w', arg_int, APTR(&G.weathport), _("weather server port")},
|
||||
{"wname", NEED_ARG, NULL, 'W', arg_string, APTR(&G.weathserver),_("weather server address")},
|
||||
end_option
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse command line options and return dynamically allocated structure
|
||||
* to global parameters
|
||||
* @param argc - copy of argc from main
|
||||
* @param argv - copy of argv from main
|
||||
* @return allocated structure with global parameters
|
||||
*/
|
||||
glob_pars *parse_args(int argc, char **argv){
|
||||
int i;
|
||||
void *ptr;
|
||||
ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr);
|
||||
size_t hlen = 1024;
|
||||
char helpstring[1024], *hptr = helpstring;
|
||||
snprintf(hptr, hlen, "Usage: %%s [args]\n\n\tWhere args are:\n");
|
||||
// format of help: "Usage: progname [args]\n"
|
||||
change_helpstring(helpstring);
|
||||
// parse arguments
|
||||
parseargs(&argc, &argv, cmdlnopts);
|
||||
if(help) showhelp(-1, cmdlnopts);
|
||||
if(argc > 0){
|
||||
G.rest_pars_num = argc;
|
||||
G.rest_pars = calloc(argc, sizeof(char*));
|
||||
for (i = 0; i < argc; i++)
|
||||
G.rest_pars[i] = strdup(argv[i]);
|
||||
}
|
||||
return &G;
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/* geany_encoding=koi8-r
|
||||
* cmdlnopts.h - comand line options for parceargs
|
||||
*
|
||||
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __CMDLNOPTS_H__
|
||||
#define __CMDLNOPTS_H__
|
||||
|
||||
/*
|
||||
* here are some typedef's for global data
|
||||
*/
|
||||
typedef struct{
|
||||
char *device; // serial device name
|
||||
char *port; // port to connect
|
||||
char *dbgport; // port for debug console
|
||||
char *pidfile; // name of PID file
|
||||
char *logfile; // logging to this file
|
||||
char *crdsfile; // file where FITS-header should be written
|
||||
char *weathserver; // weather server name
|
||||
int emulation; // run in emulation mode
|
||||
int rest_pars_num; // number of rest parameters
|
||||
int weathport; // weather server port
|
||||
char** rest_pars; // the rest parameters: array of char*
|
||||
} glob_pars;
|
||||
|
||||
// global parameters
|
||||
extern glob_pars *GP;
|
||||
|
||||
glob_pars *parse_args(int argc, char **argv);
|
||||
#endif // __CMDLNOPTS_H__
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* daemon.c - functions for running in background like a daemon
|
||||
*
|
||||
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#define PROC_BASE "/proc"
|
||||
|
||||
#include <stdio.h> // printf, fopen, ...
|
||||
#include <unistd.h> // getpid
|
||||
#include <stdio.h> // perror
|
||||
#include <sys/types.h> // opendir
|
||||
#include <dirent.h> // opendir
|
||||
#include <sys/stat.h> // stat
|
||||
#include <fcntl.h> // fcntl
|
||||
#include <stdlib.h> // exit
|
||||
#include <string.h> // memset
|
||||
|
||||
/**
|
||||
* read process name from /proc/PID/cmdline
|
||||
* @param pid - PID of interesting process
|
||||
* @return filename or NULL if not found
|
||||
* don't use this function twice for different names without copying
|
||||
* its returning by strdup, because `name` contains in static array
|
||||
*/
|
||||
char *readname(pid_t pid){
|
||||
static char name[256];
|
||||
char *pp = name, byte, path[256];
|
||||
FILE *file;
|
||||
int cntr = 0;
|
||||
size_t sz;
|
||||
snprintf (path, 255, PROC_BASE "/%d/cmdline", pid);
|
||||
file = fopen(path, "r");
|
||||
if(!file) return NULL; // there's no such file
|
||||
do{ // read basename
|
||||
sz = fread(&byte, 1, 1, file);
|
||||
if(sz != 1) break;
|
||||
if(byte != '/') *pp++ = byte;
|
||||
else{
|
||||
pp = name;
|
||||
cntr = 0;
|
||||
}
|
||||
}while(byte && cntr++ < 255);
|
||||
name[cntr] = 0;
|
||||
fclose(file);
|
||||
return name;
|
||||
}
|
||||
|
||||
void iffound_default(pid_t pid){
|
||||
fprintf(stderr, "\nFound running process (pid=%d), exit.\n", pid);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* check wether there is a same running process
|
||||
* exit if there is a running process or error
|
||||
* Checking have 3 steps:
|
||||
* 1) lock executable file
|
||||
* 2) check pidfile (if you run a copy?)
|
||||
* 3) check /proc for executables with the same name (no/wrong pidfile)
|
||||
* @param selfname - argv[0] or NULL for non-locking
|
||||
* @param pidfilename - name of pidfile or NULL if none
|
||||
* @param iffound - action to run if file found or NULL for exit(0)
|
||||
*/
|
||||
void check4running(char *selfname, char *pidfilename, void (*iffound)(pid_t pid)){
|
||||
DIR *dir;
|
||||
FILE *pidfile, *fself;
|
||||
struct dirent *de;
|
||||
struct stat s_buf;
|
||||
pid_t pid = 0, self;
|
||||
struct flock fl;
|
||||
char *name, *myname;
|
||||
if(!iffound) iffound = iffound_default;
|
||||
if(selfname){ // block self
|
||||
fself = fopen(selfname, "r"); // open self binary to lock
|
||||
if(!fself){
|
||||
perror("fopen");
|
||||
goto selfpid;
|
||||
}
|
||||
memset(&fl, 0, sizeof(struct flock));
|
||||
fl.l_type = F_WRLCK;
|
||||
if(fcntl(fileno(fself), F_GETLK, &fl) == -1){ // check locking
|
||||
perror("fcntl");
|
||||
goto selfpid;
|
||||
}
|
||||
if(fl.l_type != F_UNLCK){ // file is locking - exit
|
||||
printf("Found locker, PID = %d!\n", fl.l_pid);
|
||||
exit(1);
|
||||
}
|
||||
fl.l_type = F_RDLCK;
|
||||
if(fcntl(fileno(fself), F_SETLKW, &fl) == -1){
|
||||
perror("fcntl");
|
||||
}
|
||||
}
|
||||
selfpid:
|
||||
self = getpid(); // get self PID
|
||||
if(!(dir = opendir(PROC_BASE))){ // open /proc directory
|
||||
perror(PROC_BASE);
|
||||
}
|
||||
if(!(name = readname(self))){ // error reading self name
|
||||
perror("Can't read self name");
|
||||
exit(1);
|
||||
}
|
||||
myname = strdup(name);
|
||||
if(pidfilename && stat(pidfilename, &s_buf) == 0){ // pidfile exists
|
||||
pidfile = fopen(pidfilename, "r");
|
||||
if(pidfile){
|
||||
if(fscanf(pidfile, "%d", &pid) > 0){ // read PID of (possibly) running process
|
||||
if((name = readname(pid)) && strncmp(name, myname, 255) == 0)
|
||||
iffound(pid);
|
||||
}
|
||||
fclose(pidfile);
|
||||
}
|
||||
}
|
||||
// There is no pidfile or it consists a wrong record
|
||||
while((de = readdir(dir))){ // scan /proc
|
||||
if(!(pid = (pid_t)atoi(de->d_name)) || pid == self) // pass non-PID files and self
|
||||
continue;
|
||||
if((name = readname(pid)) && strncmp(name, myname, 255) == 0)
|
||||
iffound(pid);
|
||||
}
|
||||
closedir(dir);
|
||||
if(pidfilename){
|
||||
pidfile = fopen(pidfilename, "w");
|
||||
fprintf(pidfile, "%d\n", self); // write self PID to pidfile
|
||||
fclose(pidfile);
|
||||
}
|
||||
free(myname);
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
/*
|
||||
* geany_encoding=koi8-r
|
||||
* emulation.c
|
||||
* This file is part of the mountdaemon_10micron project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* 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; either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
@@ -15,18 +13,18 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "math.h"
|
||||
#include "emulation.h"
|
||||
#include "usefull_macro.h"
|
||||
|
||||
// emulation speed over RA & DEC (0.5 degr per sec)
|
||||
#define RA_SPEED (0.033)
|
||||
#define DECL_SPEED (0.5)
|
||||
#include <math.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "angles.h"
|
||||
#include "emulation.h"
|
||||
|
||||
// emulation speed over RA & DEC (degr per sec)
|
||||
#define RA_SPEED (5.)
|
||||
#define DECL_SPEED (11.)
|
||||
|
||||
// current coordinates
|
||||
static double RA = 0., DECL = 0.;
|
||||
@@ -35,8 +33,6 @@ static double RAtarg = 0., DECLtarg = 0.;
|
||||
// coordinates @ guiding start
|
||||
static double RA0 = 0., DECL0 = 0.;
|
||||
static double raspeed = 0.;
|
||||
// ==1 if pointing
|
||||
static int pointing = 0;
|
||||
// pointing start time
|
||||
static double tstart = -1.;
|
||||
|
||||
@@ -44,20 +40,20 @@ static double tstart = -1.;
|
||||
* send coordinates to telescope emulation
|
||||
* @param ra - right ascention (hours)
|
||||
* @param decl - declination (degrees)
|
||||
* @return 1 if all OK
|
||||
*/
|
||||
int point_emulation(double ra, double decl){
|
||||
bool point_emulation(double ra, double decl){
|
||||
norm_RADEC(&ra, &decl);
|
||||
DBG("(emul) Send ra=%g, decl=%g", ra, decl);
|
||||
putlog("(emul) Send ra=%g, decl=%g", ra, decl);
|
||||
LOGMSG("(emul) Send ra=%g, decl=%g", ra, decl);
|
||||
// TODO: check that Z<90!
|
||||
RAtarg = ra; DECLtarg = decl;
|
||||
RA0 = RA; DECL0 = DECL;
|
||||
raspeed = (RAtarg > RA) ? RA_SPEED : -RA_SPEED;
|
||||
if(fabs(RAtarg - RA) > 12.){ // go to opposite direction
|
||||
raspeed = -raspeed;
|
||||
}
|
||||
tstart = dtime();
|
||||
pointing = 1;
|
||||
return 0;
|
||||
tstart = sl_dtime();
|
||||
return true;
|
||||
}
|
||||
|
||||
static double getradiff(){
|
||||
@@ -70,20 +66,19 @@ static double getradiff(){
|
||||
|
||||
/**
|
||||
* get coordinates (emulation)
|
||||
* @return 1 if all OK
|
||||
*/
|
||||
int get_emul_coords(double *ra, double *decl){
|
||||
if(pointing){
|
||||
void get_emul_coords(double *ra, double *decl){
|
||||
if(tstart > 0.){
|
||||
DBG("RA/DEC: targ: %g/%g, cur: %g/%g, start: %g/%g", RAtarg, DECLtarg, RA, DECL, RA0, DECL0);
|
||||
// diff < speed? stop
|
||||
if((fabs(RAtarg - RA) < RA_SPEED && fabs(DECLtarg - DECL) < DECL_SPEED)){
|
||||
RA = RAtarg;
|
||||
DECL = DECLtarg;
|
||||
pointing = 0; // guiding
|
||||
tstart = -1.; // "guiding"
|
||||
DBG("@ target");
|
||||
}else{ // calculate new coordinates
|
||||
double radiff = getradiff(), decldiff = fabs(DECLtarg - DECL);
|
||||
double tdiff = dtime() - tstart;
|
||||
double tdiff = sl_dtime() - tstart;
|
||||
RA = RA0 + raspeed * tdiff;
|
||||
DBG("RA=%g", RA);
|
||||
if(getradiff() > radiff) RA = RAtarg;
|
||||
@@ -99,5 +94,5 @@ int get_emul_coords(double *ra, double *decl){
|
||||
}
|
||||
if(ra) *ra = RA;
|
||||
if(decl) *decl = DECL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
/*
|
||||
* geany_encoding=koi8-r
|
||||
* emulation.h
|
||||
* This file is part of the mountdaemon_10micron project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* 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; either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
@@ -15,16 +13,12 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
#ifndef __EMULATION_H__
|
||||
#define __EMULATION_H__
|
||||
|
||||
int point_emulation(double ra, double decl);
|
||||
int get_emul_coords(double *ra, double *decl);
|
||||
bool point_emulation(double ra, double decl);
|
||||
void get_emul_coords(double *ra, double *decl);
|
||||
|
||||
#endif // __EMULATION_H__
|
||||
|
||||
@@ -1,381 +0,0 @@
|
||||
/*
|
||||
* This file is part of the StelD project.
|
||||
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "libsofa.h"
|
||||
#include "socket.h"
|
||||
#include "usefull_macro.h"
|
||||
|
||||
#ifdef EBUG
|
||||
void reprd(char* s, double ra, double dc){
|
||||
char pm;
|
||||
int i[4];
|
||||
printf ( "%s:", s );
|
||||
eraA2tf ( 7, ra, &pm, i );
|
||||
printf ( " %2.2d %2.2d %2.2d.%7.7d", i[0],i[1],i[2],i[3] );
|
||||
eraA2af ( 6, dc, &pm, i );
|
||||
printf ( " %c%2.2d %2.2d %2.2d.%6.6d\n", pm, i[0],i[1],i[2],i[3] );
|
||||
}
|
||||
void radtodeg(double r){
|
||||
int i[4]; char pm;
|
||||
int rem = (int)(r / ERFA_D2PI);
|
||||
if(rem) r -= ERFA_D2PI * rem;
|
||||
if(r > ERFA_DPI) r -= ERFA_D2PI;
|
||||
else if(r < -ERFA_DPI) r += ERFA_D2PI;
|
||||
eraA2af (2, r, &pm, i);
|
||||
printf("%c%02d %02d %02d.%2.d", pm, i[0],i[1],i[2],i[3]);
|
||||
}
|
||||
#define REP(a,b,c) reprd(a,b,c)
|
||||
#else
|
||||
#define REP(a,b,c)
|
||||
#endif
|
||||
|
||||
// temporal stubs for weather/place/DUT1 data; user can change values of these variables
|
||||
static placeData place = {.slong = 0.7232763200, .slat = 0.7618977414, .salt = 2070.};
|
||||
placeData *getPlace(){
|
||||
return &place;
|
||||
}
|
||||
|
||||
static localWeather weather = {0};
|
||||
typedef struct{
|
||||
const char *name;
|
||||
double *valptr;
|
||||
} weathpars;
|
||||
#define WPCOUNT (7)
|
||||
static weathpars WPars[WPCOUNT] = {
|
||||
{"BTAHumid", &weather.relhum},
|
||||
{"BTAPres", &weather.pres},
|
||||
{"Exttemp", &weather.tc},
|
||||
{"Rain", &weather.rain},
|
||||
{"Clouds", &weather.clouds},
|
||||
{"Wind", &weather.wind},
|
||||
{"Time", &weather.time}
|
||||
};
|
||||
|
||||
localWeather *getWeath(){
|
||||
//DBG("DT=%zd", time(NULL) - (time_t)weather.time);
|
||||
char *w = getweathbuffer();
|
||||
//DBG("w=%s", w);
|
||||
if(w){ // get new data - check it
|
||||
int ctr = 0;
|
||||
for(int i = 0; i < WPCOUNT; ++i){
|
||||
if(getparval(WPars[i].name, w, WPars[i].valptr)) ++ctr;
|
||||
}
|
||||
if(ctr != WPCOUNT) WARN("Not full set of parameters in %s", w);
|
||||
FREE(w);
|
||||
}
|
||||
if((time_t)weather.time == 0 || time(NULL) - (time_t)weather.time > 3600) return NULL;
|
||||
return &weather;
|
||||
}
|
||||
static almDut dut1 = {0};
|
||||
almDut *getDUT(){
|
||||
// check DUT1 data HERE once per some time
|
||||
return &dut1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief r2sHMS - convert angle in radians into string "'HH:MM:SS.SS'"
|
||||
* @param radians - angle
|
||||
* @param hms (o) - string
|
||||
* @param len - length of hms
|
||||
*/
|
||||
void r2sHMS(double radians, char *hms, int len){
|
||||
char pm;
|
||||
int i[4];
|
||||
eraA2tf(2, radians, &pm, i);
|
||||
snprintf(hms, len, "'%c%02d:%02d:%02d.%02d'", pm, i[0],i[1],i[2],i[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief r2sDMS - convert angle in radians into string "'DD:MM:SS.S'"
|
||||
* @param radians - angle
|
||||
* @param dms (o) - string
|
||||
* @param len - length of hms
|
||||
*/
|
||||
void r2sDMS(double radians, char *dms, int len){
|
||||
char pm;
|
||||
int i[4];
|
||||
eraA2af(1, radians, &pm, i);
|
||||
snprintf(dms, len, "'%c%02d:%02d:%02d.%d'", pm, i[0],i[1],i[2],i[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get_MJDt - calculate MJD of date from argument
|
||||
* @param tval (i) - given date (or NULL for current)
|
||||
* @param MJD (o) - time (or NULL just to check)
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
int get_MJDt(struct timeval *tval, sMJD *MJD){
|
||||
struct tm tms;
|
||||
double tSeconds;
|
||||
if(!tval){
|
||||
//DBG("MJD for current time");
|
||||
struct timeval currentTime;
|
||||
gettimeofday(¤tTime, NULL);
|
||||
gmtime_r(¤tTime.tv_sec, &tms);
|
||||
tSeconds = tms.tm_sec + ((double)currentTime.tv_usec)/1e6;
|
||||
}else{
|
||||
gmtime_r(&tval->tv_sec, &tms);
|
||||
tSeconds = tms.tm_sec + ((double)tval->tv_usec)/1e6;
|
||||
}
|
||||
int y, m, d;
|
||||
y = 1900 + tms.tm_year;
|
||||
m = tms.tm_mon + 1;
|
||||
d = tms.tm_mday;
|
||||
double utc1, utc2;
|
||||
/* UTC date. */
|
||||
if(eraDtf2d("UTC", y, m, d, tms.tm_hour, tms.tm_min, tSeconds, &utc1, &utc2) < 0) return -1;
|
||||
if(!MJD) return 0;
|
||||
MJD->MJD = utc1 - 2400000.5 + utc2;
|
||||
MJD->utc1 = utc1;
|
||||
MJD->utc2 = utc2;
|
||||
//DBG("UTC(m): %g, %.8f\n", utc1 - 2400000.5, utc2);
|
||||
if(eraUtctai(utc1, utc2, &MJD->tai1, &MJD->tai2)) return -1;
|
||||
//DBG("TAI");
|
||||
if(eraTaitt(MJD->tai1, MJD->tai2, &MJD->tt1, &MJD->tt2)) return -1;
|
||||
//DBG("TT");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get_LST - calculate local siderial time
|
||||
* @param mjd (i) - date/time for LST (utc1 & tt used)
|
||||
* @param dUT1 - (UT1-UTC)
|
||||
* @param slong - site longitude (radians)
|
||||
* @param LST (o) - local sidereal time (radians)
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
int get_LST(sMJD *mjd, double dUT1, double slong, double *LST){
|
||||
double ut11, ut12;
|
||||
sMJD Mjd;
|
||||
if(!mjd){
|
||||
if(get_MJDt(NULL, &Mjd)) return 1;
|
||||
}else memcpy(&Mjd, mjd, sizeof(sMJD));
|
||||
if(eraUtcut1(Mjd.utc1, Mjd.utc2, dUT1, &ut11, &ut12)) return 2;
|
||||
/*double era = iauEra00(ut11, ut12) + slong;
|
||||
double eo = iauEe06a(mjd->tt1, mjd->tt2);
|
||||
printf("ERA = %s; ", radtohrs(era));
|
||||
printf("ERA-eo = %s\n", radtohrs(era-eo));*/
|
||||
if(!LST) return 0;
|
||||
double ST = eraGst06a(ut11, ut12, Mjd.tt1, Mjd.tt2);
|
||||
ST += slong;
|
||||
if(ST > ERFA_D2PI) ST -= ERFA_D2PI;
|
||||
else if(ST < 0.) ST += ERFA_D2PI;
|
||||
*LST = ST;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief hor2eq - convert horizontal coordinates to polar
|
||||
* @param h (i) - horizontal coordinates
|
||||
* @param pc (o) - polar coordinates
|
||||
* @param sidTime - sidereal time
|
||||
*/
|
||||
void hor2eq(horizCrds *h, polarCrds *pc, double sidTime){
|
||||
if(!h || !pc) return;
|
||||
placeData *p = getPlace();
|
||||
eraAe2hd(h->az, ERFA_DPI/2. - h->zd, p->slat, &pc->ha, &pc->dec); // A,H -> HA,DEC; phi - site latitude
|
||||
pc->ra = sidTime - pc->ha;
|
||||
pc->eo = 0.;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief eq2horH - convert polar coordinates to horizontal
|
||||
* @param pc (i) - polar coordinates (only HA used)
|
||||
* @param h (o) - horizontal coordinates
|
||||
* @param sidTime - sidereal time
|
||||
*/
|
||||
void eq2horH(polarCrds *pc, horizCrds *h){
|
||||
if(!h || !pc) return;
|
||||
placeData *p = getPlace();
|
||||
double alt;
|
||||
eraHd2ae(pc->ha, pc->dec, p->slat, &h->az, &alt);
|
||||
h->zd = ERFA_DPI/2. - alt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief eq2hor - convert polar coordinates to horizontal
|
||||
* @param pc (i) - polar coordinates (only RA used)
|
||||
* @param h (o) - horizontal coordinates
|
||||
* @param sidTime - sidereal time
|
||||
*/
|
||||
void eq2hor(polarCrds *pc, horizCrds *h, double sidTime){
|
||||
if(!h || !pc) return;
|
||||
double ha = sidTime - pc->ra + pc->eo;
|
||||
placeData *p = getPlace();
|
||||
double alt;
|
||||
eraHd2ae(ha, pc->dec, p->slat, &h->az, &alt);
|
||||
h->zd = ERFA_DPI/2. - alt;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief get_ObsPlace - calculate observed place (without PM etc) for given date @550nm
|
||||
* @param tval (i) - time
|
||||
* @param p2000 (i) - polar coordinates for J2000 (only ra/dec used), ICRS (catalog)
|
||||
* @param weath (i) - weather data (relhum, temp, press) or NULL if none
|
||||
* @param pnow (o) - polar coordinates for given epoch (or NULL)
|
||||
* @param hnow (o) - horizontal coordinates for given epoch (or NULL)
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
int get_ObsPlace(struct timeval *tval, polarCrds *p2000, localWeather *weath, polarCrds *pnow, horizCrds *hnow){
|
||||
double pr = 0.0; // RA proper motion (radians/year; Note 2)
|
||||
double pd = 0.0; // Dec proper motion (radians/year)
|
||||
double px = 0.0; // parallax (arcsec)
|
||||
double rv = 0.0; // radial velocity (km/s, positive if receding)
|
||||
sMJD MJD;
|
||||
if(get_MJDt(tval, &MJD)) return -1;
|
||||
if(!p2000) return -1;
|
||||
/* Effective wavelength (microns) */
|
||||
double wl = 0.55;
|
||||
/* ICRS to observed. */
|
||||
double aob, zob, hob, dob, rob, eo;
|
||||
double p = 0., t = 0., h = 0.;
|
||||
if(weath){
|
||||
p = weath->pres; t = weath->tc; h = weath->relhum;
|
||||
}
|
||||
/*
|
||||
DBG("iauAtco13(%g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g)",
|
||||
p2000->ra, p2000->dec, pr, pd, px, rv, MJD.utc1, MJD.utc2, d.DUT1, p.slong, p.slat, p.salt,
|
||||
d.px, d.py, p, t, h, wl);
|
||||
*/
|
||||
if(eraAtco13(p2000->ra, p2000->dec,
|
||||
pr, pd, px, rv,
|
||||
MJD.utc1, MJD.utc2,
|
||||
dut1.DUT1,
|
||||
place.slong, place.slat, place.salt,
|
||||
dut1.px, dut1.py,
|
||||
p, t, h,
|
||||
wl,
|
||||
&aob, &zob,
|
||||
&hob, &dob, &rob, &eo)) return -1;
|
||||
REP("ICRS->observed", rob, dob);
|
||||
if(pnow){
|
||||
pnow->eo = eo;
|
||||
pnow->ha = hob;
|
||||
pnow->ra = rob;
|
||||
pnow->dec = dob;
|
||||
}
|
||||
if(hnow){
|
||||
hnow->az = aob;
|
||||
hnow->zd = zob;
|
||||
}
|
||||
#ifdef EBUG
|
||||
printf("A(bta)/Z: ");
|
||||
radtodeg(aob);
|
||||
printf("("); radtodeg(ERFA_DPI-aob);
|
||||
printf(")/"); radtodeg(zob);
|
||||
printf("\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
// azimuth: north=zero, east=90deg
|
||||
|
||||
// parallactic angle: iauHd2pa ( ha, dec, phi );
|
||||
|
||||
// refraction coefficients: iauRefco
|
||||
|
||||
// iauAe2hd ( az, el, phi, &ha, &dec ); A,H -> HA,DEC; phi - site latitude
|
||||
// iauHd2ae ( ha, dec, phi, &az, &el ); HA,DEC -> A,H
|
||||
|
||||
// iauAtoc13 - obs->ICRS(catalog)
|
||||
// iauAtoi13 - obs->CIRS
|
||||
|
||||
// iauAtio13 - CIRS->observed
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* convert geocentric coordinates (nowadays, CIRS) to mean (JD2000, ICRS)
|
||||
* appRA, appDecl in seconds
|
||||
* r, d in seconds
|
||||
*/
|
||||
void JnowtoJ2000(double appRA, double appDecl, double *r, double *dc){
|
||||
double ra=0., dec=0., utc1, utc2, tai1, tai2, tt1, tt2, fd, eo, ri;
|
||||
int y, m, d, H, M;
|
||||
DBG("appRa: %g'', appDecl'': %g", appRA, appDecl);
|
||||
appRA *= DS2R;
|
||||
appDecl *= DAS2R;
|
||||
#define SOFA(f, ...) do{if(f(__VA_ARGS__)){WARNX("Error in " #f); goto rtn;}}while(0)
|
||||
// 1. convert system JDate to UTC
|
||||
SOFA(iauJd2cal, JDate, 0., &y, &m, &d, &fd);
|
||||
fd *= 24.;
|
||||
H = (int)fd;
|
||||
fd = (fd - H)*60.;
|
||||
M = (int)fd;
|
||||
fd = (fd - M)*60.;
|
||||
SOFA(iauDtf2d, "UTC", y, m, d, H, M, fd, &utc1, &utc2);
|
||||
SOFA(iauUtctai, utc1, utc2, &tai1, &tai2);
|
||||
SOFA(iauTaitt, tai1, tai2, &tt1, &tt2);
|
||||
iauAtic13(appRA, appDecl, tt1, tt2, &ri, &dec, &eo);
|
||||
ra = iauAnp(ri + eo);
|
||||
ra *= DR2S;
|
||||
dec *= DR2AS;
|
||||
DBG("SOFA: r=%g'', d=%g''", ra, dec);
|
||||
#undef SOFA
|
||||
rtn:
|
||||
if(r) *r = ra;
|
||||
if(dc) *dc = dec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief J2000toJnow - convert ra/dec between epochs
|
||||
* @param in - J2000 (degrees)
|
||||
* @param out - Jnow (degrees)
|
||||
* @return
|
||||
*/
|
||||
int J2000toJnow(const polar *in, polar *out){
|
||||
if(!out) return 1;
|
||||
double utc1, utc2;
|
||||
time_t tsec;
|
||||
struct tm *ts;
|
||||
tsec = time(0); // number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC)
|
||||
ts = gmtime(&tsec);
|
||||
int result = 0;
|
||||
result = iauDtf2d ( "UTC", ts->tm_year+1900, ts->tm_mon+1, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, &utc1, &utc2 );
|
||||
if (result != 0) {
|
||||
fprintf(stderr, "iauDtf2d call failed\n");
|
||||
return 1;
|
||||
}
|
||||
// Make TT julian date for Atci13 call
|
||||
double tai1, tai2;
|
||||
double tt1, tt2;
|
||||
result = iauUtctai(utc1, utc2, &tai1, &tai2);
|
||||
if(result){
|
||||
fprintf(stderr, "iauUtctai call failed\n");
|
||||
return 1;
|
||||
}
|
||||
result = iauTaitt(tai1, tai2, &tt1, &tt2);
|
||||
if(result){
|
||||
fprintf(stderr, "iauTaitt call failed\n");
|
||||
return 1;
|
||||
}
|
||||
double pr = 0.0; // RA proper motion (radians/year; Note 2)
|
||||
double pd = 0.0; // Dec proper motion (radians/year)
|
||||
double px = 0.0; // parallax (arcsec)
|
||||
double rv = 0.0; // radial velocity (km/s, positive if receding)
|
||||
double rc = DD2R * in->ra, dc = DD2R * in->dec; // convert into radians
|
||||
double ri, di, eo;
|
||||
iauAtci13(rc, dc, pr, pd, px, rv, tt1, tt2, &ri, &di, &eo);
|
||||
out->ra = iauAnp(ri - eo) * DR2D;
|
||||
out->dec = di * DR2D;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +1,10 @@
|
||||
/*
|
||||
* main.c
|
||||
* This file is part of the Snippets project.
|
||||
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* Copyright 2014 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* 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; either version 2 of the License, or
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
@@ -14,509 +13,106 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <endian.h>
|
||||
#include <math.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h> // wait
|
||||
#include <sys/prctl.h> //prctl
|
||||
#include <usefull_macros.h>
|
||||
|
||||
#include "emulation.h"
|
||||
#include "libsofa.h"
|
||||
#include "main.h"
|
||||
#include "socket.h"
|
||||
#include "telescope.h"
|
||||
#include "args.h"
|
||||
#include "server.h"
|
||||
#include "mount.h"
|
||||
|
||||
// daemon.c
|
||||
extern void check4running(char *self, char *pidfilename, void (*iffound)(pid_t pid));
|
||||
static pid_t childpid = -1; // PID of child process
|
||||
static const char *pidfile = NULL;
|
||||
|
||||
// Max amount of connections
|
||||
#define BACKLOG (10)
|
||||
#define BUFLEN (1024)
|
||||
// pause for incoming message waiting (out coordinates sent after that timeout)
|
||||
#define SOCK_TMOUT (1)
|
||||
|
||||
static pid_t childpid = 1; // PID of child process
|
||||
volatile int global_quit = 0;
|
||||
// quit by signal
|
||||
void signals(int sig){
|
||||
signal(sig, SIG_IGN);
|
||||
if(childpid){ // parent process
|
||||
restore_tty(); // restore all parameters
|
||||
unlink(GP->pidfile); // and remove pidfile
|
||||
weatherserver_disconnect();
|
||||
}
|
||||
DBG("Get signal %d, quit.\n", sig);
|
||||
global_quit = 1;
|
||||
if(childpid) putlog("PID %d exit with status %d after child's %d death", getpid(), sig, childpid);
|
||||
else WARNX("Child %d died with %d", getpid(), sig);
|
||||
sleep(1);
|
||||
exit(sig);
|
||||
}
|
||||
|
||||
// search a first word after needle without spaces
|
||||
char* stringscan(char *str, char *needle){
|
||||
char *a, *e;
|
||||
char *end = str + strlen(str);
|
||||
a = strstr(str, needle);
|
||||
if(!a) return NULL;
|
||||
a += strlen(needle);
|
||||
while (a < end && (*a == ' ' || *a == '\r' || *a == '\t')) a++;
|
||||
if(a >= end) return NULL;
|
||||
e = strchr(a, ' ');
|
||||
if(e) *e = 0;
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to user
|
||||
* @param data - data to send
|
||||
* @param dlen - data length
|
||||
* @param sockfd - socket fd for sending data
|
||||
* @return 0 if failed
|
||||
*/
|
||||
int send_data(uint8_t *data, size_t dlen, int sockfd){
|
||||
size_t sent = write(sockfd, data, dlen);
|
||||
if(sent != dlen){
|
||||
WARN("write()");
|
||||
return 0;
|
||||
if(childpid){ // single or parent process
|
||||
if(pidfile) unlink(pidfile); // remove pidfile
|
||||
if(childpid > 0) kill(childpid, SIGTERM);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//read: 0x14 0x0 0x0 0x0 0x5b 0x5a 0x2e 0xc6 0x8c 0x23 0x5 0x0 0x23 0x9 0xe5 0xaf 0x23 0x2e 0x34 0xed
|
||||
// command: goto 16h29 24.45 -26d25 55.62
|
||||
/*
|
||||
LITTLE-ENDIAN!!!
|
||||
from client:
|
||||
LENGTH (2 bytes, integer): length of the message
|
||||
TYPE (2 bytes, integer): 0
|
||||
TIME (8 bytes, integer): current time on the server computer in microseconds
|
||||
since 1970.01.01 UT. Currently unused.
|
||||
RA (4 bytes, unsigned integer): right ascension of the telescope (J2000)
|
||||
a value of 0x100000000 = 0x0 means 24h=0h,
|
||||
a value of 0x80000000 means 12h
|
||||
DEC (4 bytes, signed integer): declination of the telescope (J2000)
|
||||
a value of -0x40000000 means -90degrees,
|
||||
a value of 0x0 means 0degrees,
|
||||
a value of 0x40000000 means 90degrees
|
||||
|
||||
to client:
|
||||
LENGTH (2 bytes, integer): length of the message
|
||||
TYPE (2 bytes, integer): 0
|
||||
TIME (8 bytes, integer): current time on the server computer in microseconds
|
||||
since 1970.01.01 UT. Currently unused.
|
||||
RA (4 bytes, unsigned integer): right ascension of the telescope (J2000)
|
||||
a value of 0x100000000 = 0x0 means 24h=0h,
|
||||
a value of 0x80000000 means 12h
|
||||
DEC (4 bytes, signed integer): declination of the telescope (J2000)
|
||||
a value of -0x40000000 means -90degrees,
|
||||
a value of 0x0 means 0degrees,
|
||||
a value of 0x40000000 means 90degrees
|
||||
STATUS (4 bytes, signed integer): status of the telescope, currently unused.
|
||||
status=0 means ok, status<0 means some error
|
||||
*/
|
||||
|
||||
#define DEG2DEC(degr) ((int32_t)(degr / 90. * ((double)0x40000000)))
|
||||
#define HRS2RA(hrs) ((uint32_t)(hrs / 12. * ((double)0x80000000)))
|
||||
#define DEC2DEG(i32) (((double)i32)*90./((double)0x40000000))
|
||||
#define RA2HRS(u32) (((double)u32)*12. /((double)0x80000000))
|
||||
|
||||
typedef struct __attribute__((__packed__)){
|
||||
uint16_t len;
|
||||
uint16_t type;
|
||||
uint64_t time;
|
||||
uint32_t ra;
|
||||
int32_t dec;
|
||||
} indata;
|
||||
|
||||
typedef struct __attribute__((__packed__)){
|
||||
uint16_t len;
|
||||
uint16_t type;
|
||||
uint64_t time;
|
||||
uint32_t ra;
|
||||
int32_t dec;
|
||||
int32_t status;
|
||||
} outdata;
|
||||
|
||||
/**
|
||||
* convert RA/DEC to string in forman RA: HH:MM:SS.SS, DEC: DD:MM:SS.S
|
||||
*/
|
||||
char *radec2str(double ra, double dec){
|
||||
static char buf[1024];
|
||||
char sign = '+';
|
||||
if(dec < 0){
|
||||
sign = '-';
|
||||
dec = -dec;
|
||||
if(sig){
|
||||
signal(sig, SIG_IGN);
|
||||
DBG("Get signal %d, quit.\n", sig);
|
||||
}
|
||||
|
||||
int h = (int)ra;
|
||||
ra -= h; ra *= 60.;
|
||||
int m = (int)ra;
|
||||
ra -= m; ra *= 60.;
|
||||
|
||||
int d = (int) dec;
|
||||
dec -= d; dec *= 60.;
|
||||
int dm = (int)dec;
|
||||
dec -= dm; dec *= 60.;
|
||||
snprintf(buf, 1024, "%d:%d:%.2f %c%d:%d:%.1f", h,m,ra, sign,d,dm,dec);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* send input RA/Decl (j2000!) coordinates to tel
|
||||
* ra in hours (0..24), decl in degrees (-90..90)
|
||||
* @return 1 if all OK
|
||||
*/
|
||||
int setCoords(double ra, double dec){
|
||||
char *radec = radec2str(ra, dec);
|
||||
DBG("Set RA/Decl to %s", radec);
|
||||
putlog("Try to set RA/Decl to %s", radec);
|
||||
int (*pointfunction)(double, double) = point_telescope;
|
||||
if(GP->emulation) pointfunction = point_emulation;
|
||||
return pointfunction(ra, dec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief proc_data - process data received from Stellarium
|
||||
* @param data - raw data
|
||||
* @param len - its length
|
||||
* @return 1 if all OK
|
||||
*/
|
||||
int proc_data(uint8_t *data, ssize_t len){
|
||||
FNAME();
|
||||
if(len != sizeof(indata)){
|
||||
WARNX("Bad data size: got %zd instead of %zd!", len, sizeof(indata));
|
||||
return 0;
|
||||
if(childpid > 0){ // parent process
|
||||
LOGERR("PID %d exit with status %d", getpid(), sig);
|
||||
}else{
|
||||
LOGWARN("Child %d died with %d", getpid(), sig);
|
||||
server_stop();
|
||||
}
|
||||
indata *dat = (indata*)data;
|
||||
uint16_t L, T;
|
||||
//uint64_t tim;
|
||||
uint32_t ra;
|
||||
int32_t dec;
|
||||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
||||
L = le16toh(dat->len); T = le16toh(dat->type);
|
||||
//tim = le64toh(dat->time);
|
||||
ra = le32toh(dat->ra);
|
||||
dec = (int32_t)le32toh((uint32_t)dat->dec);
|
||||
#else
|
||||
L = dat->len; T = dat->type;
|
||||
//tim = dat->time;
|
||||
ra = dat->ra; dec = dat->dec;
|
||||
#endif
|
||||
DBG("got message with len %u & type %u", L, T);
|
||||
if(L != len){
|
||||
WARNX("Length of message != msg->len");
|
||||
return 0;
|
||||
}
|
||||
if(T){
|
||||
WARNX("Wrong message type");
|
||||
return 0;
|
||||
}
|
||||
// convert RA/DEC to hours/degrees
|
||||
double tagRA = RA2HRS(ra), tagDec = DEC2DEG(dec);
|
||||
DBG("RA: %u (%g), DEC: %d (%g)", ra, tagRA, dec, tagDec);
|
||||
putlog("RA: %u (%g degr), DEC: %d (%g degr)", ra, tagRA, dec, tagDec);
|
||||
// check RA/DEC
|
||||
horizCrds hnow; // without refraction
|
||||
polarCrds p2000, pnow;
|
||||
p2000.ra = tagRA/12. * M_PI;
|
||||
p2000.dec = tagDec * ERFA_DD2R;
|
||||
// now J2000 obs Jnow
|
||||
if(get_ObsPlace(NULL, &p2000, NULL, &pnow, &hnow)){
|
||||
WARNX("Can't convert coordinates to Jnow");
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
int i[4], j[4]; char pm, pm1;
|
||||
eraA2af(2, hnow.az, &pm, i);
|
||||
eraA2af(2, hnow.zd, &pm1, j);
|
||||
DBG("az: %c%02d %02d %02d.%2.d, zd: %c%02d %02d %02d.%2.d",
|
||||
pm, i[0],i[1],i[2],i[3],
|
||||
pm1,j[0],j[1],j[2],j[3]);
|
||||
eraA2af(2, M_PI_2 - hnow.zd, &pm, i);
|
||||
DBG("h: %c%02d %02d %02d.%2.d", pm, i[0],i[1],i[2],i[3]);
|
||||
*/
|
||||
if(hnow.zd > 80.*ERFA_DD2R){
|
||||
WARNX("Z > 80degr (%g), stop telescope", hnow.zd * ERFA_DR2D);
|
||||
putlog("Z=%.1f > 80 - stop!", hnow.zd * ERFA_DR2D);
|
||||
stop_telescope();
|
||||
return 0;
|
||||
}
|
||||
tagRA = (pnow.ra - pnow.eo) / M_PI * 12.;
|
||||
tagDec = pnow.dec / ERFA_DD2R;
|
||||
if(!setCoords(tagRA, tagDec)) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* main socket service procedure
|
||||
*/
|
||||
void *handle_socket(void *sockd){
|
||||
FNAME();
|
||||
if(global_quit) return NULL;
|
||||
outdata dout;
|
||||
int sock = *(int*)sockd;
|
||||
dout.len = htole16(sizeof(outdata));
|
||||
dout.type = 0;
|
||||
int (*getcoords)(double*, double*) = get_telescope_coords;
|
||||
if(GP->emulation) getcoords = get_emul_coords;
|
||||
while(!global_quit){
|
||||
// get coordinates
|
||||
double RA = 0., Decl = 0.;
|
||||
if((dout.status = getcoords(&RA, &Decl)) < 0){
|
||||
WARNX("Error: can't get coordinates");
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
//DBG("got : %g/%g", RA, Decl);
|
||||
dout.ra = htole32(HRS2RA(RA));
|
||||
dout.dec = (int32_t)htole32(DEG2DEC(Decl));
|
||||
if(!send_data((uint8_t*)&dout, sizeof(outdata), sock)) break;
|
||||
//DBG("sent ra = %g, dec = %g", RA2HRS(dout.ra), DEC2DEG(dout.dec));
|
||||
fd_set readfds;
|
||||
struct timeval timeout;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(sock, &readfds);
|
||||
timeout.tv_sec = SOCK_TMOUT; // wait not more than SOCK_TMOUT second
|
||||
timeout.tv_usec = 0;
|
||||
int sel = select(sock + 1 , &readfds , NULL , NULL , &timeout);
|
||||
if(sel < 0){
|
||||
if(errno != EINTR)
|
||||
WARN("select()");
|
||||
continue;
|
||||
}
|
||||
if(!(FD_ISSET(sock, &readfds))) continue;
|
||||
// fill incoming buffer
|
||||
uint8_t buff[BUFLEN+1];
|
||||
ssize_t rd = read(sock, buff, BUFLEN);
|
||||
buff[rd] = 0;
|
||||
DBG("read %zd (%s)", rd, buff);
|
||||
if(rd <= 0){ // error or disconnect
|
||||
DBG("Nothing to read from fd %d (ret: %zd)", sock, rd);
|
||||
break;
|
||||
}
|
||||
/**************************************
|
||||
* DO SOMETHING WITH DATA *
|
||||
**************************************/
|
||||
if(!proc_data(buff, rd)) dout.status = -1;
|
||||
else dout.status = 0;
|
||||
}
|
||||
close(sock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// thread writing FITS-header file
|
||||
static void *hdrthread(_U_ void *buf){
|
||||
// write FITS-header at most once per second
|
||||
while(!global_quit){
|
||||
wrhdr();
|
||||
usleep(100000); // give a chance to write/read for others
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief opensocket - open socket to port `port`
|
||||
* @return socket fd or <0 if failed
|
||||
*/
|
||||
static int opensocket(char *port){
|
||||
if(!port) return -1;
|
||||
int reuseaddr = 1;
|
||||
int sock;
|
||||
struct addrinfo hints, *res, *p;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
DBG("try to open port %s", port);
|
||||
if(getaddrinfo(NULL, port, &hints, &res) != 0){
|
||||
WARN("getaddrinfo()");
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
struct sockaddr_in *ia = (struct sockaddr_in*)res->ai_addr;
|
||||
char str[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &(ia->sin_addr), str, INET_ADDRSTRLEN);
|
||||
*/
|
||||
// loop through all the results and bind to the first we can
|
||||
for(p = res; p != NULL; p = p->ai_next){
|
||||
if((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
|
||||
WARN("socket()");
|
||||
continue;
|
||||
}
|
||||
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int)) == -1){
|
||||
WARN("setsockopt()");
|
||||
close(sock);
|
||||
continue;
|
||||
}
|
||||
if(bind(sock, p->ai_addr, p->ai_addrlen) == -1){
|
||||
WARN("bind()");
|
||||
close(sock);
|
||||
continue;
|
||||
}
|
||||
break; // if we get here, we must have connected successfully
|
||||
}
|
||||
freeaddrinfo(res);
|
||||
// Listen
|
||||
if(listen(sock, BACKLOG) == -1){
|
||||
WARN("listen");
|
||||
putlog("listen() error");
|
||||
}
|
||||
DBG("listen at %s", port);
|
||||
putlog("listen at %s", port);
|
||||
return sock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief waitconn
|
||||
* @param sock - socket fd to accept()
|
||||
* @param connthread - thread which to run when connection accepted (it's parameter - socket fd)
|
||||
*/
|
||||
static void waitconn(int sock, void *(*connthread)(void*)){
|
||||
// Main loop
|
||||
while(!global_quit){
|
||||
socklen_t size = sizeof(struct sockaddr_in);
|
||||
struct sockaddr_in myaddr;
|
||||
int newsock;
|
||||
newsock = accept(sock, (struct sockaddr*)&myaddr, &size);
|
||||
if(newsock <= 0){
|
||||
WARN("accept()");
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
struct sockaddr_in peer;
|
||||
socklen_t peer_len = sizeof(peer);
|
||||
if(getpeername(newsock, (struct sockaddr*)&peer, &peer_len) == -1){
|
||||
WARN("getpeername()");
|
||||
close(newsock);
|
||||
continue;
|
||||
}
|
||||
int sockport = -1;
|
||||
if(getsockname(newsock, (struct sockaddr*)&peer, &peer_len) == 0){
|
||||
sockport = ntohs(peer.sin_port);
|
||||
}
|
||||
char *peerIP = inet_ntoa(peer.sin_addr);
|
||||
putlog("Got connection from %s @ %d", peerIP, sockport);
|
||||
DBG("Peer's IP address is: %s (@port %d)\n", peerIP, sockport);
|
||||
/*if(strcmp(peerIP, ACCEPT_IP) && strcmp(peerIP, "127.0.0.1")){
|
||||
WARNX("Wrong IP");
|
||||
close(newsock);
|
||||
continue;
|
||||
}*/
|
||||
pthread_t rthrd;
|
||||
if(pthread_create(&rthrd, NULL, connthread, (void*)&newsock)){
|
||||
putlog("Error creating listen thread");
|
||||
ERR(_("Can't create socket thread"));
|
||||
}else{
|
||||
DBG("Thread created, detouch");
|
||||
pthread_detach(rthrd); // don't care about thread state
|
||||
}
|
||||
}
|
||||
close(sock);
|
||||
}
|
||||
|
||||
// thread working with terminal
|
||||
static void *termthread(_U_ void *buf){
|
||||
int sock = opensocket(GP->dbgport);
|
||||
if(sock < 0){
|
||||
putlog("Can't open debugging socket @ port %s", GP->dbgport);
|
||||
ERRX("Can't open debug socket");
|
||||
}
|
||||
waitconn(sock, term_thread);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void main_proc(){
|
||||
pthread_t hthrd, termthrd;
|
||||
// connect to telescope
|
||||
if(!GP->emulation){
|
||||
if(!connect_telescope(GP->device, GP->crdsfile)){
|
||||
ERRX(_("Can't connect to telescope device"));
|
||||
}
|
||||
if(pthread_create(&hthrd, NULL, hdrthread, NULL))
|
||||
ERR(_("Can't create writing thread"));
|
||||
if(pthread_create(&termthrd, NULL, termthread, NULL))
|
||||
ERR(_("Can't create terminal thread"));
|
||||
}
|
||||
// connect to weather daemon
|
||||
if(!weatherserver_connect()){
|
||||
DBG("Can't connect to weather server, will try later");
|
||||
}
|
||||
// open socket
|
||||
int sock = opensocket(GP->port);
|
||||
if(sock < 0){
|
||||
putlog("Can't open socket @ port %s", GP->port);
|
||||
ERRX("Can't open stellarium socket");
|
||||
}
|
||||
waitconn(sock, handle_socket);
|
||||
usleep(10000);
|
||||
pthread_cancel(hthrd); // cancel reading thread
|
||||
pthread_cancel(termthrd);
|
||||
pthread_join(hthrd, NULL);
|
||||
pthread_join(termthrd, NULL);
|
||||
DBG("EXIT");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
char *self = strdup(argv[0]);
|
||||
GP = parse_args(argc, argv);
|
||||
initial_setup();
|
||||
|
||||
signal(SIGTERM, signals); // kill (-15) - quit
|
||||
signal(SIGKILL, signals); // kill (-9) - quit
|
||||
signal(SIGHUP, SIG_IGN); // hup - ignore
|
||||
signal(SIGINT, signals); // ctrl+C - quit
|
||||
signal(SIGQUIT, signals); // ctrl+\ - quit
|
||||
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
||||
|
||||
sl_init();
|
||||
parameters_t *G = parse_cmdline(&argc, &argv);
|
||||
if(!G) return 1;
|
||||
sl_loglevel_e lvl = G->verbose + LOGLEVEL_ERR;
|
||||
if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1;
|
||||
DBG("verb: %d, level: %d", G->verbose, lvl);
|
||||
int fd;
|
||||
if((fd = open(GP->crdsfile, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) // test FITS-header file for writing
|
||||
ERR(_("Can't open %s for writing"), GP->crdsfile);
|
||||
if((fd = open(G->crdsfile, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) // test FITS-header file for writing
|
||||
ERR(_("Can't open %s for writing"), G->crdsfile);
|
||||
close(fd);
|
||||
|
||||
printf("Daemonize\n");
|
||||
#ifndef EBUG // daemonize only in release mode
|
||||
if(daemon(1, 0)){
|
||||
putlog("Err: daemon()");
|
||||
ERR("daemon()");
|
||||
}
|
||||
#endif // EBUG
|
||||
check4running(self, GP->pidfile, NULL);
|
||||
if(GP->logfile) openlogfile(GP->logfile);
|
||||
putlog("Starting, master PID=%d", getpid());
|
||||
|
||||
if(G->sleept < 1) ERRX("Sleeping time sould be positive value");
|
||||
if(!server_setsleept(G->sleept)) ERRX("Can't set sleep time to %d", G->sleept);
|
||||
if(G->emulation) set_emulation_mode();
|
||||
else if(!mount_set_dev(G->device, G->serspeed, G->sertmout))
|
||||
ERRX("Can't open device %s @ %d", G->device, G->serspeed);
|
||||
if(!mount_set_name(G->mountname))
|
||||
ERRX("Can't set mount name to %s", G->mountname);
|
||||
signal(SIGTERM, signals);
|
||||
signal(SIGINT, signals);
|
||||
signal(SIGQUIT, signals);
|
||||
signal(SIGTSTP, SIG_IGN);
|
||||
signal(SIGHUP, signals);
|
||||
sl_check4running((char*)__progname, G->pidfile);
|
||||
if(G->logfile) OPENLOG(G->logfile, lvl, 1);
|
||||
DBG("Started");
|
||||
LOGMSG("Started, master PID=%d", getpid());
|
||||
#ifndef EBUG
|
||||
while(1){
|
||||
time_t lastd = 0;
|
||||
while(1){ // guard for dead processes
|
||||
childpid = fork();
|
||||
if(childpid < 0){
|
||||
putlog("fork() error");
|
||||
ERR("ERROR on fork");
|
||||
LOGERR("fork() returns error");
|
||||
sleep(5);
|
||||
continue;
|
||||
}
|
||||
if(childpid){
|
||||
WARNX(_("Created child with PID %d\n"), childpid);
|
||||
DBG("Created child with PID %d\n", childpid);
|
||||
wait(NULL);
|
||||
WARNX(_("Child %d died\n"), childpid);
|
||||
DBG("Child %d died\n", childpid);
|
||||
time_t t = time(NULL);
|
||||
if(t - lastd > 600){ // at least 10 minutes of work
|
||||
LOGERR("Child %d died\n", childpid);
|
||||
}
|
||||
lastd = t;
|
||||
WARNX("Child %d died\n", childpid);
|
||||
sleep(1);
|
||||
}else{
|
||||
prctl(PR_SET_PDEATHSIG, SIGTERM); // send SIGTERM to child when parent dies
|
||||
main_proc();
|
||||
return 0;
|
||||
break; // go out to normal functional
|
||||
}
|
||||
}
|
||||
#else
|
||||
main_proc();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
server_sock_t sockt = {
|
||||
.cmd_isunix = G->isunix,
|
||||
.cmdnode = G->cmdnode,
|
||||
.stellport = G->port,
|
||||
.maxclients = G->maxclients,
|
||||
};
|
||||
if(!server_check(&sockt)){
|
||||
LOGERR("Can't run servers");
|
||||
ERRX("Can't run servers");
|
||||
}
|
||||
DBG("Run server");
|
||||
server_run();
|
||||
DBG("Server died");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* main.h
|
||||
*
|
||||
* Copyright 2014 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef __MAIN_H__
|
||||
#define __MAIN_H__
|
||||
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <libintl.h>
|
||||
#include <locale.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cmdlnopts.h"
|
||||
#include "usefull_macro.h"
|
||||
|
||||
// global parameters
|
||||
extern glob_pars *Global_parameters;
|
||||
// global quit flag
|
||||
extern volatile int global_quit;
|
||||
#endif // __MAIN_H__
|
||||
288
Daemons/10micron_stellarium/mount.c
Normal file
288
Daemons/10micron_stellarium/mount.c
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* This file is part of the mountdaemon_10micron project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "angles.h"
|
||||
#include "emulation.h"
|
||||
#include "mount.h"
|
||||
|
||||
|
||||
#define MNAME_LEN 31
|
||||
|
||||
static bool isemulated = false; // ==true for emulation mode
|
||||
|
||||
static char mount_name[MNAME_LEN+3] = "'noname'";
|
||||
static sl_tty_t *mount_dev = NULL;
|
||||
// device mutex, blocking only in non-local functions
|
||||
static pthread_mutex_t mntdev_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
// ring buffer for data incoming from serial port
|
||||
static sl_ringbuffer_t *RBin = NULL;
|
||||
// status
|
||||
static atomic_int mountstatus = MNT_S_ERROR;
|
||||
|
||||
// input and current target coordinates
|
||||
polarCrds_t InpCoords = {0}, TagCoords = {0};
|
||||
horizCrds_t InpHoriz = {0};
|
||||
|
||||
// change input coordinates
|
||||
/**
|
||||
* @brief mount_setInpHA - set hour angle
|
||||
* @param ha (HOURS!!)
|
||||
* @return false if `ha` isn't in [0,24)
|
||||
*/
|
||||
bool mount_setInpHA(double ha){
|
||||
if(ha < 0. || ha >= 24.) return false;
|
||||
InpCoords.ha = HRS2RAD(ha);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* @brief mount_setInpRA - set right ascension
|
||||
* @param ra (DEGREES!)
|
||||
* @return fale if `ra` isn't in [0, 360)
|
||||
*/
|
||||
bool mount_setInpRA(double ra){
|
||||
if(ra < 0. || ra >= 360.) return false;
|
||||
InpCoords.ra = DEG2RAD(ra);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* @brief mount_setInpDec - set declination
|
||||
* @param dec (DEGREES!)
|
||||
* @return false if `dec` isn't in [-90, 90]
|
||||
*/
|
||||
bool mount_setInpDec(double dec){
|
||||
if(dec < -90. || dec > 90.) return false;
|
||||
InpCoords.dec = DEG2RAD(dec);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* @brief mount_setInpA - set azimuth
|
||||
* @param A (DEGREES)
|
||||
* @return false if A isn't in [0, 360)
|
||||
*/
|
||||
bool mount_setInpA(double A){
|
||||
if(A < 0. || A >= 360.) return false;
|
||||
InpHoriz.az = DEG2RAD(A);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* @brief mount_setInpZ - set zenith distance
|
||||
* @param Z (DEGREES)
|
||||
* @return false if Z isn't in [0, 90]
|
||||
*/
|
||||
bool mount_setInpZ(double Z){
|
||||
if(Z < 0 || Z > 90) return false;
|
||||
InpHoriz.zd = DEG2RAD(Z);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief mount_set_name - set mount name for FITS header
|
||||
* @param name - new string with name
|
||||
* @return false if failed
|
||||
*/
|
||||
bool mount_set_name(const char *name){
|
||||
if(!name || !*name) return false;
|
||||
int l = strlen(name);
|
||||
if(l > MNAME_LEN) return false;
|
||||
sprintf(mount_name, "'%s'", name);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief mount_set_dev - open mount device without checking that mount is alive
|
||||
* @param dev - path to device
|
||||
* @param speed - baudrate
|
||||
* @return false if failed to open device `dev`
|
||||
*/
|
||||
bool mount_set_dev(char *dev, int speed, int timeout){
|
||||
pthread_mutex_lock(&mntdev_mutex);
|
||||
if(mount_dev) sl_tty_close(&mount_dev);
|
||||
mount_dev = sl_tty_new(dev, speed, 4096);
|
||||
if(mount_dev) mount_dev = sl_tty_open(mount_dev, 1);
|
||||
if(!mount_dev){
|
||||
pthread_mutex_unlock(&mntdev_mutex);
|
||||
return false;
|
||||
}
|
||||
sl_tty_tmout(timeout);
|
||||
if(!RBin) RBin = sl_RB_new(BUFSIZ);
|
||||
else sl_RB_clearbuf(RBin);
|
||||
pthread_mutex_unlock(&mntdev_mutex);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char *statuses[MNT_S_STATAMOUNT] = {
|
||||
[MNT_S_TRACKING] = "'Tracking'",
|
||||
[MNT_S_STOPHOM] = "'Stopped or homing'",
|
||||
[MNT_S_PARKING] = "'Slewing to park'",
|
||||
[MNT_S_UNPARKING] = "'Unparking'",
|
||||
[MNT_S_HOMING] = "'Slewing to home'",
|
||||
[MNT_S_PARKED] = "'Parked'",
|
||||
[MNT_S_SLEWING] = "'Slewing or going to stop'",
|
||||
[MNT_S_STOPPED] = "'Stopped'",
|
||||
[MNT_S_INHIBITED] = "'Motors inhibited, T too low'",
|
||||
[MNT_S_OUTLIMIT] = "'Outside tracking limit'",
|
||||
[MNT_S_FOLSAT]= "'Following satellite'",
|
||||
[MNT_S_DATINCOSIST]= "'Data inconsistency'",
|
||||
[MNT_S_ERROR] = "'Error (disconnected?)'"
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief strstatus - return string explanation of mount status
|
||||
* @param status - integer status code
|
||||
* @return statically allocated string with explanation
|
||||
*/
|
||||
const char* mount_status_str(){
|
||||
int curst = atomic_load(&mountstatus);
|
||||
if(curst > -1 && curst < MNT_S_STATAMOUNT) return statuses[curst];
|
||||
return "'Unknown status'";
|
||||
}
|
||||
// return current mount status
|
||||
mount_status_t mount_status(){
|
||||
return (mount_status_t)atomic_load(&mountstatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief write_cmd - try to write command to mount
|
||||
* @param cmd - string with command or NULL just to clear all incoming data
|
||||
* @return false on write/read error; all read information is in RBin (even if `false` returned, you SHOULD read all available strings from it)
|
||||
*/
|
||||
static bool write_cmd(const char *cmd){
|
||||
bool ret = true;
|
||||
if(cmd){
|
||||
size_t l = strlen(cmd);
|
||||
if(sl_tty_write(mount_dev->comfd, cmd, l)) ret = false;
|
||||
}
|
||||
int got = 0;
|
||||
while((got = sl_tty_read(mount_dev)) > 0){
|
||||
if(sl_RB_write(RBin, (uint8_t*) mount_dev->buf, got)){
|
||||
got = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(got < 0) return false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// check if mount connected
|
||||
static bool chkconn(){
|
||||
int r = 0;
|
||||
do{ // clear incoming buffer @ start
|
||||
r = sl_tty_read(mount_dev);
|
||||
}while(r > 0);
|
||||
if(r < 0) return false;
|
||||
write_cmd("#"); // clear cmd buffer
|
||||
sl_RB_clearbuf(RBin);
|
||||
bool w = write_cmd(":SB0#");
|
||||
sl_RB_clearbuf(RBin);
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
// try to guess serial speed & set 115200
|
||||
static bool guess_speed(){
|
||||
if(!mount_dev) return false;
|
||||
close(mount_dev->comfd);
|
||||
#define SPDBUFSZ 7
|
||||
const int speeds[SPDBUFSZ] = {57600, 38400, 19200, 9600, 4800, 2400, 1200};
|
||||
int idx = 0;
|
||||
for(; idx < SPDBUFSZ; ++idx){
|
||||
mount_dev->speed = speeds[idx];
|
||||
sl_tty_t *trydev = sl_tty_open(mount_dev, 1);
|
||||
if(!trydev) continue;
|
||||
if(chkconn()) break;
|
||||
close(mount_dev->comfd);
|
||||
}
|
||||
if(idx == SPDBUFSZ) return false; // device not responding
|
||||
close(mount_dev->comfd);
|
||||
mount_dev->speed = 115200;
|
||||
if(!sl_tty_open(mount_dev, 1)) return false;
|
||||
#undef SPDBUFSZ
|
||||
return true;
|
||||
}
|
||||
|
||||
// connect to mount
|
||||
bool mount_connect(){
|
||||
if(isemulated){
|
||||
atomic_store(&mountstatus, MNT_S_STOPPED);
|
||||
return true;
|
||||
}
|
||||
if(!mount_dev) return false;
|
||||
pthread_mutex_lock(&mntdev_mutex);
|
||||
if(!chkconn() && !guess_speed()) return false;
|
||||
bool ret = true;
|
||||
if(!write_cmd(":STOP#")) ret = false; // stop tracking after poweron
|
||||
sl_RB_clearbuf(RBin);
|
||||
if(!write_cmd(":U2#")) ret = false; // set high precision
|
||||
sl_RB_clearbuf(RBin);
|
||||
if(!write_cmd(":So10#")) ret = false; // set minimum altitude to 10 degrees
|
||||
sl_RB_clearbuf(RBin);
|
||||
pthread_mutex_unlock(&mntdev_mutex);
|
||||
if(ret) LOGMSG("Connected to %s@115200", mount_dev->portname);
|
||||
else LOGERR("Can't write commands to mount");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mount_disconnect(){
|
||||
if(isemulated) return;
|
||||
pthread_mutex_trylock(&mntdev_mutex); // at least, try
|
||||
if(mount_dev) close(mount_dev->comfd);
|
||||
pthread_mutex_unlock(&mntdev_mutex);
|
||||
}
|
||||
|
||||
// point to ra/dec over serial
|
||||
static bool mount_pointto(double ra, double dec){
|
||||
(void) ra; (void) dec;
|
||||
;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* send input RA/Decl (j2000!) coordinates to tel
|
||||
* ra in hours (0..24), decl in degrees (-90..90)
|
||||
* @return true if all OK
|
||||
*/
|
||||
bool mount_point(double ra, double dec){
|
||||
char buf[RADEC_STR_MAXLEN];
|
||||
radec2str(ra, dec, buf);
|
||||
DBG("Set RA/Decl to %s", buf);
|
||||
LOGMSG("Try to set RA/Decl to %s", buf);
|
||||
norm_RADEC(&ra, &dec);
|
||||
bool (*pointfunction)(double, double) = mount_pointto;
|
||||
if(isemulated) pointfunction = point_emulation;
|
||||
return pointfunction(ra, dec);
|
||||
}
|
||||
|
||||
void set_emulation_mode(){
|
||||
isemulated = true;
|
||||
}
|
||||
|
||||
mount_status_t mount_getcoords(double *ra, double *dec){
|
||||
if(!ra || !dec) return MNT_S_ERROR;
|
||||
if(isemulated){
|
||||
get_emul_coords(ra, dec);
|
||||
DBG("Emulated coordinates: %g, %g", *ra, *dec);
|
||||
}else{
|
||||
; // get real coordinates
|
||||
}
|
||||
return mount_status();
|
||||
}
|
||||
57
Daemons/10micron_stellarium/mount.h
Normal file
57
Daemons/10micron_stellarium/mount.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is part of the mountdaemon_10micron project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <usefull_macros.h>
|
||||
|
||||
// mount statuses
|
||||
typedef enum{
|
||||
MNT_S_TRACKING = 0,
|
||||
MNT_S_STOPHOM = 1,
|
||||
MNT_S_PARKING = 2,
|
||||
MNT_S_UNPARKING = 3,
|
||||
MNT_S_HOMING = 4,
|
||||
MNT_S_PARKED = 5,
|
||||
MNT_S_SLEWING = 6,
|
||||
MNT_S_STOPPED = 7,
|
||||
MNT_S_INHIBITED = 8,
|
||||
MNT_S_OUTLIMIT = 9,
|
||||
MNT_S_FOLSAT = 10,
|
||||
MNT_S_DATINCOSIST = 11,
|
||||
MNT_S_ERROR = 12, // my status
|
||||
MNT_S_STATAMOUNT = 13 // number of statuses
|
||||
} mount_status_t;
|
||||
|
||||
void set_emulation_mode();
|
||||
|
||||
bool mount_setInpHA(double ha);
|
||||
bool mount_setInpRA(double ra);
|
||||
bool mount_setInpDec(double dec);
|
||||
bool mount_setInpA(double A);
|
||||
bool mount_setInpZ(double Z);
|
||||
|
||||
bool mount_set_name(const char *name);
|
||||
bool mount_set_dev(char *dev, int speed, int timeout);
|
||||
const char* mount_status_str();
|
||||
mount_status_t mount_status();
|
||||
bool mount_connect();
|
||||
void mount_disconnect();
|
||||
|
||||
mount_status_t mount_getcoords(double *ra, double *dec);
|
||||
bool mount_point(double ra, double dec);
|
||||
1
Daemons/10micron_stellarium/mountdaemon_10micron.cflags
Normal file
1
Daemons/10micron_stellarium/mountdaemon_10micron.cflags
Normal file
@@ -0,0 +1 @@
|
||||
-std=c23
|
||||
@@ -1,3 +1,7 @@
|
||||
// Add predefined macros for your project here. For example:
|
||||
// #define THE_ANSWER 42
|
||||
#define EBUG 1
|
||||
#define _XOPEN_SOURCE 1234
|
||||
#define _DEFAULT_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#define EBUG
|
||||
|
||||
220
Daemons/10micron_stellarium/mountdaemon_10micron.creator.user
Normal file
220
Daemons/10micron_stellarium/mountdaemon_10micron.creator.user
Normal file
@@ -0,0 +1,220 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 19.0.2, 2026-06-10T13:41:37. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
<value type="QByteArray">{cf63021e-ef53-49b0-b03b-2f2570cdf3b6}</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
<value type="qlonglong">0</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoDetect">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||
<value type="QString" key="language">QmlJS</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
|
||||
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.LineEndingBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
|
||||
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">false</value>
|
||||
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
|
||||
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
|
||||
<value type="bool" key="EditorConfiguration.inEntireDocument">true</value>
|
||||
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
|
||||
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
|
||||
<value type="bool" key="AutoTest.Framework.Boost">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.CTest">false</value>
|
||||
<value type="bool" key="AutoTest.Framework.Catch">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.GTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
|
||||
</valuemap>
|
||||
<value type="bool" key="AutoTest.ApplyFilter">false</value>
|
||||
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
|
||||
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
|
||||
<value type="int" key="AutoTest.RunAfterBuild">0</value>
|
||||
<value type="bool" key="AutoTest.UseGlobal">true</value>
|
||||
<valuemap type="QVariantMap" key="ClangTools">
|
||||
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
|
||||
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
|
||||
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
|
||||
<value type="int" key="ClangTools.ParallelJobs">4</value>
|
||||
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||
</valuemap>
|
||||
<value type="int" key="RcSync">0</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="DeviceType">Desktop</value>
|
||||
<value type="bool" key="HasPerBcDcs">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{91347f2c-5221-46a7-80b1-0a054ca02f79}</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/eddy/Docs/SAO/10micron/C-sources/mountdaemon_10micron</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">all</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
|
||||
<value type="QString">clean</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.SuppressionFiles"/>
|
||||
<value type="QList<int>" key="Analyzer.Valgrind.VisibleErrorKinds"></value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.UniqueId"></value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="qlonglong">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
</qtcreator>
|
||||
16
Daemons/10micron_stellarium/mountdaemon_10micron.files
Normal file
16
Daemons/10micron_stellarium/mountdaemon_10micron.files
Normal file
@@ -0,0 +1,16 @@
|
||||
angles.c
|
||||
angles.h
|
||||
args.c
|
||||
args.h
|
||||
astrosib_proto.h
|
||||
dome.c
|
||||
dome.h
|
||||
emulation.c
|
||||
emulation.h
|
||||
main.c
|
||||
server.c
|
||||
server.h
|
||||
mount.c
|
||||
mount.h
|
||||
stellarium.c
|
||||
stellarium.h
|
||||
@@ -1,497 +0,0 @@
|
||||
/* geany_encoding=koi8-r
|
||||
* parseargs.c - parsing command line arguments & print help
|
||||
*
|
||||
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h> // printf
|
||||
#include <getopt.h> // getopt_long
|
||||
#include <stdlib.h> // calloc, exit, strtoll
|
||||
#include <assert.h> // assert
|
||||
#include <string.h> // strdup, strchr, strlen
|
||||
#include <strings.h>// strcasecmp
|
||||
#include <limits.h> // INT_MAX & so on
|
||||
#include <libintl.h>// gettext
|
||||
#include <ctype.h> // isalpha
|
||||
#include "parseargs.h"
|
||||
#include "usefull_macro.h"
|
||||
|
||||
char *helpstring = "%s\n";
|
||||
|
||||
/**
|
||||
* Change standard help header
|
||||
* MAY consist ONE "%s" for progname
|
||||
* @param str (i) - new format
|
||||
*/
|
||||
void change_helpstring(char *s){
|
||||
int pcount = 0, scount = 0;
|
||||
char *str = s;
|
||||
// check `helpstring` and set it to default in case of error
|
||||
for(; pcount < 2; str += 2){
|
||||
if(!(str = strchr(str, '%'))) break;
|
||||
if(str[1] != '%') pcount++; // increment '%' counter if it isn't "%%"
|
||||
else{
|
||||
str += 2; // pass next '%'
|
||||
continue;
|
||||
}
|
||||
if(str[1] == 's') scount++; // increment "%s" counter
|
||||
};
|
||||
if(pcount > 1 || pcount != scount){ // amount of pcount and/or scount wrong
|
||||
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÓÔÒÏËÉ ÐÏÍÏÝÉ"
|
||||
ERRX(_("Wrong helpstring!"));
|
||||
}
|
||||
helpstring = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Carefull atoll/atoi
|
||||
* @param num (o) - returning value (or NULL if you wish only check number) - allocated by user
|
||||
* @param str (i) - string with number must not be NULL
|
||||
* @param t (i) - T_INT for integer or T_LLONG for long long (if argtype would be wided, may add more)
|
||||
* @return TRUE if conversion sone without errors, FALSE otherwise
|
||||
*/
|
||||
static bool myatoll(void *num, char *str, argtype t){
|
||||
long long tmp, *llptr;
|
||||
int *iptr;
|
||||
char *endptr;
|
||||
assert(str);
|
||||
assert(num);
|
||||
tmp = strtoll(str, &endptr, 0);
|
||||
if(endptr == str || *str == '\0' || *endptr != '\0')
|
||||
return FALSE;
|
||||
switch(t){
|
||||
case arg_longlong:
|
||||
llptr = (long long*) num;
|
||||
*llptr = tmp;
|
||||
break;
|
||||
case arg_int:
|
||||
default:
|
||||
if(tmp < INT_MIN || tmp > INT_MAX){
|
||||
/// "ãÅÌÏÅ ×ÎÅ ÄÏÐÕÓÔÉÍÏÇÏ ÄÉÁÐÁÚÏÎÁ"
|
||||
WARNX(_("Integer out of range"));
|
||||
return FALSE;
|
||||
}
|
||||
iptr = (int*)num;
|
||||
*iptr = (int)tmp;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// the same as myatoll but for double
|
||||
// There's no NAN & INF checking here (what if they would be needed?)
|
||||
static bool myatod(void *num, const char *str, argtype t){
|
||||
double tmp, *dptr;
|
||||
float *fptr;
|
||||
char *endptr;
|
||||
assert(str);
|
||||
tmp = strtod(str, &endptr);
|
||||
if(endptr == str || *str == '\0' || *endptr != '\0')
|
||||
return FALSE;
|
||||
switch(t){
|
||||
case arg_double:
|
||||
dptr = (double *) num;
|
||||
*dptr = tmp;
|
||||
break;
|
||||
case arg_float:
|
||||
default:
|
||||
fptr = (float *) num;
|
||||
*fptr = (float)tmp;
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get index of current option in array options
|
||||
* @param opt (i) - returning val of getopt_long
|
||||
* @param options (i) - array of options
|
||||
* @return index in array
|
||||
*/
|
||||
static int get_optind(int opt, myoption *options){
|
||||
int oind;
|
||||
myoption *opts = options;
|
||||
assert(opts);
|
||||
for(oind = 0; opts->name && opts->val != opt; oind++, opts++);
|
||||
if(!opts->name || opts->val != opt) // no such parameter
|
||||
showhelp(-1, options);
|
||||
return oind;
|
||||
}
|
||||
|
||||
/**
|
||||
* reallocate new value in array of multiple repeating arguments
|
||||
* @arg paptr - address of pointer to array (**void)
|
||||
* @arg type - its type (for realloc)
|
||||
* @return pointer to new (next) value
|
||||
*/
|
||||
void *get_aptr(void *paptr, argtype type){
|
||||
int i = 1;
|
||||
void **aptr = *((void***)paptr);
|
||||
if(aptr){ // there's something in array
|
||||
void **p = aptr;
|
||||
while(*p++) ++i;
|
||||
}
|
||||
size_t sz = 0;
|
||||
switch(type){
|
||||
default:
|
||||
case arg_none:
|
||||
/// "îÅ ÍÏÇÕ ÉÓÐÏÌØÚÏ×ÁÔØ ÎÅÓËÏÌØËÏ ÐÁÒÁÍÅÔÒÏ× ÂÅÚ ÁÒÇÕÍÅÎÔÏ×!"
|
||||
ERRX("Can't use multiple args with arg_none!");
|
||||
break;
|
||||
case arg_int:
|
||||
sz = sizeof(int);
|
||||
break;
|
||||
case arg_longlong:
|
||||
sz = sizeof(long long);
|
||||
break;
|
||||
case arg_double:
|
||||
sz = sizeof(double);
|
||||
break;
|
||||
case arg_float:
|
||||
sz = sizeof(float);
|
||||
break;
|
||||
case arg_string:
|
||||
sz = 0;
|
||||
break;
|
||||
/* case arg_function:
|
||||
sz = sizeof(argfn *);
|
||||
break;*/
|
||||
}
|
||||
aptr = realloc(aptr, (i + 1) * sizeof(void*));
|
||||
*((void***)paptr) = aptr;
|
||||
aptr[i] = NULL;
|
||||
if(sz){
|
||||
aptr[i - 1] = malloc(sz);
|
||||
}else
|
||||
aptr[i - 1] = &aptr[i - 1];
|
||||
return aptr[i - 1];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse command line arguments
|
||||
* ! If arg is string, then value will be strdup'ed!
|
||||
*
|
||||
* @param argc (io) - address of argc of main(), return value of argc stay after `getopt`
|
||||
* @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt`
|
||||
* BE CAREFUL! if you wanna use full argc & argv, save their original values before
|
||||
* calling this function
|
||||
* @param options (i) - array of `myoption` for arguments parcing
|
||||
*
|
||||
* @exit: in case of error this function show help & make `exit(-1)`
|
||||
*/
|
||||
void parseargs(int *argc, char ***argv, myoption *options){
|
||||
char *short_options, *soptr;
|
||||
struct option *long_options, *loptr;
|
||||
size_t optsize, i;
|
||||
myoption *opts = options;
|
||||
// check whether there is at least one options
|
||||
assert(opts);
|
||||
assert(opts[0].name);
|
||||
// first we count how much values are in opts
|
||||
for(optsize = 0; opts->name; optsize++, opts++);
|
||||
// now we can allocate memory
|
||||
short_options = calloc(optsize * 3 + 1, 1); // multiply by three for '::' in case of args in opts
|
||||
long_options = calloc(optsize + 1, sizeof(struct option));
|
||||
opts = options; loptr = long_options; soptr = short_options;
|
||||
// in debug mode check the parameters are not repeated
|
||||
#ifdef EBUG
|
||||
char **longlist = MALLOC(char*, optsize);
|
||||
char *shortlist = MALLOC(char, optsize);
|
||||
#endif
|
||||
// fill short/long parameters and make a simple checking
|
||||
for(i = 0; i < optsize; i++, loptr++, opts++){
|
||||
// check
|
||||
assert(opts->name); // check name
|
||||
#ifdef EBUG
|
||||
longlist[i] = strdup(opts->name);
|
||||
#endif
|
||||
if(opts->has_arg){
|
||||
assert(opts->type != arg_none); // check error with arg type
|
||||
assert(opts->argptr); // check pointer
|
||||
}
|
||||
if(opts->type != arg_none) // if there is a flag without arg, check its pointer
|
||||
assert(opts->argptr);
|
||||
// fill long_options
|
||||
// don't do memcmp: what if there would be different alignment?
|
||||
loptr->name = opts->name;
|
||||
loptr->has_arg = (opts->has_arg < MULT_PAR) ? opts->has_arg : 1;
|
||||
loptr->flag = opts->flag;
|
||||
loptr->val = opts->val;
|
||||
// fill short options if they are:
|
||||
if(!opts->flag && opts->val){
|
||||
#ifdef EBUG
|
||||
shortlist[i] = (char) opts->val;
|
||||
#endif
|
||||
*soptr++ = opts->val;
|
||||
if(loptr->has_arg) // add ':' if option has required argument
|
||||
*soptr++ = ':';
|
||||
if(loptr->has_arg == 2) // add '::' if option has optional argument
|
||||
*soptr++ = ':';
|
||||
}
|
||||
}
|
||||
// sort all lists & check for repeating
|
||||
#ifdef EBUG
|
||||
int cmpstringp(const void *p1, const void *p2){
|
||||
return strcmp(* (char * const *) p1, * (char * const *) p2);
|
||||
}
|
||||
int cmpcharp(const void *p1, const void *p2){
|
||||
return (int)(*(char * const)p1 - *(char *const)p2);
|
||||
}
|
||||
qsort(longlist, optsize, sizeof(char *), cmpstringp);
|
||||
qsort(shortlist,optsize, sizeof(char), cmpcharp);
|
||||
char *prevl = longlist[0], prevshrt = shortlist[0];
|
||||
for(i = 1; i < optsize; ++i){
|
||||
if(longlist[i]){
|
||||
if(prevl){
|
||||
if(strcmp(prevl, longlist[i]) == 0) ERRX("double long arguments: --%s", prevl);
|
||||
}
|
||||
prevl = longlist[i];
|
||||
}
|
||||
if(shortlist[i]){
|
||||
if(prevshrt){
|
||||
if(prevshrt == shortlist[i]) ERRX("double short arguments: -%c", prevshrt);
|
||||
}
|
||||
prevshrt = shortlist[i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// now we have both long_options & short_options and can parse `getopt_long`
|
||||
while(1){
|
||||
int opt;
|
||||
int oindex = 0, optind = 0; // oindex - number of option in argv, optind - number in options[]
|
||||
if((opt = getopt_long(*argc, *argv, short_options, long_options, &oindex)) == -1) break;
|
||||
if(opt == '?'){
|
||||
opt = optopt;
|
||||
optind = get_optind(opt, options);
|
||||
if(options[optind].has_arg == NEED_ARG || options[optind].has_arg == MULT_PAR)
|
||||
showhelp(optind, options); // need argument
|
||||
}
|
||||
else{
|
||||
if(opt == 0 || oindex > 0) optind = oindex;
|
||||
else optind = get_optind(opt, options);
|
||||
}
|
||||
opts = &options[optind];
|
||||
// if(opt == 0 && opts->has_arg == NO_ARGS) continue; // only long option changing integer flag
|
||||
// now check option
|
||||
if(opts->has_arg == NEED_ARG || opts->has_arg == MULT_PAR)
|
||||
if(!optarg) showhelp(optind, options); // need argument
|
||||
void *aptr;
|
||||
if(opts->has_arg == MULT_PAR){
|
||||
aptr = get_aptr(opts->argptr, opts->type);
|
||||
}else
|
||||
aptr = opts->argptr;
|
||||
bool result = TRUE;
|
||||
// even if there is no argument, but argptr != NULL, think that optarg = "1"
|
||||
if(!optarg) optarg = "1";
|
||||
switch(opts->type){
|
||||
default:
|
||||
case arg_none:
|
||||
if(opts->argptr) *((int*)aptr) += 1; // increment value
|
||||
break;
|
||||
case arg_int:
|
||||
result = myatoll(aptr, optarg, arg_int);
|
||||
break;
|
||||
case arg_longlong:
|
||||
result = myatoll(aptr, optarg, arg_longlong);
|
||||
break;
|
||||
case arg_double:
|
||||
result = myatod(aptr, optarg, arg_double);
|
||||
break;
|
||||
case arg_float:
|
||||
result = myatod(aptr, optarg, arg_float);
|
||||
break;
|
||||
case arg_string:
|
||||
result = (*((void**)aptr) = (void*)strdup(optarg));
|
||||
break;
|
||||
case arg_function:
|
||||
result = ((argfn)aptr)(optarg);
|
||||
break;
|
||||
}
|
||||
if(!result){
|
||||
showhelp(optind, options);
|
||||
}
|
||||
}
|
||||
*argc -= optind;
|
||||
*argv += optind;
|
||||
}
|
||||
|
||||
/**
|
||||
* compare function for qsort
|
||||
* first - sort by short options; second - sort arguments without sort opts (by long options)
|
||||
*/
|
||||
static int argsort(const void *a1, const void *a2){
|
||||
const myoption *o1 = (myoption*)a1, *o2 = (myoption*)a2;
|
||||
const char *l1 = o1->name, *l2 = o2->name;
|
||||
int s1 = o1->val, s2 = o2->val;
|
||||
int *f1 = o1->flag, *f2 = o2->flag;
|
||||
// check if both options has short arg
|
||||
if(f1 == NULL && f2 == NULL && s1 && s2){ // both have short arg
|
||||
return (s1 - s2);
|
||||
}else if((f1 != NULL || !s1) && (f2 != NULL || !s2)){ // both don't have short arg - sort by long
|
||||
return strcmp(l1, l2);
|
||||
}else{ // only one have short arg -- return it
|
||||
if(f2 || !s2) return -1; // a1 have short - it is 'lesser'
|
||||
else return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show help information based on myoption->help values
|
||||
* @param oindex (i) - if non-negative, show only help by myoption[oindex].help
|
||||
* @param options (i) - array of `myoption`
|
||||
*
|
||||
* @exit: run `exit(-1)` !!!
|
||||
*/
|
||||
void showhelp(int oindex, myoption *options){
|
||||
int max_opt_len = 0; // max len of options substring - for right indentation
|
||||
const int bufsz = 255;
|
||||
char buf[bufsz+1];
|
||||
myoption *opts = options;
|
||||
assert(opts);
|
||||
assert(opts[0].name); // check whether there is at least one options
|
||||
if(oindex > -1){ // print only one message
|
||||
opts = &options[oindex];
|
||||
printf(" ");
|
||||
if(!opts->flag && isalpha(opts->val)) printf("-%c, ", opts->val);
|
||||
printf("--%s", opts->name);
|
||||
if(opts->has_arg == 1) printf("=arg");
|
||||
else if(opts->has_arg == 2) printf("[=arg]");
|
||||
printf(" %s\n", _(opts->help));
|
||||
exit(-1);
|
||||
}
|
||||
// header, by default is just "progname\n"
|
||||
printf("\n");
|
||||
if(strstr(helpstring, "%s")) // print progname
|
||||
printf(helpstring, __progname);
|
||||
else // only text
|
||||
printf("%s", helpstring);
|
||||
printf("\n");
|
||||
// count max_opt_len
|
||||
do{
|
||||
int L = strlen(opts->name);
|
||||
if(max_opt_len < L) max_opt_len = L;
|
||||
}while((++opts)->name);
|
||||
max_opt_len += 14; // format: '-S , --long[=arg]' - get addition 13 symbols
|
||||
opts = options;
|
||||
// count amount of options
|
||||
int N; for(N = 0; opts->name; ++N, ++opts);
|
||||
if(N == 0) exit(-2);
|
||||
// Now print all help (sorted)
|
||||
opts = options;
|
||||
qsort(opts, N, sizeof(myoption), argsort);
|
||||
do{
|
||||
int p = sprintf(buf, " "); // a little indent
|
||||
if(!opts->flag && opts->val) // .val is short argument
|
||||
p += snprintf(buf+p, bufsz-p, "-%c, ", opts->val);
|
||||
p += snprintf(buf+p, bufsz-p, "--%s", opts->name);
|
||||
if(opts->has_arg == 1) // required argument
|
||||
p += snprintf(buf+p, bufsz-p, "=arg");
|
||||
else if(opts->has_arg == 2) // optional argument
|
||||
p += snprintf(buf+p, bufsz-p, "[=arg]");
|
||||
assert(p < max_opt_len); // there would be magic if p >= max_opt_len
|
||||
printf("%-*s%s\n", max_opt_len+1, buf, _(opts->help)); // write options & at least 2 spaces after
|
||||
++opts;
|
||||
}while(--N);
|
||||
printf("\n\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* get suboptions from parameter string
|
||||
* @param str - parameter string
|
||||
* @param opt - pointer to suboptions structure
|
||||
* @return TRUE if all OK
|
||||
*/
|
||||
bool get_suboption(char *str, mysuboption *opt){
|
||||
int findsubopt(char *par, mysuboption *so){
|
||||
int idx = 0;
|
||||
if(!par) return -1;
|
||||
while(so[idx].name){
|
||||
if(strcasecmp(par, so[idx].name) == 0) return idx;
|
||||
++idx;
|
||||
}
|
||||
return -1; // badarg
|
||||
}
|
||||
bool opt_setarg(mysuboption *so, int idx, char *val){
|
||||
mysuboption *soptr = &so[idx];
|
||||
bool result = FALSE;
|
||||
void *aptr = soptr->argptr;
|
||||
switch(soptr->type){
|
||||
default:
|
||||
case arg_none:
|
||||
if(soptr->argptr) *((int*)aptr) += 1; // increment value
|
||||
result = TRUE;
|
||||
break;
|
||||
case arg_int:
|
||||
result = myatoll(aptr, val, arg_int);
|
||||
break;
|
||||
case arg_longlong:
|
||||
result = myatoll(aptr, val, arg_longlong);
|
||||
break;
|
||||
case arg_double:
|
||||
result = myatod(aptr, val, arg_double);
|
||||
break;
|
||||
case arg_float:
|
||||
result = myatod(aptr, val, arg_float);
|
||||
break;
|
||||
case arg_string:
|
||||
result = (*((void**)aptr) = (void*)strdup(val));
|
||||
break;
|
||||
case arg_function:
|
||||
result = ((argfn)aptr)(val);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
char *tok;
|
||||
bool ret = FALSE;
|
||||
char *tmpbuf;
|
||||
tok = strtok_r(str, ":,", &tmpbuf);
|
||||
do{
|
||||
char *val = strchr(tok, '=');
|
||||
int noarg = 0;
|
||||
if(val == NULL){ // no args
|
||||
val = "1";
|
||||
noarg = 1;
|
||||
}else{
|
||||
*val++ = '\0';
|
||||
if(!*val || *val == ':' || *val == ','){ // no argument - delimeter after =
|
||||
val = "1"; noarg = 1;
|
||||
}
|
||||
}
|
||||
int idx = findsubopt(tok, opt);
|
||||
if(idx < 0){
|
||||
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÐÁÒÁÍÅÔÒ: %s"
|
||||
WARNX(_("Wrong parameter: %s"), tok);
|
||||
goto returning;
|
||||
}
|
||||
if(noarg && opt[idx].has_arg == NEED_ARG){
|
||||
/// "%s: ÎÅÏÂÈÏÄÉÍ ÁÒÇÕÍÅÎÔ!"
|
||||
WARNX(_("%s: argument needed!"), tok);
|
||||
goto returning;
|
||||
}
|
||||
if(!opt_setarg(opt, idx, val)){
|
||||
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÁÒÇÕÍÅÎÔ \"%s\" ÐÁÒÁÍÅÔÒÁ \"%s\""
|
||||
WARNX(_("Wrong argument \"%s\" of parameter \"%s\""), val, tok);
|
||||
goto returning;
|
||||
}
|
||||
}while((tok = strtok_r(NULL, ":,", &tmpbuf)));
|
||||
ret = TRUE;
|
||||
returning:
|
||||
return ret;
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/* geany_encoding=koi8-r
|
||||
* parseargs.h - headers for parsing command line arguments
|
||||
*
|
||||
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef __PARSEARGS_H__
|
||||
#define __PARSEARGS_H__
|
||||
|
||||
#include <stdbool.h>// bool
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE true
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE false
|
||||
#endif
|
||||
|
||||
// macro for argptr
|
||||
#define APTR(x) ((void*)x)
|
||||
|
||||
// if argptr is a function:
|
||||
typedef bool(*argfn)(void *arg);
|
||||
|
||||
/*
|
||||
* type of getopt's argument
|
||||
* WARNING!
|
||||
* My function change value of flags by pointer, so if you want to use another type
|
||||
* make a latter conversion, example:
|
||||
* char charg;
|
||||
* int iarg;
|
||||
* myoption opts[] = {
|
||||
* {"value", 1, NULL, 'v', arg_int, &iarg, "char val"}, ..., end_option};
|
||||
* ..(parse args)..
|
||||
* charg = (char) iarg;
|
||||
*/
|
||||
typedef enum {
|
||||
arg_none = 0, // no arg
|
||||
arg_int, // integer
|
||||
arg_longlong, // long long
|
||||
arg_double, // double
|
||||
arg_float, // float
|
||||
arg_string, // char *
|
||||
arg_function // parse_args will run function `bool (*fn)(char *optarg, int N)`
|
||||
} argtype;
|
||||
|
||||
/*
|
||||
* Structure for getopt_long & help
|
||||
* BE CAREFUL: .argptr is pointer to data or pointer to function,
|
||||
* conversion depends on .type
|
||||
*
|
||||
* ATTENTION: string `help` prints through macro PRNT(), bu default it is gettext,
|
||||
* but you can redefine it before `#include "parseargs.h"`
|
||||
*
|
||||
* if arg is string, then value wil be strdup'ed like that:
|
||||
* char *str;
|
||||
* myoption opts[] = {{"string", 1, NULL, 's', arg_string, &str, "string val"}, ..., end_option};
|
||||
* *(opts[1].str) = strdup(optarg);
|
||||
* in other cases argptr should be address of some variable (or pointer to allocated memory)
|
||||
*
|
||||
* NON-NULL argptr should be written inside macro APTR(argptr) or directly: (void*)argptr
|
||||
*
|
||||
* !!!LAST VALUE OF ARRAY SHOULD BE `end_option` or ZEROS !!!
|
||||
*
|
||||
*/
|
||||
typedef enum{
|
||||
NO_ARGS = 0, // first three are the same as in getopt_long
|
||||
NEED_ARG = 1,
|
||||
OPT_ARG = 2,
|
||||
MULT_PAR
|
||||
} hasarg;
|
||||
|
||||
typedef struct{
|
||||
// these are from struct option:
|
||||
const char *name; // long option's name
|
||||
hasarg has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg, 4 - need arg & key can repeat (args are stored in null-terminated array)
|
||||
int *flag; // NULL to return val, pointer to int - to set its value of val (function returns 0)
|
||||
int val; // short opt name (if flag == NULL) or flag's value
|
||||
// and these are mine:
|
||||
argtype type; // type of argument
|
||||
void *argptr; // pointer to variable to assign optarg value or function `bool (*fn)(char *optarg, int N)`
|
||||
const char *help; // help string which would be shown in function `showhelp` or NULL
|
||||
} myoption;
|
||||
|
||||
/*
|
||||
* Suboptions structure, almost the same like myoption
|
||||
* used in parse_subopts()
|
||||
*/
|
||||
typedef struct{
|
||||
const char *name;
|
||||
hasarg has_arg;
|
||||
argtype type;
|
||||
void *argptr;
|
||||
} mysuboption;
|
||||
|
||||
// last string of array (all zeros)
|
||||
#define end_option {0,0,0,0,0,0,0}
|
||||
#define end_suboption {0,0,0,0}
|
||||
|
||||
extern const char *__progname;
|
||||
|
||||
void showhelp(int oindex, myoption *options);
|
||||
void parseargs(int *argc, char ***argv, myoption *options);
|
||||
void change_helpstring(char *s);
|
||||
bool get_suboption(char *str, mysuboption *opt);
|
||||
|
||||
#endif // __PARSEARGS_H__
|
||||
186
Daemons/10micron_stellarium/server.c
Normal file
186
Daemons/10micron_stellarium/server.c
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* This file is part of the Snippets project.
|
||||
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <usefull_macros.h>
|
||||
#include <weather_data.h>
|
||||
|
||||
#include "mount.h"
|
||||
#include "server.h"
|
||||
#include "stellarium.h"
|
||||
|
||||
// max age time of last status - 30s
|
||||
#define STATUS_MAX_AGE (30.)
|
||||
|
||||
// commands
|
||||
#define CMD_UNIXT "unixt"
|
||||
#define CMD_STATUS "status"
|
||||
#define CMD_RELAY "relay"
|
||||
#define CMD_OPEN "open"
|
||||
#define CMD_CLOSE "close"
|
||||
#define CMD_STOP "stop"
|
||||
#define CMD_HALF "half"
|
||||
|
||||
// main command socket
|
||||
static sl_sock_t *cmd_socket = NULL;
|
||||
// socket for stellarium purposes
|
||||
static int stellarium_sockfd = -1;
|
||||
// sleep time (us)
|
||||
static unsigned int sleept = DEFAULT_SLEEP_T;
|
||||
// running flag
|
||||
static volatile bool isrunning = false;
|
||||
|
||||
unsigned int server_getsleept(){ return sleept; }
|
||||
bool server_setsleept(unsigned int t){
|
||||
if(t == 0 || t > MAX_SLEEP_T) return false;
|
||||
sleept = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
/////// handlers
|
||||
// unixt - send to ALL clients
|
||||
static sl_sock_hresult_e dtimeh(sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
char buf[32];
|
||||
snprintf(buf, 31, "%s=%.2f\n", item->key, sl_dtime());
|
||||
LOGDBG("Client %d asks time: %s", c->fd, buf);
|
||||
sl_sock_sendstrmessage(c, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
// statust - text format status
|
||||
static sl_sock_hresult_e status(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req){
|
||||
char buf[BUFSIZ];
|
||||
snprintf(buf, BUFSIZ-1, "%s=%s\n", item->key, mount_status_str());
|
||||
LOGDBG("Client %d asks status: %s", c->fd, buf);
|
||||
sl_sock_sendstrmessage(c, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
// and all handlers collection
|
||||
static sl_sock_hitem_t handlers[] = {
|
||||
{dtimeh, CMD_UNIXT, "get server's UNIX time", NULL},
|
||||
{status, CMD_STATUS, "get mount status", NULL},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
// Too much clients handler
|
||||
static void toomuch(int fd){
|
||||
const char m[] = "Try later: too much clients connected\n";
|
||||
send(fd, m, sizeof(m)-1, MSG_NOSIGNAL);
|
||||
shutdown(fd, SHUT_WR);
|
||||
DBG("shutdown, wait");
|
||||
double t0 = sl_dtime();
|
||||
uint8_t buf[8];
|
||||
while(sl_dtime() - t0 < 11.){
|
||||
if(sl_canread(fd)){
|
||||
ssize_t got = read(fd, buf, 8);
|
||||
DBG("Got=%zd", got);
|
||||
if(got < 1) break;
|
||||
}
|
||||
}
|
||||
DBG("Disc after %gs", sl_dtime() - t0);
|
||||
LOGWARN("Client fd=%d tried to connect after MAX reached", fd);
|
||||
}
|
||||
// new connections handler: can check IP and reject client by returning FALSE
|
||||
static int connected(sl_sock_t *c){
|
||||
if(c->type == SOCKT_UNIX) LOGMSG("New client fd=%d connected", c->fd);
|
||||
else LOGMSG("New client fd=%d, IP=%s connected", c->fd, c->IP);
|
||||
return TRUE;
|
||||
}
|
||||
// disconnected handler
|
||||
static void disconnected(sl_sock_t *c){
|
||||
if(c->type == SOCKT_UNIX) LOGMSG("Disconnected client fd=%d", c->fd);
|
||||
else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP);
|
||||
}
|
||||
|
||||
bool server_check(server_sock_t *sockt){
|
||||
sl_socktype_e type = (sockt->cmd_isunix) ? SOCKT_UNIX : SOCKT_NETLOCAL;
|
||||
cmd_socket = sl_sock_run_server(type, sockt->cmdnode, BUFSIZ, handlers);
|
||||
if(!cmd_socket){
|
||||
LOGERR("Can't start main server");
|
||||
return false;
|
||||
}
|
||||
LOGMSG("Main server started: %s", sockt->cmdnode);
|
||||
sl_sock_changemaxclients(cmd_socket, sockt->maxclients);
|
||||
sl_sock_maxclhandler(cmd_socket, toomuch);
|
||||
sl_sock_connhandler(cmd_socket, connected);
|
||||
sl_sock_dischandler(cmd_socket, disconnected);
|
||||
stellarium_sockfd = sl_sock_open(SOCKT_NET, sockt->stellport, 1, 0);
|
||||
if(stellarium_sockfd < 0){
|
||||
LOGERR("Can't start stellarium socket");
|
||||
sl_sock_delete(&cmd_socket);
|
||||
return false;
|
||||
}
|
||||
if(listen(stellarium_sockfd, sockt->maxclients) == -1){
|
||||
WARN("listen() for stellarium socket");
|
||||
LOGERR("Can't run listen() for stellarium socket");
|
||||
sl_sock_delete(&cmd_socket);
|
||||
close(stellarium_sockfd);
|
||||
return false;
|
||||
}
|
||||
DBG("stellarium_sockfd=%d", stellarium_sockfd);
|
||||
LOGMSG("Prepared stellarium socket: %s", sockt->stellport);
|
||||
DBG("Prepared stellarium socket: %s", sockt->stellport);
|
||||
return true;
|
||||
}
|
||||
|
||||
void server_run(){
|
||||
if(isrunning){
|
||||
LOGERR("server_run(): still running!");
|
||||
return;
|
||||
}
|
||||
if(stellarium_sockfd == -1 || !cmd_socket){
|
||||
LOGERR("server_run(): not initialized");
|
||||
if(cmd_socket) sl_sock_delete(&cmd_socket);
|
||||
ERRX("server_run(): not initialized");
|
||||
}
|
||||
if(!stellarium_start(stellarium_sockfd)){
|
||||
LOGERR("Can't start stellarium server");
|
||||
return;
|
||||
}
|
||||
if(!mount_connect()){
|
||||
LOGERR("Can't connect to mount");
|
||||
sl_sock_delete(&cmd_socket);
|
||||
return;
|
||||
}
|
||||
isrunning = true;
|
||||
DBG("While");
|
||||
while(isrunning && cmd_socket && cmd_socket->connected){
|
||||
usleep(sleept);
|
||||
if(!cmd_socket->rthread){
|
||||
LOGERR("Server handlers thread is dead");
|
||||
break;
|
||||
}
|
||||
// finite state machine polling
|
||||
//dome_poll(DOME_POLL, 0);
|
||||
}
|
||||
DBG("Stop command socket");
|
||||
sl_sock_delete(&cmd_socket);
|
||||
WARNX("Server is dead");
|
||||
LOGERR("Server is dead");
|
||||
isrunning = false;
|
||||
}
|
||||
|
||||
void server_stop(){
|
||||
isrunning = false;
|
||||
}
|
||||
39
Daemons/10micron_stellarium/server.h
Normal file
39
Daemons/10micron_stellarium/server.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of the mountdaemon_10micron project.
|
||||
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <usefull_macros.h>
|
||||
|
||||
// default and max available time for "usleep"
|
||||
#define DEFAULT_SLEEP_T 100
|
||||
#define MAX_SLEEP_T 10000
|
||||
#define DEFAULT_MAXCLIENTS 5
|
||||
|
||||
typedef struct{
|
||||
int cmd_isunix; // UNIX-socket instead of INET for `cmdnode`
|
||||
const char *stellport; // port of stellarium server; could be "localhost:port" for local-only work
|
||||
const char *cmdnode; // node of command socket
|
||||
int maxclients; // maximal amount of clients connected
|
||||
} server_sock_t;
|
||||
|
||||
bool server_check(server_sock_t *sockt);
|
||||
void server_run();
|
||||
void server_stop();
|
||||
unsigned int server_getsleept();
|
||||
bool server_setsleept(unsigned int t);
|
||||
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
* This file is part of the StelD project.
|
||||
* Copyright 2021 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "cmdlnopts.h"
|
||||
#include "socket.h"
|
||||
#include "usefull_macro.h"
|
||||
|
||||
// max time to wait answer from server
|
||||
#define WAITANSTIME (1.0)
|
||||
|
||||
static int sockfd = -1; // server file descriptor
|
||||
static pthread_t sock_thread;
|
||||
static char buf[BUFSIZ]; // buffer for messages
|
||||
static int Nread; // amount of bytes in buf
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void *getmessages(_U_ void *par);
|
||||
|
||||
/**
|
||||
* @brief weatherserver_connect - connect to a weather server
|
||||
* @return FALSE if failed
|
||||
*/
|
||||
int weatherserver_connect(){
|
||||
if(sockfd > 0) return TRUE;
|
||||
DBG("connect to %s:%d", GP->weathserver, GP->weathport);
|
||||
char port[10];
|
||||
snprintf(port, 10, "%d", GP->weathport);
|
||||
struct addrinfo hints = {0}, *res, *p;
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
if(getaddrinfo(GP->weathserver, port, &hints, &res) != 0){
|
||||
WARN("getaddrinfo()");
|
||||
return FALSE;
|
||||
}
|
||||
// loop through all the results and connect to the first we can
|
||||
for(p = res; p; p = p->ai_next){
|
||||
if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
|
||||
WARN("socket");
|
||||
continue;
|
||||
}
|
||||
if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1){
|
||||
WARN("connect()");
|
||||
close(sockfd);
|
||||
continue;
|
||||
}
|
||||
break; // if we get here, we have a successfull connection
|
||||
}
|
||||
if(!p){
|
||||
WARNX("Can't connect to socket");
|
||||
sockfd = -1;
|
||||
return FALSE;
|
||||
}
|
||||
freeaddrinfo(res);
|
||||
if(pthread_create(&sock_thread, NULL, getmessages, NULL)){
|
||||
WARN("pthread_create()");
|
||||
weatherserver_disconnect();
|
||||
return FALSE;
|
||||
}
|
||||
DBG("connected, fd=%d", sockfd);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void weatherserver_disconnect(){
|
||||
if(sockfd > -1){
|
||||
pthread_kill(sock_thread, 9);
|
||||
pthread_join(sock_thread, NULL);
|
||||
close(sockfd);
|
||||
}
|
||||
sockfd = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief getparval - return value of parameter
|
||||
* @param par (i) - parameter value
|
||||
* @param ansbuf (i) - buffer with server answer
|
||||
* @param val (o) - value of parameter
|
||||
* @return TRUE if parameter found and set `val` to its value
|
||||
*/
|
||||
int getparval(const char *par, const char *ansbuf, double *val){
|
||||
if(!par || !ansbuf) return FALSE;
|
||||
int ret = FALSE;
|
||||
char *b = strdup(ansbuf);
|
||||
char *parval = NULL, *token = strtok(b, "\n");
|
||||
int l = strlen(par);
|
||||
if(!token) goto rtn;
|
||||
while(token){
|
||||
if(strncmp(token, par, l) == 0){ // found
|
||||
//DBG("token: '%s'", token);
|
||||
parval = strchr(token, '=');
|
||||
if(!parval) goto rtn;
|
||||
++parval; while(*parval == ' ' || *parval == '\t') ++parval;
|
||||
//DBG("parval: '%s'", parval);
|
||||
ret = TRUE;
|
||||
break;
|
||||
}
|
||||
token = strtok(NULL, "\n");
|
||||
}
|
||||
if(parval && val){
|
||||
*val = atof(parval);
|
||||
//DBG("Set %s to %g", par, *val);
|
||||
}
|
||||
rtn:
|
||||
FREE(b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* wait for answer from socket
|
||||
* @return FALSE in case of error or timeout, TRUE if socket is ready
|
||||
*/
|
||||
static int canread(){
|
||||
if(sockfd < 0) return FALSE;
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
int rc;
|
||||
// wait not more than 10ms
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 10000;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sockfd, &fds);
|
||||
do{
|
||||
rc = select(sockfd+1, &fds, NULL, NULL, &timeout);
|
||||
if(rc < 0){
|
||||
if(errno != EINTR){
|
||||
WARN("select()");
|
||||
return FALSE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}while(1);
|
||||
if(FD_ISSET(sockfd, &fds)) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief getmessages - continuosly read data from server and fill buffer
|
||||
*/
|
||||
static void *getmessages(_U_ void *par){
|
||||
write(sockfd, "get\n", 4);
|
||||
while(sockfd > 0){
|
||||
pthread_mutex_lock(&mutex);
|
||||
if(Nread == 0){
|
||||
double t0 = dtime();
|
||||
while(dtime() - t0 < WAITANSTIME && Nread < BUFSIZ){
|
||||
if(!canread()) continue;
|
||||
int n = read(sockfd, buf+Nread, BUFSIZ-Nread);
|
||||
if(n == 0) break;
|
||||
if(n < 0){
|
||||
close(sockfd);
|
||||
sockfd = -1;
|
||||
return NULL;
|
||||
}
|
||||
Nread += n;
|
||||
}
|
||||
if(Nread){
|
||||
buf[Nread] = 0;
|
||||
//DBG("got %d: %s", Nread, buf);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
if(Nread == 0){
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief getweathbuffer - read whole buffer with data and set Nread to zero
|
||||
* @return NULL if no data or buffer (allocated here)
|
||||
*/
|
||||
char *getweathbuffer(){
|
||||
if(!weatherserver_connect()) return NULL; // not connected & can't connect
|
||||
char *ret = NULL;
|
||||
pthread_mutex_lock(&mutex);
|
||||
if(Nread){
|
||||
ret = strdup(buf);
|
||||
Nread = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return ret;
|
||||
}
|
||||
284
Daemons/10micron_stellarium/stellarium.c
Normal file
284
Daemons/10micron_stellarium/stellarium.c
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* This file is part of the mountdaemon_10micron project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// base functions for sending/receiving data in stellarium protocol
|
||||
|
||||
#include <stdint.h>
|
||||
#include <usefull_macros.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "angles.h"
|
||||
#include "mount.h"
|
||||
#include "stellarium.h"
|
||||
|
||||
#define BUFLEN 256
|
||||
|
||||
// running flag
|
||||
static volatile bool isrunning = false;
|
||||
static pthread_t mainthread;
|
||||
|
||||
//read: 0x14 0x0 0x0 0x0 0x5b 0x5a 0x2e 0xc6 0x8c 0x23 0x5 0x0 0x23 0x9 0xe5 0xaf 0x23 0x2e 0x34 0xed
|
||||
// command: goto 16h29 24.45 -26d25 55.62
|
||||
/*
|
||||
LITTLE-ENDIAN!!!
|
||||
from client:
|
||||
LENGTH (2 bytes, integer): length of the message
|
||||
TYPE (2 bytes, integer): 0
|
||||
TIME (8 bytes, integer): current time on the server computer in microseconds
|
||||
since 1970.01.01 UT. Currently unused.
|
||||
RA (4 bytes, unsigned integer): right ascension of the telescope (J2000)
|
||||
a value of 0x100000000 = 0x0 means 24h=0h,
|
||||
a value of 0x80000000 means 12h
|
||||
DEC (4 bytes, signed integer): declination of the telescope (J2000)
|
||||
a value of -0x40000000 means -90degrees,
|
||||
a value of 0x0 means 0degrees,
|
||||
a value of 0x40000000 means 90degrees
|
||||
|
||||
to client:
|
||||
LENGTH (2 bytes, integer): length of the message
|
||||
TYPE (2 bytes, integer): 0
|
||||
TIME (8 bytes, integer): current time on the server computer in microseconds
|
||||
since 1970.01.01 UT. Currently unused.
|
||||
RA (4 bytes, unsigned integer): right ascension of the telescope (J2000)
|
||||
a value of 0x100000000 = 0x0 means 24h=0h,
|
||||
a value of 0x80000000 means 12h
|
||||
DEC (4 bytes, signed integer): declination of the telescope (J2000)
|
||||
a value of -0x40000000 means -90degrees,
|
||||
a value of 0x0 means 0degrees,
|
||||
a value of 0x40000000 means 90degrees
|
||||
STATUS (4 bytes, signed integer): status of the telescope, currently unused.
|
||||
status=0 means ok, status<0 means some error
|
||||
*/
|
||||
|
||||
|
||||
#define DEG2DEC(degr) ((int32_t)(degr / 90. * ((double)0x40000000)))
|
||||
#define DEG2RA(degr) ((uint32_t)(degr / 180. * ((double)0x80000000)))
|
||||
#define HRS2RA(degr) ((uint32_t)(degr / 12. * ((double)0x80000000)))
|
||||
#define DEC2DEG(i32) (((double)i32)*90./((double)0x40000000))
|
||||
#define RA2DEG(u32) (((double)u32)*180. /((double)0x80000000))
|
||||
#define RA2HRS(u32) (((double)u32)*12. /((double)0x80000000))
|
||||
|
||||
typedef struct __attribute__((__packed__)){
|
||||
uint16_t len;
|
||||
uint16_t type;
|
||||
uint64_t time;
|
||||
uint32_t ra;
|
||||
int32_t dec;
|
||||
} indata;
|
||||
|
||||
typedef struct __attribute__((__packed__)){
|
||||
uint16_t len;
|
||||
uint16_t type;
|
||||
uint64_t time;
|
||||
uint32_t ra;
|
||||
int32_t dec;
|
||||
int32_t status;
|
||||
} outdata;
|
||||
|
||||
/**
|
||||
* @brief proc_data - process data received from Stellarium
|
||||
* @param data - raw data
|
||||
* @param len - its length
|
||||
* @return true if all OK
|
||||
*/
|
||||
static bool proc_data(uint8_t *data, ssize_t len){
|
||||
FNAME();
|
||||
if(len != sizeof(indata)){
|
||||
WARNX("Bad data size: got %zd instead of %zd!", len, sizeof(indata));
|
||||
return false;
|
||||
}
|
||||
indata *dat = (indata*)data;
|
||||
uint16_t L, T;
|
||||
uint32_t ra;
|
||||
int32_t dec;
|
||||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
||||
L = le16toh(dat->len); T = le16toh(dat->type);
|
||||
ra = le32toh(dat->ra);
|
||||
dec = (int32_t)le32toh((uint32_t)dat->dec);
|
||||
#else
|
||||
L = dat->len; T = dat->type;
|
||||
ra = dat->ra; dec = dat->dec;
|
||||
#endif
|
||||
DBG("got message with len %u & type %u", L, T);
|
||||
if(L != len){
|
||||
WARNX("Length of message != msg->len");
|
||||
return false;
|
||||
}
|
||||
if(T){
|
||||
WARNX("Wrong message type");
|
||||
return false;
|
||||
}
|
||||
// convert RA/DEC to degrees
|
||||
double tagRA = RA2DEG(ra), tagDec = DEC2DEG(dec);
|
||||
DBG("RA: %u (%g degr), DEC: %d (%g degr)", ra, tagRA, dec, tagDec);
|
||||
LOGMSG("(stellarium) RA: %u (%g degr), DEC: %d (%g degr)", ra, tagRA, dec, tagDec);
|
||||
// check RA/DEC
|
||||
horizCrds_t hnow; // without refraction
|
||||
polarCrds_t p2000, pnow;
|
||||
p2000.ra = DEG2RAD(tagRA);
|
||||
p2000.dec = DEG2RAD(tagDec);
|
||||
// now J2000 obs Jnow
|
||||
if(!get_ObsPlace(NULL, &p2000, &pnow, &hnow)){
|
||||
WARNX("Can't convert coordinates to Jnow");
|
||||
return false;
|
||||
}
|
||||
tagRA = RAD2DEG(pnow.ra - pnow.eo);
|
||||
tagDec = RAD2DEG(pnow.dec);
|
||||
return (mount_setInpRA(tagRA) && mount_setInpDec(tagDec));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to user
|
||||
* @param data - data to send
|
||||
* @param dlen - data length
|
||||
* @param sockfd - socket fd for sending data
|
||||
* @return false if client disconnected
|
||||
*/
|
||||
float send_data(uint8_t *data, size_t dlen, int sockfd){
|
||||
ssize_t sent = send(sockfd, data, dlen, MSG_NOSIGNAL);
|
||||
if(sent != (ssize_t)dlen){
|
||||
if(sent == -1 && errno != EINTR){
|
||||
WARN("Disconnected?");
|
||||
return false;
|
||||
}
|
||||
WARN("write()");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* main socket service procedure
|
||||
*/
|
||||
static void *handle_socket(void *sockd){
|
||||
FNAME();
|
||||
if(!isrunning) return NULL;
|
||||
outdata dout;
|
||||
int sock = *(int*)sockd;
|
||||
dout.len = htole16(sizeof(outdata));
|
||||
dout.type = 0;
|
||||
while(isrunning){
|
||||
// get coordinates
|
||||
double RA = 0., Decl = 0.;
|
||||
if((dout.status = mount_getcoords(&RA, &Decl)) == MNT_S_ERROR){
|
||||
WARNX("Error: can't get coordinates");
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
//DBG("got : %g/%g", RA, Decl);
|
||||
dout.ra = htole32(HRS2RA(RA));
|
||||
dout.dec = (int32_t)htole32(DEG2DEC(Decl));
|
||||
if(!send_data((uint8_t*)&dout, sizeof(outdata), sock)) break;
|
||||
//DBG("sent ra = %g, dec = %g", RA2HRS(dout.ra), DEC2DEG(dout.dec));
|
||||
if(!sl_canread(sock)) continue;
|
||||
// fill incoming buffer
|
||||
uint8_t buff[BUFLEN];
|
||||
ssize_t rd = read(sock, buff, BUFLEN-1);
|
||||
if(rd <= 0){ // error or disconnect
|
||||
DBG("Nothing to read from fd %d (ret: %zd)", sock, rd);
|
||||
WARNX("Client disconnected?");
|
||||
break;
|
||||
}
|
||||
buff[rd] = 0;
|
||||
DBG("read %zd (%s)", rd, buff);
|
||||
if(!proc_data(buff, rd)) dout.status = -1;
|
||||
else dout.status = 0;
|
||||
}
|
||||
close(sock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Main loop thread: wait connections over socket and create one thread for each
|
||||
static void* start(void *F){
|
||||
if(isrunning){
|
||||
WARNX("already running");
|
||||
return NULL;
|
||||
}
|
||||
if(!F){
|
||||
WARNX("start(): No arg");
|
||||
return NULL;
|
||||
}
|
||||
int sockfd = *((int*)F);
|
||||
if(sockfd < 0){
|
||||
WARNX("start(): sockfd=%d", sockfd);
|
||||
return NULL;
|
||||
}
|
||||
DBG("Start main loop, sockfd=%d", sockfd);
|
||||
isrunning = true;
|
||||
// Main loop
|
||||
while(isrunning){
|
||||
socklen_t size = sizeof(struct sockaddr_in);
|
||||
struct sockaddr_in myaddr;
|
||||
int newsock;
|
||||
newsock = accept(sockfd, (struct sockaddr*)&myaddr, &size);
|
||||
if(newsock <= 0){
|
||||
if(errno == EAGAIN) continue; // nothing available
|
||||
WARN("accept()");
|
||||
LOGWARN("Stellarium socket error in accept()");
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
struct sockaddr_in peer;
|
||||
socklen_t peer_len = sizeof(peer);
|
||||
if(getpeername(newsock, (struct sockaddr*)&peer, &peer_len) == -1){
|
||||
LOGWARN("Stellarium socket error in getpeername()");
|
||||
WARN("getpeername()");
|
||||
close(newsock);
|
||||
continue;
|
||||
}
|
||||
int sockport = -1;
|
||||
if(getsockname(newsock, (struct sockaddr*)&peer, &peer_len) == 0){
|
||||
sockport = ntohs(peer.sin_port);
|
||||
}
|
||||
char *peerIP = inet_ntoa(peer.sin_addr);
|
||||
LOGMSG("Got connection from %s @ %d", peerIP, sockport);
|
||||
DBG("Peer's IP address is: %s (@port %d)\n", peerIP, sockport);
|
||||
/*if(strcmp(peerIP, ACCEPT_IP) && strcmp(peerIP, "127.0.0.1")){
|
||||
WARNX("Wrong IP");
|
||||
close(newsock);
|
||||
continue;
|
||||
}*/
|
||||
pthread_t rthrd;
|
||||
if(pthread_create(&rthrd, NULL, handle_socket, (void*)&newsock)){
|
||||
WARN("Can't create socket thread");
|
||||
LOGERR("Stellarium socket: error creating listen thread");
|
||||
}else{
|
||||
DBG("Thread created, detouch");
|
||||
pthread_detach(rthrd); // don't care about thread state
|
||||
}
|
||||
}
|
||||
close(sockfd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool stellarium_start(int sockfd){
|
||||
int fd = sockfd;
|
||||
if(pthread_create(&mainthread, NULL, start, (void*)&fd)){
|
||||
WARN("pthread_create()");
|
||||
return false;
|
||||
}
|
||||
DBG("Stellarium server started");
|
||||
return true;
|
||||
}
|
||||
|
||||
void stellarium_stop(){
|
||||
if(!isrunning) return;
|
||||
isrunning = false;
|
||||
pthread_join(mainthread, NULL);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* This file is part of the StelD project.
|
||||
* Copyright 2021 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
* This file is part of the mountdaemon_10micron project.
|
||||
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* 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
|
||||
@@ -17,12 +17,6 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef SOCKET_H__
|
||||
#define SOCKET_H__
|
||||
|
||||
void weatherserver_disconnect();
|
||||
int weatherserver_connect();
|
||||
int getparval(const char *par, const char *ansbuf, double *val);
|
||||
char *getweathbuffer();
|
||||
|
||||
#endif // SOCKET_H__
|
||||
bool stellarium_start(int sockfd);
|
||||
void stellarium_stop();
|
||||
@@ -1,670 +0,0 @@
|
||||
/*
|
||||
* geany_encoding=koi8-r
|
||||
* telescope.c
|
||||
*
|
||||
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
#include <arpa/inet.h> // ntoa
|
||||
#include <netinet/in.h> // ntoa
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h> // getpeername
|
||||
|
||||
#include "libsofa.h"
|
||||
#include "main.h" // global_quit
|
||||
#include "telescope.h"
|
||||
#include "usefull_macro.h"
|
||||
|
||||
// polling timeout for answer from mount
|
||||
#ifndef T_POLLING_TMOUT
|
||||
#define T_POLLING_TMOUT (0.5)
|
||||
#endif
|
||||
// wait for '\n' after last data read
|
||||
#ifndef WAIT_TMOUT
|
||||
#define WAIT_TMOUT (0.01)
|
||||
#endif
|
||||
|
||||
#define BUFLEN 80
|
||||
|
||||
static char *hdname = NULL;
|
||||
static double ptRAdeg, ptDECdeg; // target RA/DEC J2000
|
||||
static int Target = 0; // target coordinates entered
|
||||
|
||||
static double r = 0., d = 0.; // RA/DEC from wrhdr
|
||||
static int mountstatus = 0; // return of :Gstat#
|
||||
static time_t tlast = 0; // last time coordinates were refreshed
|
||||
|
||||
static int pause_communication = 0; // ==1 to prevent writing to port outside of terminal thread
|
||||
|
||||
/**
|
||||
* read strings from terminal (ending with '\n') with timeout
|
||||
* @return NULL if nothing was read or pointer to static buffer
|
||||
* THREAD UNSAFE!
|
||||
*/
|
||||
static char *read_string(){
|
||||
static char buf[BUFLEN];
|
||||
size_t r = 0, l;
|
||||
int LL = BUFLEN - 1;
|
||||
char *ptr = NULL;
|
||||
static char *optr = NULL;
|
||||
if(optr && *optr){
|
||||
ptr = optr;
|
||||
optr = strchr(optr, '\n');
|
||||
if(optr) ++optr;
|
||||
return ptr;
|
||||
}
|
||||
ptr = buf;
|
||||
double d0 = dtime();
|
||||
do{
|
||||
if((l = read_tty(ptr, LL))){
|
||||
r += l; LL -= l; ptr += l;
|
||||
if(ptr[-1] == '\n') break;
|
||||
d0 = dtime();
|
||||
}
|
||||
}while(dtime() - d0 < WAIT_TMOUT && LL);
|
||||
if(r){
|
||||
buf[r] = 0;
|
||||
optr = strchr(buf, '\n');
|
||||
if(optr) ++optr;
|
||||
return buf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* write command, thread-safe
|
||||
* @param cmd (i) - command to write
|
||||
* @param buff (o) - buffer (WHICH SIZE = BUFLEN!!!) to which write data (or NULL if don't need)
|
||||
* @return answer or NULL if error occured (or no answer)
|
||||
* WARNING!!! data returned is allocated by strdup! You MUST free it when don't need
|
||||
*/
|
||||
static char *write_cmd(const char *cmd, char *buff){
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_lock(&mutex);
|
||||
//DBG("Write %s", cmd);
|
||||
if(write_tty(cmd, strlen(cmd))) return NULL;
|
||||
double t0 = dtime();
|
||||
char *ans;
|
||||
while(dtime() - t0 < T_POLLING_TMOUT){ // read answer
|
||||
if((ans = read_string())){ // parse new data
|
||||
//DBG("got answer: %s", ans);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
if(!buff) return NULL;
|
||||
strncpy(buff, ans, BUFLEN-1);
|
||||
return buff;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// write to telescope mount corrections: datetime, pressure and temperature
|
||||
// @return 1 if time and weather was corrected
|
||||
static int makecorr(){
|
||||
if(pause_communication) return 0;
|
||||
int ret = 1;
|
||||
// write current date&time
|
||||
char buf[64], ibuff[BUFLEN], *ans;
|
||||
DBG("curtime: %s", write_cmd(":GUDT#", ibuff));
|
||||
ans = write_cmd(":Gstat#", ibuff);
|
||||
if(ans){
|
||||
mountstatus = atoi(ans);
|
||||
// if system is in tracking or unknown state - don't update data!
|
||||
if(mountstatus == TEL_SLEWING || mountstatus == TEL_TRACKING) return 0;
|
||||
}
|
||||
/*
|
||||
* there's no GPS on this mount and there's no need for it!
|
||||
write_cmd(":gT#", NULL); // correct time by GPS
|
||||
ans = write_cmd(":gtg#", ibuff);
|
||||
*/
|
||||
WARNX("Refresh datetime");
|
||||
time_t t = time(NULL);
|
||||
struct tm *stm = localtime(&t);
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
snprintf(buf, 64, ":SLDT%04d-%02d-%02d,%02d:%02d:%02d.%02ld#", 1900+stm->tm_year, stm->tm_mon+1, stm->tm_mday,
|
||||
stm->tm_hour, stm->tm_min, stm->tm_sec, tv.tv_usec/10000);
|
||||
DBG("write: %s", buf);
|
||||
ans = write_cmd(buf, ibuff);
|
||||
if(!ans || *ans != '1'){
|
||||
WARNX("Can't write current date/time");
|
||||
putlog("Can't set system time");
|
||||
ret = 0;
|
||||
}else{
|
||||
putlog("Set system time by command %s", buf);
|
||||
}
|
||||
DBG("curtime: %s", write_cmd(":GUDT#", ibuff));
|
||||
localWeather *w = getWeath();
|
||||
if(!w){
|
||||
ret = 0;
|
||||
putlog("Can't determine weather data");
|
||||
}else{ // set refraction model data
|
||||
snprintf(buf, 64, ":SRPRS%.1f#", w->pres*1013./760.);
|
||||
ans = write_cmd(buf, ibuff);
|
||||
if(!ans || *ans != '1'){
|
||||
ret = 0;
|
||||
putlog("Can't set pressure data of refraction model");
|
||||
}else putlog("Correct pressure to %gmmHg", w->pres);
|
||||
snprintf(buf, 64, ":SRTMP%.1f#", w->tc);
|
||||
ans = write_cmd(buf, ibuff);
|
||||
if(!ans || *ans != '1'){
|
||||
ret = 0;
|
||||
putlog("Can't set temperature data of refraction model");
|
||||
}else putlog("Correct temperature to %g", w->tc);
|
||||
}
|
||||
sprintf(buf, ":SREF1#"); // turn on refraction correction
|
||||
write_cmd(buf, ibuff);
|
||||
sprintf(buf, ":Sdat1#"); // turn on dual-axis tracking
|
||||
write_cmd(buf, ibuff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int chkconn(){
|
||||
char tmpbuf[4096];
|
||||
read_tty(tmpbuf, 4096); // clear rbuf
|
||||
write_cmd("#", NULL); // clear cmd buffer
|
||||
if(!write_cmd(":SB0#", tmpbuf)) return 0; // 115200
|
||||
//if(!write_cmd(":GR#")) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* connect telescope device
|
||||
* @param dev (i) - device name to connect
|
||||
* @param hdrname (i) - output file with FITS-headers
|
||||
* @return 1 if all OK
|
||||
*/
|
||||
int connect_telescope(char *dev, char *hdrname){
|
||||
if(!dev) return 0;
|
||||
tcflag_t spds[] = {B9600, B115200, B57600, B38400, B19200, B4800, B2400, B1200, 0}, *speeds = spds;
|
||||
DBG("Connection to device %s...", dev);
|
||||
while(*speeds){
|
||||
DBG("Try %d", *speeds);
|
||||
tty_init(dev, *speeds);
|
||||
if(chkconn()) break;
|
||||
++speeds;
|
||||
}
|
||||
if(!*speeds) return 0;
|
||||
if(*speeds != B115200){
|
||||
restore_tty();
|
||||
tty_init(dev, B115200);
|
||||
if(!chkconn()) return 0;
|
||||
}
|
||||
write_cmd("#", NULL); // clear previous buffer
|
||||
write_cmd(":STOP#", NULL); // stop tracking after poweron
|
||||
write_cmd(":U2#", NULL); // set high precision
|
||||
write_cmd(":So10#", NULL); // set minimum altitude to 10 degrees
|
||||
putlog("Connected to %s@115200, will write FITS-header into %s", dev, hdrname);
|
||||
FREE(hdname);
|
||||
hdname = strdup(hdrname);
|
||||
DBG("connected");
|
||||
Target = 0;
|
||||
getWeath(); getPlace(); getDUT(); // determine starting values
|
||||
//write_cmd(":gT#", NULL); // correct time by GPS
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
:MS# - move to target, return: 0 if all OK or text with error
|
||||
:SrHH:MM:SS.SS# - set target RA (return 1 if all OK)
|
||||
:SdsDD*MM:SS.S# - set target DECL (return 1 if all OK)
|
||||
*/
|
||||
/**
|
||||
* send coordinates to telescope
|
||||
* @param ra - right ascention (hours), Jnow without refraction
|
||||
* @param dec - declination (degrees), Jnow without refraction
|
||||
* @return 1 if all OK
|
||||
*/
|
||||
int point_telescope(double ra, double dec){
|
||||
if(pause_communication){
|
||||
putlog("Can't point telescope in paused mode");
|
||||
return 0;
|
||||
}
|
||||
DBG("try to send ra=%g, decl=%g", ra, dec);
|
||||
ptRAdeg = ra * 15.;
|
||||
ptDECdeg = dec;
|
||||
Target = 0;
|
||||
int err = 0;
|
||||
char buf[80], ibuff[BUFLEN];
|
||||
char sign = '+';
|
||||
if(dec < 0){
|
||||
sign = '-';
|
||||
dec = -dec;
|
||||
}
|
||||
|
||||
int h = (int)ra;
|
||||
ra -= h; ra *= 60.;
|
||||
int m = (int)ra;
|
||||
ra -= m; ra *= 60.;
|
||||
|
||||
int d = (int) dec;
|
||||
dec -= d; dec *= 60.;
|
||||
int dm = (int)dec;
|
||||
dec -= dm; dec *= 60.;
|
||||
snprintf(buf, 80, ":Sr%d:%d:%.2f#", h,m,ra);
|
||||
char *ans = write_cmd(buf, ibuff);
|
||||
if(!ans || *ans != '1'){
|
||||
err = 1;
|
||||
goto ret;
|
||||
}
|
||||
snprintf(buf, 80, ":Sd%c%d:%d:%.1f#", sign,d,dm,dec);
|
||||
ans = write_cmd(buf, ibuff);
|
||||
if(!ans || *ans != '1'){
|
||||
err = 2;
|
||||
goto ret;
|
||||
}
|
||||
DBG("Move");
|
||||
ans = write_cmd(":MS#", ibuff);
|
||||
if(!ans || *ans != '0'){
|
||||
putlog("move error, answer: %s", ans);
|
||||
err = 3;
|
||||
goto ret;
|
||||
}
|
||||
ret:
|
||||
if(err){
|
||||
putlog("error sending coordinates (err = %d: RA/DEC/MOVE)!", err);
|
||||
return 0;
|
||||
}else{
|
||||
Target = 1;
|
||||
putlog("Send ra=%g degr, dec=%g degr", ptRAdeg, ptDECdeg);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert str into RA/DEC coordinate
|
||||
* @param str (i) - string with angle
|
||||
* @param val (o) - output angle value
|
||||
* @return 1 if all OK
|
||||
*/
|
||||
static int str2coord(char *str, double *val){
|
||||
if(!str || !val) return 0;
|
||||
int d, m;
|
||||
float s;
|
||||
int sign = 1;
|
||||
if(*str == '+') ++str;
|
||||
else if(*str == '-'){
|
||||
sign = -1;
|
||||
++str;
|
||||
}
|
||||
int n = sscanf(str, "%d:%d:%f#", &d, &m, &s);
|
||||
if(n != 3) return 0;
|
||||
double ang = d + ((double)m)/60. + s/3600.;
|
||||
if(sign == -1) *val = -ang;
|
||||
else *val = ang;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief printhdr - write FITS record into output file
|
||||
* @param fd - fd to write
|
||||
* @param key - key
|
||||
* @param val - value
|
||||
* @param cmnt - comment
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
static int printhdr(int fd, const char *key, const char *val, const char *cmnt){
|
||||
char tmp[81];
|
||||
char tk[9];
|
||||
if(strlen(key) > 8){
|
||||
snprintf(tk, 9, "%s", key);
|
||||
key = tk;
|
||||
}
|
||||
if(cmnt){
|
||||
snprintf(tmp, 81, "%-8s= %-21s / %s", key, val, cmnt);
|
||||
}else{
|
||||
snprintf(tmp, 81, "%-8s= %s", key, val);
|
||||
}
|
||||
size_t l = strlen(tmp);
|
||||
tmp[l] = '\n';
|
||||
++l;
|
||||
if(write(fd, tmp, l) != (ssize_t)l){
|
||||
WARN("write()");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get coordinates
|
||||
* @param ra (o) - right ascension (hours)
|
||||
* @param decl (o) - declination (degrees)
|
||||
* @return telescope status or -1 if coordinates are too old
|
||||
*/
|
||||
int get_telescope_coords(double *ra, double *decl){
|
||||
if(!tlast) tlast = time(NULL);
|
||||
if(time(NULL) - tlast > COORDS_TOO_OLD_TIME) return -1; // coordinates are too old
|
||||
if(ra) *ra = r;
|
||||
if(decl) *decl = d;
|
||||
return mountstatus;
|
||||
}
|
||||
|
||||
void stop_telescope(){ // work even in paused mode if moving!
|
||||
Target = 0;
|
||||
if(pause_communication){
|
||||
if(mountstatus == TEL_PARKED || mountstatus == TEL_STOPPED || mountstatus == TEL_INHIBITED
|
||||
|| mountstatus == TEL_OUTLIMIT) return;
|
||||
}
|
||||
write_cmd(":RT9#", NULL); // stop tracking
|
||||
write_cmd(":AL#", NULL); // stop tracking
|
||||
write_cmd(":STOP#", NULL); // halt moving
|
||||
}
|
||||
|
||||
// site characteristics
|
||||
static char
|
||||
*elevation = NULL,
|
||||
*longitude = NULL,
|
||||
*latitude = NULL;
|
||||
|
||||
// make duplicate of buf without trailing `#`
|
||||
// if astr == 1, surround content with ''
|
||||
static char *dups(const char *buf, int astr){
|
||||
if(!buf) return NULL;
|
||||
char *newbuf = malloc(strlen(buf)+5), *bptr = newbuf+1;
|
||||
if(!newbuf) return NULL;
|
||||
strcpy(bptr, buf);
|
||||
char *sharp = strrchr(bptr, '#');
|
||||
if(sharp) *sharp = 0;
|
||||
if(astr){
|
||||
bptr = newbuf;
|
||||
*bptr = '\'';
|
||||
int l = strlen(newbuf);
|
||||
newbuf[l] = '\'';
|
||||
newbuf[l+1] = 0;
|
||||
}
|
||||
char *d = strdup(bptr);
|
||||
free(newbuf);
|
||||
return d;
|
||||
}
|
||||
|
||||
static void getplace(){
|
||||
char *ans, ibuff[BUFLEN];
|
||||
if(!elevation){
|
||||
ans = write_cmd(":Gev#", ibuff);
|
||||
elevation = dups(ans, 0);
|
||||
}
|
||||
if(!longitude){
|
||||
ans = write_cmd(":Gg#", ibuff);
|
||||
longitude = dups(ans, 1);
|
||||
}
|
||||
if(!latitude){
|
||||
ans = write_cmd(":Gt#", ibuff);
|
||||
latitude = dups(ans, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *statuses[12] = {
|
||||
[TEL_TRACKING] = "'Tracking'",
|
||||
[TEL_STOPHOM] = "'Stopped or homing'",
|
||||
[TEL_PARKING] = "'Slewing to park'",
|
||||
[TEL_UNPARKING] = "'Unparking'",
|
||||
[TEL_HOMING] = "'Slewing to home'",
|
||||
[TEL_PARKED] = "'Parked'",
|
||||
[TEL_SLEWING] = "'Slewing or going to stop'",
|
||||
[TEL_STOPPED] = "'Stopped'",
|
||||
[TEL_INHIBITED] = "'Motors inhibited, T too low'",
|
||||
[TEL_OUTLIMIT] = "'Outside tracking limit'",
|
||||
[TEL_FOLSAT]= "'Following satellite'",
|
||||
[TEL_DATINCOSIST]= "'Data inconsistency'"
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief strstatus - return string explanation of mount status
|
||||
* @param status - integer status code
|
||||
* @return statically allocated string with explanation
|
||||
*/
|
||||
static const char* strstatus(int status){
|
||||
if(status < 0) return "'Signal lost'";
|
||||
if(status < TEL_MAXSTATUS) return statuses[status];
|
||||
if(status == 99) return "'Error'";
|
||||
return "'Unknown status'";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief wrhdr - try to write into header file
|
||||
*/
|
||||
void wrhdr(){
|
||||
static time_t commWasPaused = 0;
|
||||
if(pause_communication){ // don't allow pauses more for 15 minutes!
|
||||
if(commWasPaused == 0){
|
||||
commWasPaused = time(NULL);
|
||||
return;
|
||||
}else{
|
||||
if(time(NULL) - commWasPaused > 15*60){
|
||||
putlog("Clear communication pause after 15 minutes");
|
||||
pause_communication = 0;
|
||||
}else return;
|
||||
}
|
||||
}
|
||||
static int failcounter = 0;
|
||||
static time_t lastcorr = 0; // last time of corrections made
|
||||
if(time(NULL) - lastcorr > CORRECTIONS_TIMEDIFF){ // make correction once per hour
|
||||
if(makecorr()) lastcorr = time(NULL);
|
||||
else lastcorr += 30; // failed -> check 30s later
|
||||
}
|
||||
char *ans = NULL, *jd = NULL, *lst = NULL, *date = NULL, *pS = NULL;
|
||||
char ibuff[BUFLEN];
|
||||
// get coordinates for writing to file & sending to stellarium client
|
||||
ans = write_cmd(":GR#", ibuff);
|
||||
if(!str2coord(ans, &r)){
|
||||
if(++failcounter == 10){
|
||||
putlog("Lost connection with mount");
|
||||
DBG("Can't get RA!");
|
||||
signals(9);
|
||||
}
|
||||
DBG("Failed");
|
||||
return;
|
||||
}
|
||||
ans = write_cmd(":GD#", ibuff);
|
||||
if(!str2coord(ans, &d)){
|
||||
if(++failcounter == 10){
|
||||
putlog("Lost connection with mount");
|
||||
DBG("Can't get DEC!");
|
||||
signals(9);
|
||||
}
|
||||
DBG("Failed");
|
||||
return;
|
||||
}
|
||||
almDut *dut = getDUT();
|
||||
localWeather *weather = getWeath();
|
||||
double LST = 0; // local sidereal time IN RADIANS!
|
||||
|
||||
placeData *place = getPlace();
|
||||
if(get_LST(NULL, dut->DUT1, place->slong, &LST)){
|
||||
DBG("Can't calculate coordinates, get from mount");
|
||||
ans = write_cmd(":GS#", ibuff);
|
||||
lst = dups(ans, 1);
|
||||
if(!str2coord(ans, &LST)){
|
||||
if(++failcounter == 10){
|
||||
putlog("Lost connection with mount");
|
||||
DBG("Can't get LST!");
|
||||
signals(9);
|
||||
}
|
||||
DBG("Failed");
|
||||
return;
|
||||
}
|
||||
LST *= 15.*ERFA_DD2R; // convert hours to radians
|
||||
}else{
|
||||
lst = MALLOC(char, 32);
|
||||
r2sHMS(LST, lst, 32);
|
||||
}
|
||||
sMJD mjd;
|
||||
if(get_MJDt(NULL, &mjd)){
|
||||
ans = write_cmd(":GJD1#", ibuff);
|
||||
jd = dups(ans, 0);
|
||||
}else{
|
||||
jd = MALLOC(char, 32);
|
||||
snprintf(jd, 32, "%.10f", mjd.MJD);
|
||||
}
|
||||
polarCrds pNow = {.ra = r*15.*ERFA_DD2R, .dec = d*ERFA_DD2R}; // coordinates now
|
||||
horizCrds hNow;
|
||||
eq2hor(&pNow, &hNow, LST);
|
||||
failcounter = 0;
|
||||
tlast = time(NULL);
|
||||
// check it here, not in the beginning of function - to check connection with mount first
|
||||
if(!hdname){
|
||||
DBG("hdname not given!");
|
||||
return;
|
||||
}
|
||||
if(!elevation || !longitude || !latitude) getplace();
|
||||
ans = write_cmd(":GUDT#", ibuff);
|
||||
if(ans){
|
||||
char *comma = strchr(ans, ',');
|
||||
if(comma){
|
||||
*comma = 'T';
|
||||
date = dups(ans, 1);
|
||||
}
|
||||
}
|
||||
ans = write_cmd(":pS#", ibuff); pS = dups(ans, 1);
|
||||
ans = write_cmd(":Gstat#", ibuff);
|
||||
if(ans){
|
||||
mountstatus = atoi(ans);
|
||||
//DBG("Status: %d", mountstatus);
|
||||
}
|
||||
int l = strlen(hdname) + 7;
|
||||
char *aname = MALLOC(char, l);
|
||||
snprintf(aname, l, "%sXXXXXX", hdname);
|
||||
int fd = mkstemp(aname);
|
||||
if(fd < 0){
|
||||
WARN("Can't write header file: mkstemp()");
|
||||
FREE(aname);
|
||||
FREE(jd); FREE(lst); FREE(date); FREE(pS);
|
||||
return;
|
||||
}
|
||||
fchmod(fd, 0644);
|
||||
char val[22];
|
||||
#define WRHDR(k, v, c) do{if(printhdr(fd, k, v, c)){goto returning;}}while(0)
|
||||
WRHDR("TIMESYS", "'UTC'", "Time system");
|
||||
WRHDR("ORIGIN", "'SAO RAS'", "Organization responsible for the data");
|
||||
WRHDR("TELESCOP", TELESCOPE_NAME, "Telescope name");
|
||||
snprintf(val, 22, "%.10f", dut->px);
|
||||
WRHDR("POLARX", val, "IERS pole X coordinate, arcsec");
|
||||
snprintf(val, 22, "%.10f", dut->py);
|
||||
WRHDR("POLARY", val, "IERS pole Y coordinate, arcsec");
|
||||
snprintf(val, 22, "%.10f", dut->py);
|
||||
WRHDR("DUT1", val, "IERS `UT1-UTC`, sec");
|
||||
if(Target){ // target coordinates entered - store them @header
|
||||
snprintf(val, 22, "%.10f", ptRAdeg);
|
||||
WRHDR("TAGRA", val, "Target RA (J2000), degrees");
|
||||
snprintf(val, 22, "%.10f", ptDECdeg);
|
||||
WRHDR("TAGDEC", val, "Target DEC (J2000), degrees");
|
||||
}
|
||||
snprintf(val, 22, "%.10f", r*15.); // convert RA to degrees
|
||||
WRHDR("RA", val, "Telescope right ascension, current epoch, deg");
|
||||
snprintf(val, 22, "%.10f", d);
|
||||
WRHDR("DEC", val, "Telescope declination, current epoch, deg");
|
||||
snprintf(val, 22, "%.10f", hNow.az * ERFA_DR2D);
|
||||
WRHDR("AZ", val, "Telescope azimuth, current epoch, deg");
|
||||
snprintf(val, 22, "%.10f", hNow.zd * ERFA_DR2D);
|
||||
WRHDR("ZD", val, "Telescope zenith distance, current epoch, deg");
|
||||
WRHDR("TELSTAT", strstatus(mountstatus), "Telescope mount status");
|
||||
if(!get_MJDt(NULL, &mjd)){
|
||||
snprintf(val, 22, "%.10f", 2000.+(mjd.MJD-MJD2000)/365.25); // calculate EPOCH/EQUINOX
|
||||
WRHDR("EQUINOX", val, "Equinox of celestial coordinate system");
|
||||
if(!jd){
|
||||
snprintf(val, 22, "%.10f", mjd.MJD);
|
||||
WRHDR("MJD-END", val, "Modified julian date of observations end");
|
||||
}
|
||||
}
|
||||
if(jd){
|
||||
WRHDR("MJD-END", jd, "Modified julian date of observations end");
|
||||
}
|
||||
if(pS) WRHDR("PIERSIDE", pS, "Pier side of telescope mount");
|
||||
if(elevation) WRHDR("ELEVAT", elevation, "Elevation of site over the sea level");
|
||||
if(longitude) WRHDR("LONGITUD", longitude, "Geo longitude of site (east negative)");
|
||||
if(latitude) WRHDR("LATITUDE", latitude, "Geo latitude of site (south negative)");
|
||||
if(lst) WRHDR("LSTEND", lst, "Local sidereal time of observations end");
|
||||
if(date) WRHDR("DATE-END", date, "Date (UTC) of observations end");
|
||||
if(weather){
|
||||
snprintf(val, 22, "%.1f", weather->relhum);
|
||||
WRHDR("HUMIDITY", val, "Relative humidity, %%");
|
||||
snprintf(val, 22, "%.1f", weather->pres);
|
||||
WRHDR("PRESSURE", val, "Atmospheric pressure, mmHg");
|
||||
snprintf(val, 22, "%.1f", weather->tc);
|
||||
WRHDR("EXTTEMP", val, "External temperature, degrC");
|
||||
snprintf(val, 22, "%.0f", weather->rain);
|
||||
WRHDR("RAIN", val, "Rain conditions");
|
||||
snprintf(val, 22, "%.1f", weather->clouds);
|
||||
WRHDR("SKYQUAL", val, "Sky quality (0 - wery bad, >2500 - good)");
|
||||
snprintf(val, 22, "%.1f", weather->wind);
|
||||
WRHDR("WINDSPD", val, "Wind speed (m/s)");
|
||||
snprintf(val, 22, "%.0f", weather->time);
|
||||
WRHDR("WEATTIME", val, "Unix time of weather measurements");
|
||||
}
|
||||
// WRHDR("", , "");
|
||||
#undef WRHDR
|
||||
returning:
|
||||
FREE(jd); FREE(lst); FREE(date); FREE(pS);
|
||||
close(fd);
|
||||
rename(aname, hdname);
|
||||
FREE(aname);
|
||||
}
|
||||
|
||||
// terminal thread: allows to work with terminal through socket
|
||||
void *term_thread(void *sockd){
|
||||
int sock = *(int*)sockd;
|
||||
char buff[BUFLEN+1], ibuff[BUFLEN+2];
|
||||
// get client IP from socket fd - for logging
|
||||
struct sockaddr_in peer;
|
||||
socklen_t peer_len = sizeof(peer);
|
||||
char *peerIP = NULL;
|
||||
if(getpeername(sock, (struct sockaddr*)&peer, &peer_len) == 0){
|
||||
peerIP = inet_ntoa(peer.sin_addr);
|
||||
}
|
||||
while(!global_quit){ // blocking read
|
||||
ssize_t rd = read(sock, buff, BUFLEN);
|
||||
if(rd <= 0){ // error or disconnect
|
||||
DBG("Nothing to read from fd %d (ret: %zd)", sock, rd);
|
||||
break;
|
||||
}
|
||||
buff[rd] = 0;
|
||||
char *ch = strchr(buff, '\n');
|
||||
if(ch) *ch = 0;
|
||||
if(!buff[0]) continue; // empty string
|
||||
DBG("%s COMMAND: %s", peerIP, buff);
|
||||
if(strcasecmp(buff, "pause") == 0){
|
||||
DBG("PAUSED");
|
||||
putlog("Port writing outside terminal thread is paused");
|
||||
pause_communication = 1;
|
||||
continue;
|
||||
}
|
||||
if(strcasecmp(buff, "continue") == 0){
|
||||
DBG("CONTINUED");
|
||||
putlog("Port writing outside terminal thread is restored by user");
|
||||
pause_communication = 0;
|
||||
continue;
|
||||
}
|
||||
char *ans = write_cmd(buff, ibuff);
|
||||
putlog("%s COMMAND %s ANSWER %s", peerIP, buff, ibuff);
|
||||
DBG("ANSWER: %s", ibuff);
|
||||
if(ans){
|
||||
ssize_t l = (ssize_t)strlen(ans);
|
||||
if(l++){
|
||||
ans[l-1] = '\n';
|
||||
ans[l] = 0;
|
||||
if(l != write(sock, ans, l)){
|
||||
WARN("term_thread, write()");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
close(sock);
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* geany_encoding=koi8-r
|
||||
* telescope.h
|
||||
*
|
||||
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef __TELESCOPE_H__
|
||||
#define __TELESCOPE_H__
|
||||
|
||||
// max time after last coordinates reading
|
||||
#define COORDS_TOO_OLD_TIME (5)
|
||||
// make datetime/pressure/temperature corrections each CORRECTIONS_TIMEDIFF seconds
|
||||
#define CORRECTIONS_TIMEDIFF (3600)
|
||||
|
||||
#define TELESCOPE_NAME "'Astrosib-500 (2)'"
|
||||
|
||||
// telescope statuses
|
||||
typedef enum{
|
||||
TEL_TRACKING = 0,
|
||||
TEL_STOPHOM = 1,
|
||||
TEL_PARKING = 2,
|
||||
TEL_UNPARKING = 3,
|
||||
TEL_HOMING = 4,
|
||||
TEL_PARKED = 5,
|
||||
TEL_SLEWING = 6,
|
||||
TEL_STOPPED = 7,
|
||||
TEL_INHIBITED = 8,
|
||||
TEL_OUTLIMIT = 9,
|
||||
TEL_FOLSAT = 10,
|
||||
TEL_DATINCOSIST = 11,
|
||||
TEL_MAXSTATUS = 12 // number of statuses
|
||||
} tel_status;
|
||||
|
||||
int connect_telescope(char *dev, char *hdrname);
|
||||
int point_telescope(double ra, double decl);
|
||||
int get_telescope_coords(double *ra, double *decl);
|
||||
void stop_telescope();
|
||||
void wrhdr();
|
||||
void *term_thread(void *sockd);
|
||||
|
||||
#endif // __TELESCOPE_H__
|
||||
@@ -1,445 +0,0 @@
|
||||
/* geany_encoding=koi8-r
|
||||
* usefull_macros.h - a set of usefull functions: memory, color etc
|
||||
*
|
||||
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "usefull_macro.h"
|
||||
#include <linux/limits.h> // PATH_MAX
|
||||
#include <pthread.h>
|
||||
|
||||
/**
|
||||
* function for different purposes that need to know time intervals
|
||||
* @return double value: time in seconds
|
||||
*/
|
||||
double dtime(){
|
||||
double t;
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
t = tv.tv_sec + ((double)tv.tv_usec)/1e6;
|
||||
return t;
|
||||
}
|
||||
|
||||
/******************************************************************************\
|
||||
* Coloured terminal
|
||||
\******************************************************************************/
|
||||
int globErr = 0; // errno for WARN/ERR
|
||||
|
||||
// pointers to coloured output printf
|
||||
int (*red)(const char *fmt, ...);
|
||||
int (*green)(const char *fmt, ...);
|
||||
int (*_WARN)(const char *fmt, ...);
|
||||
|
||||
/*
|
||||
* format red / green messages
|
||||
* name: r_pr_, g_pr_
|
||||
* @param fmt ... - printf-like format
|
||||
* @return number of printed symbols
|
||||
*/
|
||||
int r_pr_(const char *fmt, ...){
|
||||
va_list ar; int i;
|
||||
printf(RED);
|
||||
va_start(ar, fmt);
|
||||
i = vprintf(fmt, ar);
|
||||
va_end(ar);
|
||||
printf(OLDCOLOR);
|
||||
return i;
|
||||
}
|
||||
int g_pr_(const char *fmt, ...){
|
||||
va_list ar; int i;
|
||||
printf(GREEN);
|
||||
va_start(ar, fmt);
|
||||
i = vprintf(fmt, ar);
|
||||
va_end(ar);
|
||||
printf(OLDCOLOR);
|
||||
return i;
|
||||
}
|
||||
/*
|
||||
* print red error/warning messages (if output is a tty)
|
||||
* @param fmt ... - printf-like format
|
||||
* @return number of printed symbols
|
||||
*/
|
||||
int r_WARN(const char *fmt, ...){
|
||||
va_list ar; int i = 1;
|
||||
fprintf(stderr, RED);
|
||||
va_start(ar, fmt);
|
||||
if(globErr){
|
||||
errno = globErr;
|
||||
vwarn(fmt, ar);
|
||||
errno = 0;
|
||||
}else
|
||||
i = vfprintf(stderr, fmt, ar);
|
||||
va_end(ar);
|
||||
i++;
|
||||
fprintf(stderr, OLDCOLOR "\n");
|
||||
return i;
|
||||
}
|
||||
|
||||
static const char stars[] = "****************************************";
|
||||
/*
|
||||
* notty variants of coloured printf
|
||||
* name: s_WARN, r_pr_notty
|
||||
* @param fmt ... - printf-like format
|
||||
* @return number of printed symbols
|
||||
*/
|
||||
int s_WARN(const char *fmt, ...){
|
||||
va_list ar; int i;
|
||||
i = fprintf(stderr, "\n%s\n", stars);
|
||||
va_start(ar, fmt);
|
||||
if(globErr){
|
||||
errno = globErr;
|
||||
vwarn(fmt, ar);
|
||||
errno = 0;
|
||||
}else
|
||||
i = +vfprintf(stderr, fmt, ar);
|
||||
va_end(ar);
|
||||
i += fprintf(stderr, "\n%s\n", stars);
|
||||
i += fprintf(stderr, "\n");
|
||||
return i;
|
||||
}
|
||||
int r_pr_notty(const char *fmt, ...){
|
||||
va_list ar; int i;
|
||||
i = printf("\n%s\n", stars);
|
||||
va_start(ar, fmt);
|
||||
i += vprintf(fmt, ar);
|
||||
va_end(ar);
|
||||
i += printf("\n%s\n", stars);
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run this function in the beginning of main() to setup locale & coloured output
|
||||
*/
|
||||
void initial_setup(){
|
||||
// setup coloured output
|
||||
if(isatty(STDOUT_FILENO)){ // make color output in tty
|
||||
red = r_pr_; green = g_pr_;
|
||||
}else{ // no colors in case of pipe
|
||||
red = r_pr_notty; green = printf;
|
||||
}
|
||||
if(isatty(STDERR_FILENO)) _WARN = r_WARN;
|
||||
else _WARN = s_WARN;
|
||||
// Setup locale
|
||||
setlocale(LC_ALL, "");
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
#if defined GETTEXT_PACKAGE && defined LOCALEDIR
|
||||
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
|
||||
textdomain(GETTEXT_PACKAGE);
|
||||
#endif
|
||||
}
|
||||
|
||||
/******************************************************************************\
|
||||
* Memory
|
||||
\******************************************************************************/
|
||||
/*
|
||||
* safe memory allocation for macro ALLOC
|
||||
* @param N - number of elements to allocate
|
||||
* @param S - size of single element (typically sizeof)
|
||||
* @return pointer to allocated memory area
|
||||
*/
|
||||
void *my_alloc(size_t N, size_t S){
|
||||
void *p = calloc(N, S);
|
||||
if(!p) ERR("malloc");
|
||||
//assert(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mmap file to a memory area
|
||||
*
|
||||
* @param filename (i) - name of file to mmap
|
||||
* @return stuct with mmap'ed file or die
|
||||
*/
|
||||
mmapbuf *My_mmap(char *filename){
|
||||
int fd;
|
||||
char *ptr;
|
||||
size_t Mlen;
|
||||
struct stat statbuf;
|
||||
/// "îÅ ÚÁÄÁÎÏ ÉÍÑ ÆÁÊÌÁ!"
|
||||
if(!filename){
|
||||
WARNX(_("No filename given!"));
|
||||
return NULL;
|
||||
}
|
||||
if((fd = open(filename, O_RDONLY)) < 0){
|
||||
/// "îÅ ÍÏÇÕ ÏÔËÒÙÔØ %s ÄÌÑ ÞÔÅÎÉÑ"
|
||||
WARN(_("Can't open %s for reading"), filename);
|
||||
return NULL;
|
||||
}
|
||||
if(fstat (fd, &statbuf) < 0){
|
||||
/// "îÅ ÍÏÇÕ ×ÙÐÏÌÎÉÔØ stat %s"
|
||||
WARN(_("Can't stat %s"), filename);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
Mlen = statbuf.st_size;
|
||||
if((ptr = mmap (0, Mlen, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED){
|
||||
/// "ïÛÉÂËÁ mmap"
|
||||
WARN(_("Mmap error for input"));
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
/// "îÅ ÍÏÇÕ ÚÁËÒÙÔØ mmap'ÎÕÔÙÊ ÆÁÊÌ"
|
||||
if(close(fd)) WARN(_("Can't close mmap'ed file"));
|
||||
mmapbuf *ret = MALLOC(mmapbuf, 1);
|
||||
ret->data = ptr;
|
||||
ret->len = Mlen;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void My_munmap(mmapbuf *b){
|
||||
if(munmap(b->data, b->len)){
|
||||
/// "îÅ ÍÏÇÕ munmap"
|
||||
ERR(_("Can't munmap"));
|
||||
}
|
||||
FREE(b);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************\
|
||||
* Terminal in no-echo mode
|
||||
\******************************************************************************/
|
||||
static struct termios oldt, newt; // terminal flags
|
||||
static int console_changed = 0;
|
||||
// run on exit:
|
||||
void restore_console(){
|
||||
if(console_changed)
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // return terminal to previous state
|
||||
console_changed = 0;
|
||||
}
|
||||
|
||||
// initial setup:
|
||||
void setup_con(){
|
||||
if(console_changed) return;
|
||||
tcgetattr(STDIN_FILENO, &oldt);
|
||||
newt = oldt;
|
||||
newt.c_lflag &= ~(ICANON | ECHO);
|
||||
if(tcsetattr(STDIN_FILENO, TCSANOW, &newt) < 0){
|
||||
/// "îÅ ÍÏÇÕ ÎÁÓÔÒÏÉÔØ ËÏÎÓÏÌØ"
|
||||
WARN(_("Can't setup console"));
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
|
||||
signals(0); //quit?
|
||||
}
|
||||
console_changed = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read character from console without echo
|
||||
* @return char readed
|
||||
*/
|
||||
int read_console(){
|
||||
int rb;
|
||||
struct timeval tv;
|
||||
int retval;
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(STDIN_FILENO, &rfds);
|
||||
tv.tv_sec = 0; tv.tv_usec = 10000;
|
||||
retval = select(1, &rfds, NULL, NULL, &tv);
|
||||
if(!retval) rb = 0;
|
||||
else {
|
||||
if(FD_ISSET(STDIN_FILENO, &rfds)) rb = getchar();
|
||||
else rb = 0;
|
||||
}
|
||||
return rb;
|
||||
}
|
||||
|
||||
/**
|
||||
* getchar() without echo
|
||||
* wait until at least one character pressed
|
||||
* @return character readed
|
||||
*/
|
||||
int mygetchar(){ // getchar() without need of pressing ENTER
|
||||
int ret;
|
||||
do ret = read_console();
|
||||
while(ret == 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************\
|
||||
* TTY with select()
|
||||
\******************************************************************************/
|
||||
static struct termios oldtty, tty; // TTY flags
|
||||
static int comfd = -1; // TTY fd
|
||||
|
||||
// run on exit:
|
||||
void restore_tty(){
|
||||
if(comfd == -1) return;
|
||||
ioctl(comfd, TCSANOW, &oldtty ); // return TTY to previous state
|
||||
close(comfd);
|
||||
comfd = -1;
|
||||
}
|
||||
|
||||
#ifndef BAUD_RATE
|
||||
#define BAUD_RATE B9600
|
||||
#endif
|
||||
// init: (speed = B9600 etc)
|
||||
void tty_init(char *comdev, tcflag_t speed){
|
||||
if(comfd == -1){ // not opened
|
||||
if(!comdev){
|
||||
WARNX("comdev == NULL");
|
||||
signals(11);
|
||||
}
|
||||
DBG("Open port...");
|
||||
do{
|
||||
comfd = open(comdev,O_RDWR|O_NOCTTY|O_NONBLOCK);
|
||||
}while (comfd == -1 && errno == EINTR);
|
||||
if(comfd < 0){
|
||||
WARN(_("Can't open port %s"),comdev);
|
||||
signals(2);
|
||||
}
|
||||
/* DBG("OK\nGet current settings...");
|
||||
if(ioctl(comfd, TCGETA, &oldtty) < 0){ // Get settings
|
||||
/// "îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÎÁÓÔÒÏÊËÉ"
|
||||
WARN(_("Can't get settings"));
|
||||
signals(2);
|
||||
}*/
|
||||
DBG("Make exclusive");
|
||||
// make exclusive open
|
||||
if(ioctl(comfd, TIOCEXCL)){
|
||||
WARN(_("Can't do exclusive open"));
|
||||
close(comfd);
|
||||
signals(2);
|
||||
}
|
||||
}
|
||||
tty = oldtty;
|
||||
tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
|
||||
tty.c_oflag = 0;
|
||||
tty.c_cflag = speed|CS8|CREAD|CLOCAL; // 9.6k, 8N1, RW, ignore line ctrl
|
||||
tty.c_cc[VMIN] = 0; // non-canonical mode
|
||||
tty.c_cc[VTIME] = 5;
|
||||
if(ioctl(comfd, TCSETA, &tty) < 0){
|
||||
/// "îÅ ÍÏÇÕ ÕÓÔÁÎÏ×ÉÔØ ÎÁÓÔÒÏÊËÉ"
|
||||
WARN(_("Can't set settings"));
|
||||
signals(0);
|
||||
}
|
||||
DBG("OK");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from TTY
|
||||
* @param buff (o) - buffer for data read
|
||||
* @param length - buffer len
|
||||
* @return amount of bytes read
|
||||
*/
|
||||
size_t read_tty(char *buff, size_t length){
|
||||
if(comfd < 0) return 0;
|
||||
ssize_t L = 0, l;
|
||||
char *ptr = buff;
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
int retval;
|
||||
do{
|
||||
l = 0;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(comfd, &rfds);
|
||||
// wait for 10ms
|
||||
tv.tv_sec = 0; tv.tv_usec = 10000;
|
||||
retval = select(comfd + 1, &rfds, NULL, NULL, &tv);
|
||||
if (!retval) break;
|
||||
if(FD_ISSET(comfd, &rfds)){
|
||||
if((l = read(comfd, ptr, length)) < 1){
|
||||
return 0;
|
||||
}
|
||||
ptr += l; L += l;
|
||||
length -= l;
|
||||
}
|
||||
}while(l);
|
||||
return (size_t)L;
|
||||
}
|
||||
|
||||
int write_tty(const char *buff, size_t length){
|
||||
ssize_t L = write(comfd, buff, length);
|
||||
if((size_t)L != length){
|
||||
/// "ïÛÉÂËÁ ÚÁÐÉÓÉ!"
|
||||
WARN("Write error");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely convert data from string to double
|
||||
*
|
||||
* @param num (o) - double number read from string
|
||||
* @param str (i) - input string
|
||||
* @return 1 if success, 0 if fails
|
||||
*/
|
||||
int str2double(double *num, const char *str){
|
||||
double res;
|
||||
char *endptr;
|
||||
if(!str) return 0;
|
||||
res = strtod(str, &endptr);
|
||||
if(endptr == str || *str == '\0' || *endptr != '\0'){
|
||||
/// "îÅÐÒÁ×ÉÌØÎÙÊ ÆÏÒÍÁÔ ÞÉÓÌÁ double!"
|
||||
WARNX("Wrong double number format!");
|
||||
return FALSE;
|
||||
}
|
||||
if(num) *num = res; // you may run it like myatod(NULL, str) to test wether str is double number
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static char *logname = NULL;
|
||||
static pthread_mutex_t logmutex;
|
||||
|
||||
/**
|
||||
* @brief openlogfile - create log file: init mutex, test file open ability
|
||||
* @param log - log structure
|
||||
* @return 0 if all OK
|
||||
*/
|
||||
int openlogfile(char *name){
|
||||
FREE(logname);
|
||||
pthread_mutex_destroy(&logmutex);
|
||||
FILE *logfd = fopen(name, "a");
|
||||
if(!logfd){
|
||||
WARN("Can't open log file");
|
||||
return 2;
|
||||
}
|
||||
fclose(logfd);
|
||||
if(pthread_mutex_init(&logmutex, NULL)){
|
||||
WARN("Can't init log mutes");
|
||||
return 3;
|
||||
}
|
||||
logname = strdup(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int putlog(const char *fmt, ...){
|
||||
if(!logname) return 0;
|
||||
if(pthread_mutex_lock(&logmutex)){
|
||||
WARN("Can't lock log mutex");
|
||||
return 0;
|
||||
}
|
||||
int i = 0;
|
||||
FILE *logfd = fopen(logname, "a");
|
||||
if(!logfd) goto rtn;
|
||||
char strtm[128];
|
||||
time_t t = time(NULL);
|
||||
struct tm *curtm = localtime(&t);
|
||||
strftime(strtm, 128, "%Y/%m/%d-%H:%M:%S", curtm);
|
||||
i = fprintf(logfd, "%s\t", strtm);
|
||||
va_list ar;
|
||||
va_start(ar, fmt);
|
||||
i += vfprintf(logfd, fmt, ar);
|
||||
va_end(ar);
|
||||
i += fprintf(logfd, "\n");
|
||||
fclose(logfd);
|
||||
rtn:
|
||||
pthread_mutex_unlock(&logmutex);
|
||||
return i;
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
/* geany_encoding=koi8-r
|
||||
* usefull_macros.h - a set of usefull macros: memory, color etc
|
||||
*
|
||||
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef __USEFULL_MACROS_H__
|
||||
#define __USEFULL_MACROS_H__
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
#include <locale.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#if defined GETTEXT_PACKAGE && defined LOCALEDIR
|
||||
/*
|
||||
* GETTEXT
|
||||
*/
|
||||
#include <libintl.h>
|
||||
#define _(String) gettext(String)
|
||||
#define gettext_noop(String) String
|
||||
#define N_(String) gettext_noop(String)
|
||||
#else
|
||||
#define _(String) (String)
|
||||
#define N_(String) (String)
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <termios.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// unused arguments with -Wall -Werror
|
||||
#define _U_ __attribute__((__unused__))
|
||||
|
||||
/*
|
||||
* Coloured messages output
|
||||
*/
|
||||
#define RED "\033[1;31;40m"
|
||||
#define GREEN "\033[1;32;40m"
|
||||
#define OLDCOLOR "\033[0;0;0m"
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE (0)
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE (1)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ERROR/WARNING messages
|
||||
*/
|
||||
extern int globErr;
|
||||
extern void signals(int sig);
|
||||
#define ERR(...) do{globErr=errno; _WARN(__VA_ARGS__); signals(9);}while(0)
|
||||
#define ERRX(...) do{globErr=0; _WARN(__VA_ARGS__); signals(9);}while(0)
|
||||
#define WARN(...) do{globErr=errno; _WARN(__VA_ARGS__);}while(0)
|
||||
#define WARNX(...) do{globErr=0; _WARN(__VA_ARGS__);}while(0)
|
||||
|
||||
/*
|
||||
* print function name, debug messages
|
||||
* debug mode, -DEBUG
|
||||
*/
|
||||
#ifdef EBUG
|
||||
#define FNAME() do{ fprintf(stderr, OLDCOLOR); \
|
||||
fprintf(stderr, "\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__);} while(0)
|
||||
#define DBG(...) do{ fprintf(stderr, OLDCOLOR); \
|
||||
fprintf(stderr, "%s (%s, line %d): ", __func__, __FILE__, __LINE__); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n");} while(0)
|
||||
#else
|
||||
#define FNAME() do{}while(0)
|
||||
#define DBG(...) do{}while(0)
|
||||
#endif //EBUG
|
||||
|
||||
/*
|
||||
* Memory allocation
|
||||
*/
|
||||
#define ALLOC(type, var, size) type * var = ((type *)my_alloc(size, sizeof(type)))
|
||||
#define MALLOC(type, size) ((type *)my_alloc(size, sizeof(type)))
|
||||
#define FREE(ptr) do{if(ptr){free(ptr); ptr = NULL;}}while(0)
|
||||
|
||||
#ifndef DBL_EPSILON
|
||||
#define DBL_EPSILON (2.2204460492503131e-16)
|
||||
#endif
|
||||
|
||||
double dtime();
|
||||
|
||||
// functions for color output in tty & no-color in pipes
|
||||
extern int (*red)(const char *fmt, ...);
|
||||
extern int (*_WARN)(const char *fmt, ...);
|
||||
extern int (*green)(const char *fmt, ...);
|
||||
void * my_alloc(size_t N, size_t S);
|
||||
void initial_setup();
|
||||
|
||||
// mmap file
|
||||
typedef struct{
|
||||
char *data;
|
||||
size_t len;
|
||||
} mmapbuf;
|
||||
mmapbuf *My_mmap(char *filename);
|
||||
void My_munmap(mmapbuf *b);
|
||||
|
||||
void restore_console();
|
||||
void setup_con();
|
||||
int read_console();
|
||||
int mygetchar();
|
||||
|
||||
void restore_tty();
|
||||
void tty_init(char *comdev, tcflag_t speed);
|
||||
size_t read_tty(char *buff, size_t length);
|
||||
int write_tty(const char *buff, size_t length);
|
||||
|
||||
int str2double(double *num, const char *str);
|
||||
|
||||
int openlogfile(char *name);
|
||||
int putlog(const char *fmt, ...);
|
||||
|
||||
#endif // __USEFULL_MACROS_H__
|
||||
Reference in New Issue
Block a user