Compare commits

..

2 Commits

Author SHA1 Message Date
Paul Dino Jones 485a3489a9 Cleaning up sim mapper code 2022-12-01 21:23:36 +00:00
Paul Dino Jones aeaced3676 Added support for rfactor2 2022-11-21 13:28:20 -05:00
124 changed files with 1746 additions and 12247 deletions

View File

@ -1,81 +0,0 @@
name: Make Packages
on:
push:
tags: [ "*" ]
jobs:
build-monocoque-debs:
strategy:
matrix:
os: [ubuntu-latest, debian-latest, debian-stable]
runs-on: ${{ matrix.os }}
permissions:
contents: write
steps:
- uses: actions/checkout@v1
- name: Checkout submodules
run: git submodule update --init --recursive
- name: Update apt
run: sudo apt update
- name: Install Dependencies
run: sudo apt install -y libuv1-dev libargtable2-dev libserialport-dev libconfig-dev libhidapi-dev liblua5.4-dev libxdg-basedir-dev libxml2-dev libpulse-dev libproc2-dev
- name: Set build dir
id: strings
shell: bash
run: |
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
github_sha_hash=${{ github.sha }}
echo "github-sha-short=${github_sha_hash:0:7}" >> $GITHUB_OUTPUT
- name: Configure CMake
run: >
cmake -B ${{ steps.strings.outputs.build-output-dir }}
-DCMAKE_CXX_COMPILER=g++
-DCMAKE_C_COMPILER=gcc
-DCMAKE_BUILD_TYPE=Release
-S ${{ github.workspace }}
- name: Build
run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release
- name: Copy script files around to stop .github from being added to the package then build the package
run: |
mkdir PKG_SOURCE
mkdir -p PKG_SOURCE/DEBIAN
mkdir -p PKG_SOURCE/usr/bin/
cp ./tools/distro/debian/dpkg/${{ matrix.os }}/control ./PKG_SOURCE/DEBIAN/control
cp ./build/monocoque ./PKG_SOURCE/usr/bin/monocoque
dpkg-deb --build PKG_SOURCE monocoque-${{ matrix.os }}.deb
- name: Release the Package
uses: softprops/action-gh-release@v1
with:
files: monocoque-${{ matrix.os }}.deb
build-monocoque-rpms:
strategy:
matrix:
os: [fedora-43]
runs-on: ${{ matrix.os }}
permissions:
contents: write
steps:
- name: create rpmbuild dirs
run: mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
- name: get spec file
run: curl -o ~/fedora.spec https://raw.githubusercontent.com/Spacefreak18/monocoque/refs/heads/master/tools/distro/fedora/rpm/fedora.spec
- name: run spec file
run: rpmbuild -ba ~/fedora.spec
- name: rename file
run: cp ~/rpmbuild/RPMS/x86_64/monocoque-0.0.5-1.x86_64.rpm $GITHUB_WORKSPACE/monocoque-${{ matrix.os }}.rpm
- name: Release the Package
uses: softprops/action-gh-release@v1
with:
files: monocoque-${{ matrix.os }}.rpm

2
.gitignore vendored
View File

@ -1,5 +1,3 @@
/build /build
/src/monocoque/tags /src/monocoque/tags
/src/monocoque/simulatorapi/simapi /src/monocoque/simulatorapi/simapi
.vscode

1
.gitmodules vendored
View File

@ -1,4 +1,3 @@
[submodule "src/monocoque/simulatorapi/simapi"] [submodule "src/monocoque/simulatorapi/simapi"]
path = src/monocoque/simulatorapi/simapi path = src/monocoque/simulatorapi/simapi
url = https://github.com/spacefreak18/simapi url = https://github.com/spacefreak18/simapi
# url = git@github.com:spacefreak18/simapi # ssh is better but Microsoft requires an account

View File

@ -1,4 +1,3 @@
# small known issue with argtable2
{ {
argtable arg_parse argtable arg_parse
Memcheck:Leak Memcheck:Leak
@ -108,15 +107,3 @@
... ...
... ...
} }
{
ignore_unversioned_libs
Memcheck:Leak
...
obj:*/lib*/lib*.so
}
{
ignore_versioned_libs
Memcheck:Leak
...
obj:*/lib*/lib*.so.*
}

View File

@ -1,4 +1,7 @@
cmake_minimum_required(VERSION 3.18)
# minimum CMake version required for C++20 support, among other things
cmake_minimum_required(VERSION 3.15)
# detect if Monocoque is being used as a sub-project of another CMake project # detect if Monocoque is being used as a sub-project of another CMake project
if(NOT DEFINED PROJECT_NAME) if(NOT DEFINED PROJECT_NAME)
set(MONOCOQUE_SUBPROJECT OFF) set(MONOCOQUE_SUBPROJECT OFF)
@ -8,7 +11,7 @@ endif()
SET_SOURCE_FILES_PROPERTIES( src/monocoque.c PROPERTIES LANGUAGE C) SET_SOURCE_FILES_PROPERTIES( src/monocoque.c PROPERTIES LANGUAGE C)
#set(CMAKE_BUILD_TYPE Debug) set(CMAKE_BUILD_TYPE Debug)
project(monocoque) project(monocoque)
@ -17,98 +20,54 @@ set(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed -ldl")
set(LIBUSB_INCLUDE_DIR /usr/include/libusb-1.0) set(LIBUSB_INCLUDE_DIR /usr/include/libusb-1.0)
set(LIBXML_INCLUDE_DIR /usr/include/libxml2) set(LIBXML_INCLUDE_DIR /usr/include/libxml2)
FIND_PACKAGE(Lua) FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h
set(INCLUDE_DIRS ${LUA_INCLUDE_DIR}) HINTS $ENV{LIBUSB_ROOT}
include_directories(${LUA_INCLUDE_DIR}) PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS}
if(Lua_FOUND AND NOT TARGET Lua::Lua) PATH_SUFFIXES include)
add_library(Lua::Lua INTERFACE IMPORTED)
set_target_properties(
Lua::Lua
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}"
)
endif() FIND_LIBRARY(LIBUSB_LIBRARY NAMES usb-1.0
message("Lua ${Lua_VERSION} found") HINTS $ENV{LIBUSB_ROOT}
if (Lua_VERSION VERSION_GREATER_EQUAL "5.5.0") PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS}
add_compile_definitions(USE_LUA_55) PATH_SUFFIXES lib)
else()
remove_definitions(USE_LUA_55)
endif()
#FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h set(HIDAPI_WITH_LIBUSB TRUE) # surely will be used only on Linux
# HINTS $ENV{LIBUSB_ROOT}
# PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS}
# PATH_SUFFIXES include)
#
#FIND_LIBRARY(LIBUSB_LIBRARY NAMES usb-1.0
# HINTS $ENV{LIBUSB_ROOT}
# PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS}
# PATH_SUFFIXES lib)
#
set(HIDAPI_WITH_LIBUSB FALSE) # surely will be used only on Linux
set(BUILD_SHARED_LIBS TRUE) # HIDAPI as static library on all platforms set(BUILD_SHARED_LIBS TRUE) # HIDAPI as static library on all platforms
add_executable(monocoque src/monocoque/monocoque.c)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBPROCPS libprocps)
pkg_check_modules(LIBPROC2 libproc2)
if (LIBPROCPS_FOUND)
#add_compile_definitions(USE_OLD_PID_VAL=0)
elseif (LIBPROC2_FOUND)
if (LIBPROC2_VERSION VERSION_GREATER_EQUAL "4.0.5")
#add_compile_definitions(USE_OLD_PID_VAL=0)
else()
add_compile_definitions(USE_OLD_PID_VAL=1)
endif()
else()
message(FATAL_ERROR "Either libprocps or libproc2 is required")
endif()
#if(USE_PULSEAUDIO)
#else()
#endif()
message("Using pulseaudio backend...")
add_compile_definitions(USE_PULSEAUDIO=true)
target_link_libraries(monocoque m hidapi-hidraw pulse serialport xml2 argtable2 config gameloop helper devices slog simulatorapi uv xdg-basedir Lua::Lua proc2)
target_include_directories(monocoque PUBLIC config ${LIBXML_INCLUDE_DIR} ${LUA_INCLUDE_DIR})
add_subdirectory(src/monocoque/gameloop) add_subdirectory(src/monocoque/gameloop)
add_subdirectory(src/monocoque/simulatorapi) add_subdirectory(src/monocoque/simulatorapi)
add_subdirectory(src/monocoque/helper) add_subdirectory(src/monocoque/helper)
add_subdirectory(src/monocoque/devices) add_subdirectory(src/monocoque/devices)
add_subdirectory(src/monocoque/slog) add_subdirectory(src/monocoque/slog)
#add_executable(listusb tests/testlibusb.c) add_executable(monocoque src/monocoque/monocoque.c)
#target_include_directories(listusb PUBLIC) target_include_directories(monocoque PUBLIC config ${LIBUSB_INCLUDE_DIR} ${LIBXML_INCLUDE_DIR})
#target_link_libraries(listusb portaudio hidapi-hidraw) target_link_libraries(monocoque m ${LIBUSB_LIBRARY} hidapi-libusb portaudio serialport xml2 argtable2 config gameloop helper devices slog simulatorapi)
#add_test(listusb list-usb-devices listusb)
#add_executable(testrevburner tests/testrevburner.c) add_executable(listusb tests/testlibusb.c)
#target_include_directories(testrevburner PUBLIC) target_include_directories(listusb PUBLIC ${LIBUSB_INCLUDE_DIR})
#target_link_libraries(testrevburner hidapi-hidraw) target_link_libraries(listusb ${LIBUSB_LIBRARY} portaudio)
#add_test(testrevburner testrevburner) add_test(listusb list-usb-devices listusb)
add_executable(testrevburner tests/testrevburner.c)
target_include_directories(testrevburner PUBLIC ${LIBUSB_INCLUDE_DIR})
target_link_libraries(testrevburner ${LIBUSB_LIBRARY})
add_test(testrevburner testrevburner)
add_executable(listsound tests/pa_devs.c)
target_include_directories(listsound PUBLIC)
target_link_libraries(listsound m portaudio)
add_test(list-sound-devices listsound)
add_executable(longsine tests/patest_longsine.c)
target_include_directories(longsine PUBLIC ${LIBUSB_INCLUDE_DIR} ${LIBXML_INCLUDE_DIR})
target_link_libraries(longsine ${LIBUSB_LIBRARY} m portaudio)
add_test(longsine longsine)
add_executable(parserevburnerxml tests/revburnerparsetest.c)
target_include_directories(parserevburnerxml PUBLIC ${LIBXML_INCLUDE_DIR})
target_link_libraries(parserevburnerxml ${LIBUSB_LIBRARY} portaudio xml2)
add_test(parserevburnerxml parserevburnerxml)
#add_executable(listsound tests/pa_devs.c)
#target_include_directories(listsound PUBLIC)
#target_link_libraries(listsound m portaudio)
#add_test(list-sound-devices listsound)
#
#add_executable(longsine tests/patest_longsine.c)
#target_include_directories(longsine PUBLIC ${LIBXML_INCLUDE_DIR})
#target_link_libraries(longsine m portaudio)
#add_test(longsine longsine)
#
#add_executable(parserevburnerxml tests/revburnerparsetest.c)
#target_include_directories(parserevburnerxml PUBLIC ${LIBXML_INCLUDE_DIR})
#target_link_libraries(parserevburnerxml portaudio xml2)
#add_test(parserevburnerxml parserevburnerxml)
#
add_executable(setmem tests/setmem.c) add_executable(setmem tests/setmem.c)
target_include_directories(setmem PUBLIC) target_include_directories(setmem PUBLIC)
target_link_libraries(setmem) target_link_libraries(setmem)
@ -118,17 +77,17 @@ add_executable(getmem tests/getmem.c)
target_include_directories(getmem PUBLIC) target_include_directories(getmem PUBLIC)
target_link_libraries(getmem) target_link_libraries(getmem)
add_test(getmem getmem) add_test(getmem getmem)
#
#add_executable(setsimdata tests/setsimdata.c) add_executable(setsimdata tests/setsimdata.c)
#target_include_directories(setsimdata PUBLIC) target_include_directories(setsimdata PUBLIC)
#target_link_libraries(setsimdata) target_link_libraries(setsimdata)
#add_test(setsimdata setsimdata) add_test(setsimdata setsimdata)
#
#add_executable(hidtest tests/hidtest.c) add_executable(hidtest tests/hidtest.c)
#target_include_directories(hidtest PUBLIC) target_include_directories(hidtest PUBLIC ${LIBUSB_INCLUDE_DIR})
#target_link_libraries(hidtest hidapi-hidraw) target_link_libraries(hidtest ${LIBUSB_LIBRARY} hidapi-libusb)
#add_test(hidtest hidtest) add_test(hidtest hidtest)
#
add_executable(simlighttest tests/simlighttest.c) add_executable(simlighttest tests/simlighttest.c)
target_include_directories(simlighttest PUBLIC) target_include_directories(simlighttest PUBLIC)
target_link_libraries(simlighttest serialport) target_link_libraries(simlighttest serialport)
@ -163,12 +122,6 @@ else() # GCC/Clang warning option
# e.g. at the time of writing, GCC does not support -Wdocumentation # e.g. at the time of writing, GCC does not support -Wdocumentation
# #
# enable all warnings about 'questionable constructs' # enable all warnings about 'questionable constructs'
if(analyze)
message("-- Analyzer is on")
target_compile_options(monocoque PRIVATE -fanalyzer -Wno-analyzer-possible-null-argument -Wno-analyzer-possible-null-dereference)
endif()
enable_cxx_compiler_flag_if_supported("-Wall") enable_cxx_compiler_flag_if_supported("-Wall")
# issue 'pedantic' warnings for strict ISO compliance # issue 'pedantic' warnings for strict ISO compliance
enable_cxx_compiler_flag_if_supported("-pedantic") enable_cxx_compiler_flag_if_supported("-pedantic")

View File

@ -1,39 +0,0 @@
# Monocoque User Setup Guide
## Required Packages / Software
* build [monocoque](https://github.com/Spacefreak18/monocoque) e.g. in `~/monocoque` - follow instructions from repo
* build [simd](https://github.com/Spacefreak18/simapi/tree/master/simd) e.g. in `~/simapi/simd` - follow instructions from repo
* build [simshmbridge](https://github.com/spacefreak18/simshmbridge) e.g. in `~/simshmbridge` - follow instructions from repo
## Configure SIMD & Monocoque
* Create a `simd` Config in `~/.config/simd/simd.config` - [use example](https://github.com/Spacefreak18/simapi/blob/master/simd/conf/simd.config)
* no changes required to the example file
* create Monocoque config in `~/.config/monocque/monocoque.config` - [example](https://github.com/Spacefreak18/monocoque/blob/master/conf/monocoque.config)
* Adapt the config to your needs, remove unused entries
* [Documentation for Bass Shaker Config](https://spacefreak18.github.io/simapi/shakers)
* Test your config as described in the [README](README.md#testing)
## Steam & Game Config
### Steam
* Adapt Steam Launch Commands from [simshbridge](https://github.com/spacefreak18/simshmbridge?tab=readme-ov-file#basic-mapping-examples)
* to use with `simd` you need to `SIMD_BRIDGE_EXE` to the launch command like `SIMD_BRIDGE_EXE=~/git/simshmbridge/assets/pcars2bridge.exe %command% & sleep 5 && ~/.steam/steam/steamapps/common/Proton\ 6.3/proton run ~/git/simshmbridge/assets/pcars2bridge.exe`
### Game specific settings
#### Automobilista 2 (AMS2)
* In Games like `Automobilista 2` activate the `Shared Memory` Setting and make sure to set the Protocal to `Project CARS 2`. ![System Settings in AMS2](https://static.wixstatic.com/media/910f3b_adabfa94a57944cca33e488972534fdd~mv2.png/v1/fill/w_964,h_374,al_c,q_90,usm_0.66_1.00_0.01,enc_avif,quality_auto/game_setup_ams2_1.png) <img src="https://docs.simucube.com/Tuner/games/assets/automobilista2_telemetry_2.png" alt="Shared Memory Settings in AMS2" width="65%">
* Restart the Game after changing these Settings!
#### Assetto Corsa & Assetto Corsa Competizione (ACC)
* Assetto Corsa & ACC do not need any additional settings and should work out of the box
## Run everything, but in the right order
* first start `simd` like `~/simapi/simd/build/simd --no-daemon -vv`
* this will likely fail, so you'll need to add `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib` or `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib64` in front of the command like `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib64 ~/simapi/simd/build/simd --no-daemon -vv`
* now start `monocoque` like `~/monocoque/build/monocoque play`
* finally start the Game you want to play from Steam
## Troubleshooting
* make sure `simd` is running and after you start a game, the CLI output confirms the game is detected
* make sure the bridge application starts e.g. with `ps aux|grep pcars2bridge.exe` before the game starts
* see if you have the path `/dev/shm/acpmf_physics` (for ACC) and/or `/dev/shm/$pcars2$` for AMS2
* when `simd` recognizes the game, see if `monocoque` shows `RPM, Gear..` and other Car Metrics once you start a game
* if `simd` recognozies the game, but `monocoque` does not show any data, doublecheck the [Game Settings](#steam--game-config) to see if `Shared Memory` is activated

View File

@ -1,622 +1,281 @@
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007 Version 2, June 1991
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed. of this license document, but changing it is not allowed.
Preamble Preamble
The GNU General Public License is a free, copyleft license for The licenses for most software are designed to take away your
software and other kinds of works. freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
The licenses for most software and other practical works are designed software--to make sure the software is free for all its users. This
to take away your freedom to share and change the works. By contrast, General Public License applies to most of the Free Software
the GNU General Public License is intended to guarantee your freedom to Foundation's software and to any other program whose authors commit to
share and change all versions of a program--to make sure it remains free using it. (Some other Free Software Foundation software is covered by
software for all its users. We, the Free Software Foundation, use the the GNU Lesser General Public License instead.) You can apply it to
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too. your programs, too.
When we speak of free software, we are referring to freedom, not When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you this service if you wish), that you receive source code or can get it
want it, that you can change the software or use pieces of it in new if you want it, that you can change the software or use pieces of it
free programs, and that you know you can do these things. in new free programs; and that you know you can do these things.
To protect your rights, we need to prevent others from denying you To protect your rights, we need to make restrictions that forbid
these rights or asking you to surrender the rights. Therefore, you have anyone to deny you these rights or to ask you to surrender the rights.
certain responsibilities if you distribute copies of the software, or if These restrictions translate to certain responsibilities for you if you
you modify it: responsibilities to respect the freedom of others. distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same gratis or for a fee, you must give the recipients all the rights that
freedoms that you received. You must make sure that they, too, receive you have. You must make sure that they, too, receive or can get the
or can get the source code. And you must show them these terms so they source code. And you must show them these terms so they know their
know their rights. rights.
Developers that use the GNU GPL protect your rights with two steps: We protect your rights with two steps: (1) copyright the software, and
(1) assert copyright on the software, and (2) offer you this License (2) offer you this license which gives you legal permission to copy,
giving you legal permission to copy, distribute and/or modify it. distribute and/or modify the software.
For the developers' and authors' protection, the GPL clearly explains Also, for each author's protection and ours, we want to make certain
that there is no warranty for this free software. For both users' and that everyone understands that there is no warranty for this free
authors' sake, the GPL requires that modified versions be marked as software. If the software is modified by someone else and passed on, we
changed, so that their problems will not be attributed erroneously to want its recipients to know that what they have is not the original, so
authors of previous versions. that any problems introduced by others will not reflect on the original
authors' reputations.
Some devices are designed to deny users access to install or run Finally, any free program is threatened constantly by software
modified versions of the software inside them, although the manufacturer patents. We wish to avoid the danger that redistributors of a free
can do so. This is fundamentally incompatible with the aim of program will individually obtain patent licenses, in effect making the
protecting users' freedom to change the software. The systematic program proprietary. To prevent this, we have made it clear that any
pattern of such abuse occurs in the area of products for individuals to patent must be licensed for everyone's free use or not licensed at all.
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and The precise terms and conditions for copying, distribution and
modification follow. modification follow.
TERMS AND CONDITIONS GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. Definitions.
0. This License applies to any program or other work which contains
"This License" refers to version 3 of the GNU General Public License. a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
"Copyright" also means copyright-like laws that apply to other kinds of refers to any such program or work, and a "work based on the Program"
works, such as semiconductor masks. means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
"The Program" refers to any copyrightable work licensed under this either verbatim or with modifications and/or translated into another
License. Each licensee is addressed as "you". "Licensees" and language. (Hereinafter, translation is included without limitation in
"recipients" may be individuals or organizations. the term "modification".) Each licensee is addressed as "you".
To "modify" a work means to copy from or adapt all or part of the work Activities other than copying, distribution and modification are not
in a fashion requiring copyright permission, other than the making of an covered by this License; they are outside its scope. The act of
exact copy. The resulting work is called a "modified version" of the running the Program is not restricted, and the output from the Program
earlier work or a work "based on" the earlier work. is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
A "covered work" means either the unmodified Program or a work based Whether that is true depends on what the Program does.
on the Program.
1. You may copy and distribute verbatim copies of the Program's
To "propagate" a work means to do anything with it that, without source code as you receive it, in any medium, provided that you
permission, would make you directly or secondarily liable for conspicuously and appropriately publish on each copy an appropriate
infringement under applicable copyright law, except executing it on a copyright notice and disclaimer of warranty; keep intact all the
computer or modifying a private copy. Propagation includes copying, notices that refer to this License and to the absence of any warranty;
distribution (with or without modification), making available to the and give any other recipients of the Program a copy of this License
public, and in some countries other activities as well. along with the Program.
To "convey" a work means any kind of propagation that enables other You may charge a fee for the physical act of transferring a copy, and
parties to make or receive copies. Mere interaction with a user through you may at your option offer warranty protection in exchange for a fee.
a computer network, with no transfer of a copy, is not conveying.
2. You may modify your copy or copies of the Program or any portion
An interactive user interface displays "Appropriate Legal Notices" of it, thus forming a work based on the Program, and copy and
to the extent that it includes a convenient and prominently visible distribute such modifications or work under the terms of Section 1
feature that (1) displays an appropriate copyright notice, and (2) above, provided that you also meet all of these conditions:
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the a) You must cause the modified files to carry prominent notices
work under this License, and how to view a copy of this License. If stating that you changed the files and the date of any change.
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion. b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
1. Source Code. part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source c) If the modified program normally reads commands interactively
form of a work. when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
A "Standard Interface" means an interface that either is an official announcement including an appropriate copyright notice and a
standard defined by a recognized standards body, or, in the case of notice that there is no warranty (or else, saying that you provide
interfaces specified for a particular programming language, one that a warranty) and that users may redistribute the program under
is widely used among developers working in that language. these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
The "System Libraries" of an executable work include anything, other does not normally print such an announcement, your work based on
than the work as a whole, that (a) is included in the normal form of the Program is not required to print an announcement.)
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that These requirements apply to the modified work as a whole. If
Major Component, or to implement a Standard Interface for which an identifiable sections of that work are not derived from the Program,
implementation is available to the public in source code form. A and can be reasonably considered independent and separate works in
"Major Component", in this context, means a major essential component themselves, then this License, and its terms, do not apply to those
(kernel, window system, and so on) of the specific operating system sections when you distribute them as separate works. But when you
(if any) on which the executable work runs, or a compiler used to distribute the same sections as part of a whole which is a work based
produce the work, or an object code interpreter used to run it. on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
The "Corresponding Source" for a work in object code form means all entire whole, and thus to each and every part regardless of who wrote it.
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to Thus, it is not the intent of this section to claim rights or contest
control those activities. However, it does not include the work's your rights to work written entirely by you; rather, the intent is to
System Libraries, or general-purpose tools or generally available free exercise the right to control the distribution of derivative or
programs which are used unmodified in performing those activities but collective works based on the Program.
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for In addition, mere aggregation of another work not based on the Program
the work, and the source code for shared libraries and dynamically with the Program (or with a work based on the Program) on a volume of
linked subprograms that the work is specifically designed to require, a storage or distribution medium does not bring the other work under
such as by intimate data communication or control flow between those the scope of this License.
subprograms and other parts of the work.
3. You may copy and distribute the Program (or a work based on it,
The Corresponding Source need not include anything that users under Section 2) in object code or executable form under the terms of
can regenerate automatically from other parts of the Corresponding Sections 1 and 2 above provided that you also do one of the following:
Source.
a) Accompany it with the complete corresponding machine-readable
The Corresponding Source for a work in source code form is that source code, which must be distributed under the terms of Sections
same work. 1 and 2 above on a medium customarily used for software interchange; or,
2. Basic Permissions. b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
All rights granted under this License are granted for the term of cost of physically performing source distribution, a complete
copyright on the Program, and are irrevocable provided the stated machine-readable copy of the corresponding source code, to be
conditions are met. This License explicitly affirms your unlimited distributed under the terms of Sections 1 and 2 above on a medium
permission to run the unmodified Program. The output from running a customarily used for software interchange; or,
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your c) Accompany it with the information you received as to the offer
rights of fair use or other equivalent, as provided by copyright law. to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
You may make, run and propagate covered works that you do not received the program in object code or executable form with such
convey, without conditions so long as your license otherwise remains an offer, in accord with Subsection b above.)
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you The source code for a work means the preferred form of the work for
with facilities for running those works, provided that you comply with making modifications to it. For an executable work, complete source
the terms of this License in conveying all material for which you do code means all the source code for all modules it contains, plus any
not control copyright. Those thus making or running the covered works associated interface definition files, plus the scripts used to
for you must do so exclusively on your behalf, under your direction control compilation and installation of the executable. However, as a
and control, on terms that prohibit them from making any copies of special exception, the source code distributed need not include
your copyrighted material outside their relationship with you. anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
Conveying under any other circumstances is permitted solely under operating system on which the executable runs, unless that component
the conditions stated below. Sublicensing is not allowed; section 10 itself accompanies the executable.
makes it unnecessary.
If distribution of executable or object code is made by offering
3. Protecting Users' Legal Rights From Anti-Circumvention Law. access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
No covered work shall be deemed part of an effective technological distribution of the source code, even though third parties are not
measure under any applicable law fulfilling obligations under article compelled to copy the source along with the object code.
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such 4. You may not copy, modify, sublicense, or distribute the Program
measures. except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
When you convey a covered work, you waive any legal power to forbid void, and will automatically terminate your rights under this License.
circumvention of technological measures to the extent such circumvention However, parties who have received copies, or rights, from you under
is effected by exercising rights under this License with respect to this License will not have their licenses terminated so long as such
the covered work, and you disclaim any intention to limit operation or parties remain in full compliance.
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of 5. You are not required to accept this License, since you have not
technological measures. signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
4. Conveying Verbatim Copies. prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
You may convey verbatim copies of the Program's source code as you Program), you indicate your acceptance of this License to do so, and
receive it, in any medium, provided that you conspicuously and all its terms and conditions for copying, distributing or modifying
appropriately publish on each copy an appropriate copyright notice; the Program or works based on it.
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code; 6. Each time you redistribute the Program (or any work based on the
keep intact all notices of the absence of any warranty; and give all Program), the recipient automatically receives a license from the
recipients a copy of this License along with the Program. original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
You may charge any price or no price for each copy that you convey, restrictions on the recipients' exercise of the rights granted herein.
and you may offer support or warranty protection for a fee. You are not responsible for enforcing compliance by third parties to
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License. this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free 7. If, as a consequence of a court judgment or allegation of patent
patent license under the contributor's essential patent claims, to infringement or for any other reason (not limited to patent issues),
make, use, sell, offer for sale, import and otherwise run, modify and conditions are imposed on you (whether by court order, agreement or
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a excuse you from the conditions of this License. If you cannot
covered work so as to satisfy simultaneously your obligations under this distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may License and any other pertinent obligations, then as a consequence you
not convey it at all. For example, if you agree to terms that obligate you may not distribute the Program at all. For example, if a patent
to collect a royalty for further conveying from those to whom you convey license would not permit royalty-free redistribution of the Program by
the Program, the only way you could satisfy both those terms and this all those who receive copies directly or indirectly through you, then
License would be to refrain entirely from conveying the Program. the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
13. Use with the GNU Affero General Public License. If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
Notwithstanding any other provision of this License, you have It is not the purpose of this section to induce you to infringe any
permission to link or combine any covered work with a work licensed patents or other property right claims or to contest validity of any
under version 3 of the GNU Affero General Public License into a single such claims; this section has the sole purpose of protecting the
combined work, and to convey the resulting work. The terms of this integrity of the free software distribution system, which is
License will continue to apply to the part which is the covered work, implemented by public license practices. Many people have made
but the special requirements of the GNU Affero General Public License, generous contributions to the wide range of software distributed
section 13, concerning interaction through a network will apply to the through that system in reliance on consistent application of that
combination as such. system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
14. Revised Versions of this License. This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
The Free Software Foundation may publish revised and/or new versions of 8. If the distribution and/or use of the Program is restricted in
the GNU General Public License from time to time. Such new versions will certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to be similar in spirit to the present version, but may differ in detail to
address new problems or concerns. address new problems or concerns.
Each version is given a distinguishing version number. If the Each version is given a distinguishing version number. If the Program
Program specifies that a certain numbered version of the GNU General specifies a version number of this License which applies to it and "any
Public License "or any later version" applies to it, you have the later version", you have the option of following the terms and conditions
option of following the terms and conditions either of that numbered either of that version or of any later version published by the Free
version or of any later version published by the Free Software Software Foundation. If the Program does not specify a version number of
Foundation. If the Program does not specify a version number of the this License, you may choose any version ever published by the Free Software
GNU General Public License, you may choose any version ever published Foundation.
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future 10. If you wish to incorporate parts of the Program into other free
versions of the GNU General Public License can be used, that proxy's programs whose distribution conditions are different, write to the author
public statement of acceptance of a version permanently authorizes you to ask for permission. For software which is copyrighted by the Free
to choose that version for the Program. Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
Later license versions may give you additional or different NO WARRANTY
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty. 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
ALL NECESSARY SERVICING, REPAIR OR CORRECTION. PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
@ -628,15 +287,15 @@ free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found. the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> <one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author> Copyright (C) <year> <name of author>
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 it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
@ -644,31 +303,37 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License along
along with this program. If not, see <http://www.gnu.org/licenses/>. with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail. Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short If the program is interactive, make it output a short notice like this
notice like this when it starts in an interactive mode: when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author> Gnomovision version 69, Copyright (C) year name of author
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details. under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands parts of the General Public License. Of course, the commands you use may
might be different; for a GUI interface, you would use an "about box". be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or school, You should also get your employer (if you work as a programmer) or your
if any, to sign a "copyright disclaimer" for the program, if necessary. school, if any, to sign a "copyright disclaimer" for the program, if
For more information on this, and how to apply and follow the GNU GPL, see necessary. Here is a sample; alter the names:
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program Yoyodyne, Inc., hereby disclaims all copyright interest in the program
into proprietary programs. If your program is a subroutine library, you `Gnomovision' (which makes passes at compilers) written by James Hacker.
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General <signature of Ty Coon>, 1 April 1989
Public License instead of this License. But first, please read Ty Coon, President of Vice
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

101
README.md
View File

@ -8,131 +8,58 @@ _ / / / / /_/ / / / / /_/ / /__ / /_/ / /_/ // /_/ // __/
``` ```
Cross Platform device manager for driving and flight simulators, for use with common simulator software titles. Cross Platform device manager for driving and flight simulators, for use with common simulator software titles.
📚 **Usage Documentation:** [spacefreak18.github.io/simapi/](https://spacefreak18.github.io/simapi/)
## Features ## Features
- Updates at 60 frames per seconds. - Updates at 120 frames per seconds.
- Modular design for support with various titles and devices. - Modular design for support with various titles and devices.
- Supports bass shakers, tachometers, several wheels and pedals, simlights, simwind etc, through usb and arduino serial. - Supports bass shakers, tachometers, simlights, simwind etc, through usb and arduino serial.
- Tachometer support is currently limited to the Revburner model. Supports existing revburner xml configuration files. - Tachometer support is currently limited to the Revburner model. Supports existing revburner xml configuration files.
- Includes utility to configure revburner tachometer - Includes utility to configure revburner tachometer
- Can send data to any serial device. So far only tested with arduino. and ESP32. Includes sample arduino sketch for sim lights, simwind, and simhaptic effects for motors. - Can send data to any serial device. So far only tested with arduino. Includes sample arduino sketch for sim lights.
- Support for custom arduino (or any serial) device, with a [custom lua format](https://spacefreak18.github.io/simapi/serial_custom) for sending data - The support for haptic bass shakers is limited and needs the most work. So far the engine rev is a simple sine wave, which I find convincing. The gear shift event works but not convincing enough for me.
- Convincing shaker effects for noise tranducers for wheel slip, wheel lock, and abs, as well as engine rpm and gear shifts.
- Support for many [wheels and pedals](https://spacefreak18.github.io/simapi/thirdpartydevices) including Clubsport Elite V3, [Logitech G29](https://spacefreak18.github.io/simapi/logitechg29), and Moza R5.
## Adding More Devices ## Dependencies
- If a device isn't already supported, feel free to assist in the reverse engineering process by submitting a pcap or even better working code in a pull request!
https://santeri.pikarinen.com/pages/usb_hid_reverse_engineering/
## Quick Install
**One-Line Installation:**
```bash
curl -fsSL https://raw.githubusercontent.com/Spacefreak18/monocoque/master/install.sh | bash
```
***AUR Package following git master:***
```https://aur.archlinux.org/packages/monocoque-git```
**Or download and review first:**
```bash
wget https://raw.githubusercontent.com/Spacefreak18/monocoque/master/install.sh
chmod +x install.sh
./install.sh
```
### Installation on Immutable Distros (Bazzite, Silverblue, etc.)
On immutable distributions where native package managers are unavailable,
monocoque can be installed inside a distrobox container:
```bash
bash tools/distro/distrobox/install-distrobox.sh
```
This creates an Arch Linux container, installs all packages via AUR, and sets up
wrapper scripts (`start-simd`, `start-monocoque`, `test-monocoque`) in `~/.local/bin/`.
To uninstall: `bash tools/distro/distrobox/uninstall-distrobox.sh`
**TUI Manager:**
After installation, use the interactive manager:
```bash
monocoque-manager
```
**Supported Games**
[Supported Sims](https://spacefreak18.github.io/simapi/supportedsims)
***please note on Linux some titles will require a compatibility exe from simshmbridge to be setup. Please follow the linked Documentation
for installation instructions***
## Building
### Dependencies
- libserialport - arduino serial devices - libserialport - arduino serial devices
- hidapi - usb hid devices (hidraw) - hidapi - usb hid devices
- libusb - used by hidapi
- portaudio - sound devices (haptic bass shakers) - portaudio - sound devices (haptic bass shakers)
- libpulse - sound devices (haptic bass shakers) - libenet - UDP support
- libuv base event loop
- libxml2 - libxml2
- argtable2 - argtable2
- libconfig - libconfig
- xdg-basedir - slog (static)
- lua - [wine-linux-shm-adapter](https://github.com/spacefreak18/wine-linux-shm-adapter)
- libproc2
- [slog](https://github.com/kala13x/slog) (static)
- [simshmbridge](https://github.com/spacefreak18/simshmbridge) - for sims that need shared memory mapping like AC and Project Cars related.
- [simapi](https://github.com/spacefreak18/simapi) - [simapi](https://github.com/spacefreak18/simapi)
```
pacman -Syu git cmake pulse-native-provider libxdg-basedir libserialport libconfig libuv argtable hidapi lua libproc2
```
## Building
This code depends on the shared memory data headers in the simapi [repo](https://github.com/spacefreak18/simapi). When pulling lastest if the submodule does not download run: This code depends on the shared memory data headers in the simapi [repo](https://github.com/spacefreak18/simapi). When pulling lastest if the submodule does not download run:
``` ```
git submodule sync --recursive git submodule sync --recursive
git submodule update --init --recursive git submodule update --init --recursive
``` ```
Then to compile simply: Then to compile simply:
``` ```
mkdir build; cd build mkdir build; cd build
cmake .. cmake ..
make make
``` ```
## User Setup Guide
See the dedicated [How To](HOW-TO-USE.md) for detailed instructions to set up and run 'monocoque`
## Testing ## Testing
```
./monocoque test -vv # Make sure that ~/.config/monocque/monocoque.config only contains the devices you have connected.
```
### Logs file location
`~/.cache/monocoque/*.log`
### Static Analysis ### Static Analysis
``` ```
mkdir build; cd build mkdir build; cd build
make clean make clean
cmake -Danalyze=on .. CFLAGS=-fanalyzer cmake ..
make make
``` ```
### Valgrind ### Valgrind
``` ```
cd build cd build
valgrind -v --leak-check=full --show-leak-kinds=all --suppressions=../.valgrindrc ./monocoque play valgrind -v --leak-check=full --show-leak-kinds=all --suppressions=../.valgrindrc ./monocoque play
``` ```
## Join the Discussion
[Sim Racing Matrix Space](https://matrix.to/#/#simracing:matrix.org)
## ToDo ## ToDo
- add frequency cap (low pass filter) to sound haptic effects
- add road and kerb sound haptic effects
- windows port
- more memory testing - more memory testing
- handling null mallocs
- move config code around
- cleanup tests which are basically just copies of the example from their respective projects - cleanup tests which are basically just copies of the example from their respective projects
- much, much more - much, much more

View File

@ -1,29 +0,0 @@
-- start led, end led, color
-- set_led_range_to_color(1, 5, YELLOW);
-- led, color
-- set_led_to_color(3, ORANGE);
-- current colors
-- RED
-- GREEN
-- BLUE
-- YELLOW
-- ORANGE
if simdata.rpm >= simdata.maxrpm-500 then
set_led_range_to_color(1, 6, RED)
elseif simdata.rpm >= 4000 then
set_led_range_to_color(1, 4, YELLOW)
elseif simdata.rpm >= 3000 then
set_led_range_to_color(1, 3, GREEN)
elseif simdata.rpm >= 2000 then
set_led_range_to_rgb_color(1, 2, 0, 255, 0)
-- set_led_to_rgb_color also available
elseif simdata.rpm >= 1000 then
set_led_to_color(1, GREEN)
end

View File

@ -1,60 +0,0 @@
-- configuration
-- set these accordingly
right_leds_start = 7
right_num_leds = 42
left_leds_start = 49
left_num_leds = 42
--
max_radius = 10 -- radius is hardcoded in simapi
proxcars = simdata.proxcars -- also hard coded in simapi
rightsideset = false
leftsideset = false
-- right side
litleds = 0
i = 1
while(i <= proxcars and rightsideset == false) do
if simdata.pd[i].theta > 40 and simdata.pd[i].theta < 130 then
dist = math.abs(90 - simdata.pd[i].theta)
perct = (90 - dist)/90
litleds = math.ceil(perct * right_num_leds)
color_perct = simdata.pd[i].radius/10
yellow = math.floor(color_perct * 255)
local rgb = (255 << 16) | (yellow << 8) | (0 << 0)
if simdata.pd[1].theta >= 90 then
-- car is behind
set_led_range_to_rgb_color(right_leds_start, right_leds_start + litleds, rgb)
else
set_led_range_to_rgb_color(right_leds_start + (right_num_leds - litleds), right_leds_start + right_num_leds, rgb)
end
rightsideset = true
end
i = i + 1
end
-- left side
litleds = 0
i = 1
while(i <= proxcars and leftsideset == false) do
if simdata.pd[i].theta > 230 and simdata.pd[i].theta < 320 then
dist = math.abs(270 - simdata.pd[1].theta)
perct = (90 - dist)/90
litleds = math.ceil(perct * left_num_leds)
color_perct = simdata.pd[i].radius/10
yellow = math.floor(color_perct * 255)
local rgb = (255 << 16) | (yellow << 8) | (0 << 0)
if simdata.pd[1].theta <= 270 then
-- car is behind
set_led_range_to_rgb_color(left_leds_start, left_leds_start + litleds, rgb)
else
set_led_range_to_rgb_color(left_leds_start + (left_num_leds - litleds), left_leds_start + left_num_leds, rgb)
end
leftsideset = true
end
i = i + 1
end

View File

@ -1 +0,0 @@
Message = simdata.velocity .. ";" .. simdata.gear .. ";" .. simdata.rpm .. ";"

View File

@ -1,246 +1,15 @@
configs = (
{ devices = ( { device = "USB";
sim = "default";
// one can also specify sim by using the "short-name" for example "ac". also, "acc", "ace", "ams2", "et", "at" and "rf2"
//sim = "ac";
car = "default";
devices = (
{
device = "USB";
type = "Tachometer"; type = "Tachometer";
devid = "98FD:83AC"; devid = "98FD:83AC";
subtype = "Revburner"; subtype = "Revburner";
granularity = 2; granularity = 4;
config = "~/.config/monocoque/revburner15000.xml"; config = "/home/paul/.config/monocoque/revburner1.xml"; },
}, { device = "Sound";
type = "Shaker"
{ devid = "98FD:83AC";
device = "Sound"; config = "shaker1.config"; },
effect = "Engine"; { device = "Serial";
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71"; type = "ShiftLights";
pan = 3;
fps = 60;
threshold = 0.2;
channels = 8;
volume = 70;
modulation = "frequency";
frequency = 17;
frequencyMax = 37;
noise = 10; // additive noise in Hz
},
{
device = "Sound";
effect = "Gear"
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
pan = 3;
fps = 60;
threshold = 0.2;
channels = 8;
duration = .6;
volume = 100;
frequency = 15;
},
{
device = "Sound";
effect = "TyreSlip";
tyre = "RearLeft";
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
pan = 4;
channels = 8;
threshold = 0.2;
fps = 60;
volume = 100;
modulation = "frequency";
frequency = 30;
frequencyMax = 37;
},
{
device = "Sound";
effect = "TyreSlip";
tyre = "RearRight";
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
pan = 5;
fps = 60;
channels = 8;
threshold = 0.2;
volume = 100;
modulation = "frequency";
frequency = 30;
frequencyMax = 37;
},
{
device = "Sound";
effect = "TyreSlip";
tyre = "FrontLeft";
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
pan = 0;
channels = 8;
threshold = 0.2;
volume = 100;
modulation = "frequency";
frequency = 30;
frequencyMax = 37;
},
{
device = "Sound";
effect = "TyreSlip";
tyre = "FrontRight";
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
pan = 1;
channels = 8;
threshold = 0.2;
volume = 100;
modulation = "frequency";
frequency = 30;
frequencyMax = 37;
},
{
device = "Sound";
effect = "TyreLock";
tyre = "RearLeft";
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
pan = 4;
channels = 8;
threshold = 0.2;
volume = 100;
modulation = "frequency";
frequency = 30;
frequencyMax = 37;
},
{
device = "Sound";
effect = "TyreLock";
tyre = "RearRight";
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
pan = 5;
channels = 8;
threshold = 0.2;
modulation = "frequency";
volume = 100;
frequency = 30;
frequencyMax = 37;
},
{
device = "Sound";
effect = "TyreLock";
tyre = "FrontLeft";
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
pan = 0;
channels = 8;
threshold = 0.2;
volume = 100;
modulation = "frequency";
frequency = 30;
frequencyMax = 37;
},
{
device = "Sound";
effect = "TyreLock";
tyre = "FrontRight";
devid = "alsa_output.pci-0000_0d_00.0.analog-surround-71";
pan = 1;
channels = 8;
threshold = 0.2;
volume = 100;
modulation = "frequency";
frequency = 30;
frequencyMax = 37;
},
{
device = "USB";
type = "Haptic";
subtype = "CSLELITEV3PEDALS";
//devpath = "/sys/module/hid_fanatec/drivers/hid:fanatec/0003:0EB7:183B.0002/rumble";
devpath = "/sys/module/hid_fanatec/drivers/hid:ftec_csl_elite/0003:0EB7:183B.0002/rumble";
effect = "ABS";
tyre = "ALL";
threshold = 0.2;
value0 = 0;
value1 = 16776960;
},
{
device = "USB";
type = "Haptic";
subtype = "CSLELITEV3PEDALS";
//devpath = "/sys/module/hid_fanatec/drivers/hid:fanatec/0003:0EB7:183B.0002/rumble";
devpath = "/sys/module/hid_fanatec/drivers/hid:ftec_csl_elite/0003:0EB7:183B.0002/rumble";
effect = "Slip";
tyre = "ALL";
threshold = 0.2;
value0 = 0;
value1 = 16711680;
},
{
device = "USB";
type = "Haptic";
subtype = "CSLELITEV3PEDALS";
//devpath = "/sys/module/hid_fanatec/drivers/hid:fanatec/0003:0EB7:183B.0002/rumble";
devpath = "/sys/module/hid_fanatec/drivers/hid:ftec_csl_elite/0003:0EB7:183B.0002/rumble";
effect = "Lock";
tyre = "ALL";
threshold = 0.2;
value0 = 0;
value1 = 65280;
},
{
device = "Serial";
type = "Haptic";
effect = "TyreSlip";
tyre = "ALL"; // motor m1 connected to throttle
motors = 0;
devpath = "/dev/ttyACM0";
},
{
device = "Serial";
type = "Haptic";
effect = "TyreLock";
tyre = "ALL"; // motor m3 connected to brake;
motors = 2;
devpath = "/dev/ttyACM0";
},
{
device = "Serial";
type = "Haptic"
effect = "ABS";
tyre = "ALL"; // this will have to be documented, this tells the code to spin both motors
motors = 8;
devpath = "/dev/ttyACM0";
},
{
device = "Serial";
type = "SimWind";
config = "None"; config = "None";
baud = 115200; devpath = "/dev/ttyACM0"; } );
devpath = "/dev/simdev1";
fanpower = 0.6;
},
{
device = "Serial";
type = "SimWind";
config = "None";
baud = 115200;
devpath = "/dev/simdev1";
fanpower = 0.6;
},
{
device = "Serial";
type = "Simleds";
numleds = 6;
startled = 2;
endled = 5;
config = "~/git/monocoque/conf/rpms_and_radar.lua"
baud = 115200;
devpath = "/dev/simdev0";
});
}
);

View File

@ -1,48 +0,0 @@
-- from simapi.h
-- SIMAPI_FLAG_GREEN = 0,
-- SIMAPI_FLAG_YELLOW = 1,
-- SIMAPI_FLAG_RED = 2,
-- SIMAPI_FLAG_CHEQUERED = 3,
-- SIMAPI_FLAG_BLUE = 4,
-- SIMAPI_FLAG_WHITE = 5,
-- SIMAPI_FLAG_BLACK = 6,
-- SIMAPI_FLAG_BLACK_WHITE = 7,
-- SIMAPI_FLAG_BLACK_ORANGE = 8,
-- SIMAPI_FLAG_ORANGE = 9
led_clear_all()
if simdata.rpm > 0 and simdata.maxrpm > 0 then
rpmmargin = .05*simdata.maxrpm;
rpminterval = (simdata.maxrpm-rpmmargin) / 14;
litleds = 0
for i = 1,14 do
if simdata.rpm >= (rpminterval * i) then
litleds = i;
end
end
color = GREEN
if litleds > 5 and litleds < 12 then
color = YELLOW
end
if litleds >= 12 then
color = RED
end
set_led_range_to_color(59, 59+litleds, color)
end
if simdata.playerflag == 0 then
set_led_range_to_color(4, 7, GREEN)
end
if simdata.playerflag == 1 then
set_led_range_to_color(4, 7, YELLOW)
end
if simdata.playerflag == 4 then
set_led_range_to_color(4, 7, BLUE)
end

View File

@ -1,55 +0,0 @@
-- from simapi.h
-- SIMAPI_FLAG_GREEN = 0,
-- SIMAPI_FLAG_YELLOW = 1,
-- SIMAPI_FLAG_RED = 2,
-- SIMAPI_FLAG_CHEQUERED = 3,
-- SIMAPI_FLAG_BLUE = 4,
-- SIMAPI_FLAG_WHITE = 5,
-- SIMAPI_FLAG_BLACK = 6,
-- SIMAPI_FLAG_BLACK_WHITE = 7,
-- SIMAPI_FLAG_BLACK_ORANGE = 8,
-- SIMAPI_FLAG_ORANGE = 9
if simdata.rpm > 0 and simdata.maxrpm > 0 then
rpmmargin = .05*simdata.maxrpm;
rpminterval = (simdata.maxrpm-rpmmargin) / 6;
litleds = 0
for i = 1,6 do
if simdata.rpm >= (rpminterval * i) then
litleds = i;
end
end
color = GREEN
if litleds > 3 and litleds < 6 then
color = YELLOW
end
if litleds >= 6 then
color = RED
end
if litleds >= 6 then
if simdata.mtick % 2 == 0 then
set_led_range_to_color(1, litleds, color)
else
led_clear_all()
end
else
set_led_range_to_color(1, litleds, color)
end
end
if simdata.playerflag == 0 then
set_led_range_to_color(7, 48, GREEN)
end
if simdata.playerflag == 1 then
set_led_range_to_color(7, 48, YELLOW)
end
if simdata.playerflag == 4 then
set_led_range_to_color(7, 48, BLUE)
end

View File

@ -1,91 +0,0 @@
-- configuration
-- set these accordingly
right_leds_start = 7
right_num_leds = 42
left_leds_start = 49
left_num_leds = 42
--
max_radius = 10 -- radius is hardcoded in simapi
proxcars = simdata.proxcars -- also hard coded in simapi
rightsideset = false
leftsideset = false
-- right side
litleds = 0
i = 1
while(i <= proxcars and rightsideset == false) do
if simdata.pd[i].theta > 40 and simdata.pd[i].theta < 130 then
dist = math.abs(90 - simdata.pd[i].theta)
perct = (90 - dist)/90
litleds = math.ceil(perct * right_num_leds)
color_perct = simdata.pd[i].radius/10
yellow = math.floor(color_perct * 255)
local rgb = (255 << 16) | (yellow << 8) | (0 << 0)
if simdata.pd[1].theta >= 90 then
-- car is behind
set_led_range_to_rgb_color(right_leds_start, right_leds_start + litleds, rgb)
else
set_led_range_to_rgb_color(right_leds_start + (right_num_leds - litleds), right_leds_start + right_num_leds, rgb)
end
rightsideset = true
end
i = i + 1
end
-- left side
litleds = 0
i = 1
while(i <= proxcars and leftsideset == false) do
if simdata.pd[i].theta > 230 and simdata.pd[i].theta < 320 then
dist = math.abs(270 - simdata.pd[1].theta)
perct = (90 - dist)/90
litleds = math.ceil(perct * left_num_leds)
color_perct = simdata.pd[i].radius/10
yellow = math.floor(color_perct * 255)
local rgb = (255 << 16) | (yellow << 8) | (0 << 0)
if simdata.pd[1].theta <= 270 then
-- car is behind
set_led_range_to_rgb_color(left_leds_start, left_leds_start + litleds, rgb)
else
set_led_range_to_rgb_color(left_leds_start + (left_num_leds - litleds), left_leds_start + left_num_leds, rgb)
end
leftsideset = true
end
i = i + 1
end
rpm_leds = 6
-- rpm stuff
if simdata.rpm > 0 and simdata.maxrpm > 0 then
rpmmargin = .05*simdata.maxrpm;
rpminterval = (simdata.maxrpm-rpmmargin) / rpm_leds;
litleds = 0
for i = 1,6 do
if simdata.rpm >= (rpminterval * i) then
litleds = i;
end
end
color = GREEN
if litleds > rpm_leds/2 and litleds < rpm_leds then
color = YELLOW
end
if litleds >= rpm_leds then
color = RED
end
if litleds >= rpm_leds then
if simdata.mtick % 2 == 0 then
set_led_range_to_color(1, litleds, color)
else
led_clear_range(1, rpm_leds)
end
else
set_led_range_to_color(1, litleds, color)
end
end

View File

@ -1,554 +0,0 @@
#!/bin/bash
set -e
# Monocoque Universal Installer
# Works on: Arch, Debian/Ubuntu, Fedora, and other major distros
# Version: 1.0.0
SCRIPT_VERSION="1.0.0"
INSTALL_DIR="${MONOCOQUE_INSTALL_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/monocoque}"
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}"
BIN_DIR="${HOME}/.local/bin"
# Color output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_header() {
echo ""
echo "╔══════════════════════════════════════════════════════════════════╗"
echo "║ Monocoque Universal Installer v${SCRIPT_VERSION}"
echo "╚══════════════════════════════════════════════════════════════════╝"
echo ""
}
# Detect distribution
detect_distro() {
if [ -f /etc/os-release ]; then
. /etc/os-release
DISTRO=$ID
DISTRO_VERSION=$VERSION_ID
DISTRO_LIKE=$ID_LIKE
elif [ -f /etc/arch-release ]; then
DISTRO="arch"
else
DISTRO="unknown"
fi
# Normalize Arch-based distributions
if [[ "$DISTRO_LIKE" == *"arch"* ]] || [[ "$DISTRO" == "manjaro" ]] || [[ "$DISTRO" == "garuda" ]] || [[ "$DISTRO" == "endeavouros" ]]; then
log_info "Detected distribution: $DISTRO (Arch-based)"
DISTRO="arch"
else
log_info "Detected distribution: $DISTRO"
fi
}
# Check if running as root
check_root() {
if [ "$EUID" -eq 0 ]; then
log_error "Please do not run this script as root"
exit 1
fi
}
# Check for required commands
check_requirements() {
local missing_commands=()
for cmd in git cmake make gcc; do
if ! command -v $cmd &> /dev/null; then
missing_commands+=($cmd)
fi
done
if [ ${#missing_commands[@]} -ne 0 ]; then
log_error "Missing required commands: ${missing_commands[*]}"
log_info "Please install them first"
exit 1
fi
}
# Install dependencies based on distro
install_dependencies() {
log_info "Installing dependencies for $DISTRO..."
case $DISTRO in
arch|manjaro)
local deps="yder libuv argtable libserialport libconfig hidapi lua54 libpulse pkgconfig libxdg-basedir mingw-w64-gcc"
# Check if yay is available for AUR
if command -v yay &> /dev/null; then
log_info "Using yay to check for AUR packages..."
echo ""
echo "You can install monocoque from AUR instead:"
echo " yay -S simapi-git simd-git monocoque-git"
echo ""
read -p "Install from AUR? (recommended) [Y/n]: " use_aur
if [[ ! $use_aur =~ ^[Nn]$ ]]; then
log_info "Installing from AUR..."
yay -S --needed simapi-git simd-git monocoque-git
# Still need simshmbridge
log_info "Building simshmbridge (not in AUR)..."
build_simshmbridge_only
# Setup configs and services
setup_configs
setup_systemd_services
# create_launcher_scripts uses SCRIPT_DIR which is set in main()
create_launcher_scripts
log_success "Installation complete (AUR method)!"
print_next_steps
exit 0
fi
fi
log_info "Installing build dependencies with pacman..."
sudo pacman -S --needed --noconfirm $deps
;;
ubuntu|debian|linuxmint|pop)
local deps="build-essential git cmake libyder-dev libuv1-dev libargtable2-dev libserialport-dev libconfig-dev libhidapi-dev lua5.3 liblua5.3-dev libxdg-basedir-dev mingw-w64"
log_info "Updating package lists..."
sudo apt-get update
log_info "Installing build dependencies..."
sudo apt-get install -y $deps
;;
fedora|rhel|centos)
local deps="git cmake gcc gcc-c++ yder-devel libuv-devel argtable-devel libserialport-devel libconfig-devel hidapi-devel lua-devel libxdg-basedir-devel mingw64-gcc"
log_info "Installing build dependencies..."
sudo dnf install -y $deps
;;
opensuse*)
local deps="git cmake gcc gcc-c++ libyder-devel libuv-devel argtable-devel libserialport-devel libconfig-devel hidapi-devel lua-devel libxdg-basedir-devel mingw64-gcc"
log_info "Installing build dependencies..."
sudo zypper install -y $deps
;;
*)
log_error "Unsupported distribution: $DISTRO"
log_info "Please install dependencies manually and try again"
log_info "Required: git, cmake, gcc, yder, libuv, argtable, libserialport, libconfig, hidapi, lua, libxdg-basedir, mingw-gcc"
exit 1
;;
esac
log_success "Dependencies installed"
}
# Clone repositories
clone_repositories() {
log_info "Creating installation directory: $INSTALL_DIR"
mkdir -p "$INSTALL_DIR"
cd "$INSTALL_DIR"
# Clone monocoque
if [ ! -d "monocoque" ]; then
log_info "Cloning monocoque..."
git clone https://github.com/Spacefreak18/monocoque.git
cd monocoque
git submodule sync --recursive
git submodule update --init --recursive
else
log_info "monocoque already cloned, updating..."
cd monocoque
git pull
git submodule sync --recursive
git submodule update --init --recursive
fi
cd "$INSTALL_DIR"
# Clone simapi
if [ ! -d "simapi" ]; then
log_info "Cloning simapi..."
git clone https://github.com/Spacefreak18/simapi.git
else
log_info "simapi already cloned, updating..."
cd simapi
git pull
fi
cd "$INSTALL_DIR"
# Clone simshmbridge
if [ ! -d "simshmbridge" ]; then
log_info "Cloning simshmbridge..."
git clone https://github.com/spacefreak18/simshmbridge.git
cd simshmbridge
git submodule sync --recursive
git submodule update --init --recursive
else
log_info "simshmbridge already cloned, updating..."
cd simshmbridge
git pull
git submodule sync --recursive
git submodule update --init --recursive
fi
log_success "Repositories cloned/updated"
}
# Build simapi
build_simapi() {
log_info "Building simapi..."
cd "$INSTALL_DIR/simapi"
mkdir -p build
cd build
cmake -DINSTALL_SYSTEMD_SERVICE=off ..
make -j$(nproc)
log_info "Installing simapi library..."
sudo make install
# Update library cache
sudo ldconfig 2>/dev/null || true
log_success "simapi built and installed"
}
# Build simd
build_simd() {
log_info "Building simd..."
cd "$INSTALL_DIR/simapi/simd"
rm -rf build
mkdir -p build
cd build
cmake ..
make -j$(nproc)
log_success "simd built"
}
# Build simshmbridge
build_simshmbridge() {
log_info "Building simshmbridge..."
cd "$INSTALL_DIR/simshmbridge"
make clean || true
make -j$(nproc)
log_success "simshmbridge built"
}
build_simshmbridge_only() {
mkdir -p "$INSTALL_DIR"
cd "$INSTALL_DIR"
if [ ! -d "simshmbridge" ]; then
log_info "Cloning simshmbridge..."
git clone https://github.com/spacefreak18/simshmbridge.git
cd simshmbridge
git submodule sync --recursive
git submodule update --init --recursive
fi
build_simshmbridge
}
# Build monocoque
build_monocoque() {
log_info "Building monocoque..."
cd "$INSTALL_DIR/monocoque"
mkdir -p build
cd build
cmake ..
make -j$(nproc)
log_success "monocoque built"
}
# Setup configuration files
setup_configs() {
log_info "Setting up configuration files..."
# simd config
mkdir -p "$CONFIG_DIR/simd"
if [ ! -f "$CONFIG_DIR/simd/simd.config" ]; then
if [ -f "$INSTALL_DIR/simapi/simd/conf/simd.config" ]; then
cp "$INSTALL_DIR/simapi/simd/conf/simd.config" "$CONFIG_DIR/simd/simd.config"
log_success "Created simd config"
fi
else
log_info "simd config already exists, skipping"
fi
# monocoque config
mkdir -p "$CONFIG_DIR/monocoque"
if [ ! -f "$CONFIG_DIR/monocoque/monocoque.config" ]; then
cat > "$CONFIG_DIR/monocoque/monocoque.config" << 'EOF'
configs = (
{
sim = "default";
car = "default";
devices = (
// Add your devices here
// Example: Serial device (ESP32/Arduino)
/*
{
device = "Serial";
type = "Custom";
config = "None";
baud = 115200;
devpath = "/dev/ttyUSB0";
},
*/
// Example: Bass shaker
/*
{
device = "Sound";
effect = "Engine";
devid = "alsa_output.your_device_here";
pan = 0;
fps = 60;
threshold = 0.2;
channels = 2;
volume = 70;
modulation = "frequency";
frequency = 17;
frequencyMax = 37;
},
*/
);
}
);
EOF
log_success "Created monocoque config"
else
log_info "monocoque config already exists, skipping"
fi
}
# Create launcher scripts
create_launcher_scripts() {
log_info "Creating launcher scripts in $BIN_DIR..."
mkdir -p "$BIN_DIR"
# Find simd binary location
local SIMD_BIN=""
if [ -f "$INSTALL_DIR/simapi/simd/build/simd" ]; then
SIMD_BIN="$INSTALL_DIR/simapi/simd/build/simd"
elif command -v simd &> /dev/null; then
SIMD_BIN=$(which simd)
fi
# Find monocoque binary location
local MONOCOQUE_BIN=""
if [ -f "$INSTALL_DIR/monocoque/build/monocoque" ]; then
MONOCOQUE_BIN="$INSTALL_DIR/monocoque/build/monocoque"
elif command -v monocoque &> /dev/null; then
MONOCOQUE_BIN=$(which monocoque)
fi
# start-simd script
cat > "$BIN_DIR/start-simd" << EOF
#!/bin/bash
export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/lib64
exec $SIMD_BIN -vv "\$@"
EOF
chmod +x "$BIN_DIR/start-simd"
# start-monocoque script
cat > "$BIN_DIR/start-monocoque" << EOF
#!/bin/bash
exec $MONOCOQUE_BIN play "\$@"
EOF
chmod +x "$BIN_DIR/start-monocoque"
# test-monocoque script
cat > "$BIN_DIR/test-monocoque" << EOF
#!/bin/bash
exec $MONOCOQUE_BIN test -vv "\$@"
EOF
chmod +x "$BIN_DIR/test-monocoque"
# monocoque-manager TUI
log_info "Looking for monocoque-manager to install..."
local MANAGER_INSTALLED=false
# Use the SCRIPT_DIR exported from main()
# Try multiple locations
local SEARCH_PATHS=(
"$SCRIPT_DIR/monocoque-manager"
"$SCRIPT_DIR/tools/monocoque-manager"
)
for MANAGER_PATH in "${SEARCH_PATHS[@]}"; do
log_info "Checking: $MANAGER_PATH"
if [ -f "$MANAGER_PATH" ]; then
log_info "Found monocoque-manager at: $MANAGER_PATH"
cp "$MANAGER_PATH" "$BIN_DIR/monocoque-manager"
chmod +x "$BIN_DIR/monocoque-manager"
log_success "✓ Installed monocoque-manager TUI to $BIN_DIR/monocoque-manager"
MANAGER_INSTALLED=true
break
fi
done
if [ "$MANAGER_INSTALLED" = false ]; then
log_error "monocoque-manager not found!"
log_warn "Searched in: ${SEARCH_PATHS[*]}"
log_warn "SCRIPT_DIR was: $SCRIPT_DIR"
log_info "Please copy it manually: cp $SCRIPT_DIR/monocoque-manager ~/.local/bin/"
fi
# Check if BIN_DIR is in PATH
if [[ ":$PATH:" != *":$BIN_DIR:"* ]]; then
log_warn "$BIN_DIR is not in your PATH"
log_info "Add this to your shell config:"
echo " # For bash/zsh:"
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
echo ""
echo " # For fish:"
echo " fish_add_path ~/.local/bin"
fi
log_success "Launcher scripts created"
}
# Setup systemd user services
setup_systemd_services() {
local SYSTEMD_DIR="$HOME/.config/systemd/user"
mkdir -p "$SYSTEMD_DIR"
log_info "Creating systemd service files..."
# Find binary locations
local SIMD_BIN=""
if [ -f "$INSTALL_DIR/simapi/simd/build/simd" ]; then
SIMD_BIN="$INSTALL_DIR/simapi/simd/build/simd"
elif command -v simd &> /dev/null; then
SIMD_BIN=$(which simd)
fi
# simd service
cat > "$SYSTEMD_DIR/simd.service" << EOF
[Unit]
Description=Sim Telemetry Daemon
Documentation=https://spacefreak18.github.io/simapi/
After=network.target
[Service]
Type=simple
Environment="LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64"
ExecStart=$SIMD_BIN
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
EOF
log_success "systemd services created"
log_info "To enable auto-start on boot:"
echo " systemctl --user enable simd.service"
echo " systemctl --user start simd.service"
}
# Print next steps
print_next_steps() {
echo ""
echo "╔══════════════════════════════════════════════════════════════════╗"
echo "║ Installation Complete! ║"
echo "╚══════════════════════════════════════════════════════════════════╝"
echo ""
log_success "Monocoque and all components are installed!"
echo ""
echo "📁 Installation location: $INSTALL_DIR"
echo "⚙️ Configuration: $CONFIG_DIR/simd and $CONFIG_DIR/monocoque"
echo "🚀 Launcher scripts: $BIN_DIR"
echo ""
echo "Next steps:"
echo ""
echo "1⃣ Configure your games (REQUIRED!)"
echo " See: https://github.com/Spacefreak18/monocoque/blob/master/HOW-TO-USE.md"
echo ""
echo "2⃣ Start the services:"
echo " Terminal 1: start-simd"
echo " Terminal 2: start-monocoque"
echo ""
echo " OR enable auto-start:"
echo " systemctl --user enable --now simd.service"
echo ""
echo "3⃣ Configure your devices:"
echo " Edit: $CONFIG_DIR/monocoque/monocoque.config"
echo ""
echo "4⃣ Test your setup:"
echo " test-monocoque"
echo ""
echo "🎮 Supported games:"
echo " • Assetto Corsa / ACC"
echo " • Automobilista 2"
echo " • Project Cars 2"
echo " • RFactor 2"
echo " • Euro/American Truck Simulator"
echo ""
echo "📚 Documentation: https://spacefreak18.github.io/simapi/"
echo ""
}
# Main installation flow
main() {
# Save the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
export SCRIPT_DIR
print_header
check_root
detect_distro
check_requirements
log_info "Starting installation..."
echo ""
install_dependencies
clone_repositories
log_info "Building components (this may take a few minutes)..."
build_simapi
build_simd
build_monocoque
build_simshmbridge
setup_configs
create_launcher_scripts
setup_systemd_services
print_next_steps
}
# Run main function
main "$@"

View File

@ -1,166 +0,0 @@
# Makefile for Arduino based scketches
#
# Copyright 2020 Valerio Di Giampietro http://va.ler.io v@ler.io
# MIT License - see License.txt file
#
# This Makefile uses the arduino-cli, the Arduino command line interface
# and has been designed and tested to run on Linux, not on Windows.
# Probably it will run on a Mac, but it has not been tested.
#
# Please note that:
#
# 1. each sketch must reside in his own folder with this Makefile
#
# 2. the main make targets are:
# - all compiles and upload
# - compile compiles only
# - upload upload via serial port, compile if the binary file is
# not available
# - ota upload Over The Air, automatically find the device
# IP address using the IOT_NAME (device hostname)
# - clean clean the build directory
# - find find OTA updatable devices on the local subnet
# - requirements it the file "requirements.txt" exists it will
# install the libraries listed in this file
#
# default is "all"
#
# 3. it gets the name of the sketch using the wildcard make command;
# the name is *.ino; this means that you must have ONLY a file
# with .ino extension, otherwise this makefile will break. This
# also means that you can use this Makefile, almost unmodified,
# for any sketch as long as you keep a single .ino file in each
# folder
#
# 4. you can split your project in multiple files, if you wish,
# using a single .ino file and multiple .h files, that you can
# include in the .ino file with an '#include "myfile.h"'
# directive
#
# Optionally some environment variables can be set:
#
# FQBN Fully Qualified Board Name; if not set in the environment
# it will be assigned a value in this makefile
#
# SERIAL_DEV Serial device to upload the sketch; if not set in the
# environment it will be assigned:
# /dev/ttyUSB0 if it exists, or
# /dev/ttyACM0 if it exists, or
# unknown
#
# IOT_NAME Name of the IOT device; if not set in the environment
# it will be assigned a value in this makefile. This is
# very useful for OTA update, the device will be searched
# on the local subnet using this name
#
# OTA_PORT Port used by OTA update; if not set in the environment
# it will be assigned the default value of 8266 in this
# makefile
#
# OTA_PASS Password used for OTA update; if not set in the environment
# it will be assigned the default value of an empty string
#
# V verbose flag; can be 0 (quiet) or 1 (verbose); if not set
# in the environment it will be assigned a default value
# in this makefile
MAKE_DIR := $(PWD)
#
# ----- setup wor Wemos D1 mini -----
#FQBN ?= esp8266:esp8266:d1_mini
#IOT_NAME ?= esp8266-meteo
#OTA_PORT ?= 8266
#OTA_PASS ?=
# ----- setup for Arduino Uno
FQBN ?= arduino:avr:uno
# ----- ---------------------
V ?= 0
VFLAG =
ifeq "$(V)" "1"
VFLAG =-v
endif
ifndef SERIAL_DEV
ifneq (,$(wildcard /dev/ttyUSB0))
SERIAL_DEV = /dev/ttyUSB0
else ifneq (,$(wildcard /dev/ttyACM0))
SERIAL_DEV = /dev/ttyACM0
else
SERIAL_DEV = unknown
endif
endif
BUILD_DIR := $(subst :,.,build/$(FQBN))
SRC := $(wildcard *.ino)
HDRS := $(wildcard *.h)
BIN := $(BUILD_DIR)/$(SRC).bin
ELF := $(BUILD_DIR)/$(SRC).elf
$(info FQBN is [${FQBN}])
$(info IOT_NAME is [${IOT_NAME}])
$(info OTA_PORT is [${OTA_PORT}])
$(info OTA_PASS is [${OTA_PASS}])
$(info V is [${V}])
$(info VFLAG is [${VFLAG}])
$(info MAKE_DIR is [${MAKE_DIR}])
$(info BUILD_DIR is [${BUILD_DIR}])
$(info SRC is [${SRC}])
$(info HDRS is [${HDRS}])
$(info BIN is [${BIN}])
$(info SERIAL_DEV is [${SERIAL_DEV}])
all: $(ELF) upload
.PHONY: all
compile: $(ELF)
.PHONY: compile
$(ELF): $(SRC) $(HDRS)
arduino-cli compile -b $(FQBN) $(VFLAG)
@if which arduino-manifest.pl; \
then echo "---> Generating manifest.txt"; \
arduino-manifest.pl -b $(FQBN) $(SRC) $(HDRS) > manifest.txt; \
else echo "---> If you want to generate manifest.txt, listing used libraries and their versions,"; \
echo "---> please install arduino-manifest, see https://github.com/digiampietro/arduino-manifest"; \
fi
upload:
@if [ ! -c $(SERIAL_DEV) ] ; \
then echo "---> ERROR: Serial Device not available, please set the SERIAL_DEV environment variable" ; \
else echo "---> Uploading sketch\n"; \
arduino-cli upload -b $(FQBN) -p $(SERIAL_DEV) $(VFLAG); \
fi
ota:
@PLAT_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.platform.path' | awk -F= '{print $$2}'` ; \
PY_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.tools.python3.path' | awk -F= '{print $$2}'` ; \
IOT_IP=`avahi-browse _arduino._tcp --resolve --parsable --terminate|grep -i ';$(IOT_NAME);'|grep ';$(OTA_PORT);'| awk -F\; '{print $$8}'|head -1`; \
BINFILE=$(wildcard $(BUILD_DIR)/$(SRC)*bin); \
echo "PLAT_PATH is [$$PLAT_PATH]" ; \
echo "PY_PATH: is [$$PY_PATH]" ; \
echo "IOT_IP: is [$$IOT_IP]" ; \
echo "BINFILE: is [$$BINFILE]" ; \
if [ "$$IOT_IP" = "" ] ; \
then echo "Unable to find device IP. Check that the IOT_NAME environment variable is correctly set. Use 'make find' to search devices"; \
else echo "---> Uploading Over The Air"; \
$$PY_PATH/python3 $$PLAT_PATH/tools/espota.py -i $$IOT_IP -p $(OTA_PORT) --auth=$(OTA_PASS) -f $$BINFILE ;\
fi
clean:
@echo "---> Cleaning the build directory"
rm -rf build
find:
avahi-browse _arduino._tcp --resolve --parsable --terminate
requirements:
@if [ -e requirements.txt ]; \
then while read -r i ; do echo ; \
echo "---> Installing " '"'$$i'"' ; \
arduino-cli lib install "$$i" ; \
done < requirements.txt ; \
else echo "---> MISSING requirements.txt file"; \
fi

View File

@ -1,30 +0,0 @@
// Called when starting the arduino (setup method in main sketch)
void setup() {
Serial.begin(115200);
}
// Called when new data is coming from computer
void read()
{
if(Serial.available())
{
int speed = Serial.readStringUntil(';').toInt();
int gear = Serial.readStringUntil(';').toInt();
int rpms = Serial.readStringUntil(';').toInt();
//Serial.println("speed: " + String(speed) + " " + "gear: " + String(gear) + " " + "rpms: " + String(rpms));
}
}
// Called once per arduino loop, timing can't be predicted,
// but it's called between each command sent to the arduino
void loop() {
read();
}
// Called once between each byte read on arduino,
// THIS IS A CRITICAL PATH :
// AVOID ANY TIME CONSUMING ROUTINES !!!
// PREFER READ OR LOOP METHOS AS MUCH AS POSSIBLE
// AVOID ANY INTERRUPTS DISABLE (serial data would be lost!!!)
void idle() {
}

View File

@ -0,0 +1,95 @@
#include <FastLED.h>
#include "../../monocoque/simulatorapi/simdata.h"
#define BYTE_SIZE sizeof(SimData)
#define LED_PIN 7
#define NUM_LEDS 6
#define BRIGHTNESS 40
CRGB leds[NUM_LEDS];
SimData sd;
int maxrpm = 0;
int rpm = 0;
int numlights = NUM_LEDS;
int pin = LED_PIN;
int lights[6];
void setup()
{
Serial.begin(9600);
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);
FastLED.setBrightness(BRIGHTNESS);
for (int i = 0; i < numlights; i++)
{
leds[i] = CRGB ( 0, 0, 0);
lights[i] = 0;
}
FastLED.clear();
sd.rpms = 0;
sd.maxrpm = 6500;
sd.altitude = 10;
sd.pulses = 40000;
sd.velocity = 10;
}
void loop()
{
int l = 0;
char buff[BYTE_SIZE];
if (Serial.available() >= BYTE_SIZE)
{
Serial.readBytes(buff, BYTE_SIZE);
memcpy(&sd, &buff, BYTE_SIZE);
rpm = sd.rpms;
maxrpm = sd.maxrpm;
}
while (l < numlights)
{
lights[l] = 0;
l++;
}
l = -1;
int rpmlights = 0;
while (rpm > rpmlights)
{
if (l>=0)
{
lights[l] = 1;
}
l++;
rpmlights = rpmlights + (((maxrpm-250)/numlights));
}
l = 0;
FastLED.clear();
while (l < numlights)
{
if (l >= numlights / 2)
{
leds[l] = CRGB ( 0, 0, 255);
}
if (l < numlights / 2)
{
leds[l] = CRGB ( 0, 255, 0);
}
if (l == numlights - 1)
{
leds[l] = CRGB ( 255, 0, 0);
}
if (lights[l] <= 0)
{
leds[l] = CRGB ( 0, 0, 0);
}
FastLED.show();
l++;
}
}

View File

@ -0,0 +1,93 @@
#include <FastLED.h>
#include "../../monocoque/simulatorapi/simdata.h"
#define BYTE_SIZE sizeof(SimData)
#define LED_PIN 7
#define NUM_LEDS 6
CRGB leds[NUM_LEDS];
SimData sd;
int maxrpm = 0;
int rpm = 0;
int numlights = NUM_LEDS;
int pin = LED_PIN;
int lights[6];
void setup() {
Serial.begin(9600);
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);
FastLED.setBrightness(40);
for (int i = 0; i < numlights; i++)
{
leds[i] = CRGB ( 0, 0, 0);
lights[i] = 0;
}
FastLED.clear();
sd.rpms = 0;
sd.maxrpm = 6500;
sd.altitude = 10;
sd.pulses = 40000;
sd.velocity = 10;
}
void loop() {
int l = 0;
char buff[BYTE_SIZE];
if (Serial.available() >= BYTE_SIZE) {
Serial.readBytes(buff, BYTE_SIZE);
memcpy(&sd, &buff, BYTE_SIZE);
rpm = sd.rpms;
maxrpm = sd.maxrpm;
}
while (l < numlights)
{
lights[l] = 0;
l++;
}
l = -1;
int rpmlights = 0;
while (rpm > rpmlights)
{
if (l>=0)
{
lights[l] = 1;
}
l++;
rpmlights = rpmlights + (((maxrpm-250)/numlights));
}
l = 0;
FastLED.clear();
while (l < numlights)
{
if (l >= numlights / 2)
{
leds[l] = CRGB ( 0, 0, 255);
}
if (l < numlights / 2)
{
leds[l] = CRGB ( 0, 255, 0);
}
if (l == numlights - 1)
{
leds[l] = CRGB ( 255, 0, 0);
}
if (lights[l] <= 0)
{
leds[l] = CRGB ( 0, 0, 0);
}
FastLED.show();
l++;
}
}

View File

@ -1,166 +0,0 @@
# Makefile for Arduino based scketches
#
# Copyright 2020 Valerio Di Giampietro http://va.ler.io v@ler.io
# MIT License - see License.txt file
#
# This Makefile uses the arduino-cli, the Arduino command line interface
# and has been designed and tested to run on Linux, not on Windows.
# Probably it will run on a Mac, but it has not been tested.
#
# Please note that:
#
# 1. each sketch must reside in his own folder with this Makefile
#
# 2. the main make targets are:
# - all compiles and upload
# - compile compiles only
# - upload upload via serial port, compile if the binary file is
# not available
# - ota upload Over The Air, automatically find the device
# IP address using the IOT_NAME (device hostname)
# - clean clean the build directory
# - find find OTA updatable devices on the local subnet
# - requirements it the file "requirements.txt" exists it will
# install the libraries listed in this file
#
# default is "all"
#
# 3. it gets the name of the sketch using the wildcard make command;
# the name is *.ino; this means that you must have ONLY a file
# with .ino extension, otherwise this makefile will break. This
# also means that you can use this Makefile, almost unmodified,
# for any sketch as long as you keep a single .ino file in each
# folder
#
# 4. you can split your project in multiple files, if you wish,
# using a single .ino file and multiple .h files, that you can
# include in the .ino file with an '#include "myfile.h"'
# directive
#
# Optionally some environment variables can be set:
#
# FQBN Fully Qualified Board Name; if not set in the environment
# it will be assigned a value in this makefile
#
# SERIAL_DEV Serial device to upload the sketch; if not set in the
# environment it will be assigned:
# /dev/ttyUSB0 if it exists, or
# /dev/ttyACM0 if it exists, or
# unknown
#
# IOT_NAME Name of the IOT device; if not set in the environment
# it will be assigned a value in this makefile. This is
# very useful for OTA update, the device will be searched
# on the local subnet using this name
#
# OTA_PORT Port used by OTA update; if not set in the environment
# it will be assigned the default value of 8266 in this
# makefile
#
# OTA_PASS Password used for OTA update; if not set in the environment
# it will be assigned the default value of an empty string
#
# V verbose flag; can be 0 (quiet) or 1 (verbose); if not set
# in the environment it will be assigned a default value
# in this makefile
MAKE_DIR := $(PWD)
#
# ----- setup wor Wemos D1 mini -----
#FQBN ?= esp8266:esp8266:d1_mini
#IOT_NAME ?= esp8266-meteo
#OTA_PORT ?= 8266
#OTA_PASS ?=
# ----- setup for Arduino Uno
FQBN ?= arduino:avr:uno
# ----- ---------------------
V ?= 0
VFLAG =
ifeq "$(V)" "1"
VFLAG =-v
endif
ifndef SERIAL_DEV
ifneq (,$(wildcard /dev/ttyUSB0))
SERIAL_DEV = /dev/ttyUSB0
else ifneq (,$(wildcard /dev/ttyACM0))
SERIAL_DEV = /dev/ttyACM0
else
SERIAL_DEV = unknown
endif
endif
BUILD_DIR := $(subst :,.,build/$(FQBN))
SRC := $(wildcard *.ino)
HDRS := $(wildcard *.h)
BIN := $(BUILD_DIR)/$(SRC).bin
ELF := $(BUILD_DIR)/$(SRC).elf
$(info FQBN is [${FQBN}])
$(info IOT_NAME is [${IOT_NAME}])
$(info OTA_PORT is [${OTA_PORT}])
$(info OTA_PASS is [${OTA_PASS}])
$(info V is [${V}])
$(info VFLAG is [${VFLAG}])
$(info MAKE_DIR is [${MAKE_DIR}])
$(info BUILD_DIR is [${BUILD_DIR}])
$(info SRC is [${SRC}])
$(info HDRS is [${HDRS}])
$(info BIN is [${BIN}])
$(info SERIAL_DEV is [${SERIAL_DEV}])
all: $(ELF) upload
.PHONY: all
compile: $(ELF)
.PHONY: compile
$(ELF): $(SRC) $(HDRS)
arduino-cli compile -b $(FQBN) $(VFLAG)
@if which arduino-manifest.pl; \
then echo "---> Generating manifest.txt"; \
arduino-manifest.pl -b $(FQBN) $(SRC) $(HDRS) > manifest.txt; \
else echo "---> If you want to generate manifest.txt, listing used libraries and their versions,"; \
echo "---> please install arduino-manifest, see https://github.com/digiampietro/arduino-manifest"; \
fi
upload:
@if [ ! -c $(SERIAL_DEV) ] ; \
then echo "---> ERROR: Serial Device not available, please set the SERIAL_DEV environment variable" ; \
else echo "---> Uploading sketch\n"; \
arduino-cli upload -b $(FQBN) -p $(SERIAL_DEV) $(VFLAG); \
fi
ota:
@PLAT_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.platform.path' | awk -F= '{print $$2}'` ; \
PY_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.tools.python3.path' | awk -F= '{print $$2}'` ; \
IOT_IP=`avahi-browse _arduino._tcp --resolve --parsable --terminate|grep -i ';$(IOT_NAME);'|grep ';$(OTA_PORT);'| awk -F\; '{print $$8}'|head -1`; \
BINFILE=$(wildcard $(BUILD_DIR)/$(SRC)*bin); \
echo "PLAT_PATH is [$$PLAT_PATH]" ; \
echo "PY_PATH: is [$$PY_PATH]" ; \
echo "IOT_IP: is [$$IOT_IP]" ; \
echo "BINFILE: is [$$BINFILE]" ; \
if [ "$$IOT_IP" = "" ] ; \
then echo "Unable to find device IP. Check that the IOT_NAME environment variable is correctly set. Use 'make find' to search devices"; \
else echo "---> Uploading Over The Air"; \
$$PY_PATH/python3 $$PLAT_PATH/tools/espota.py -i $$IOT_IP -p $(OTA_PORT) --auth=$(OTA_PASS) -f $$BINFILE ;\
fi
clean:
@echo "---> Cleaning the build directory"
rm -rf build
find:
avahi-browse _arduino._tcp --resolve --parsable --terminate
requirements:
@if [ -e requirements.txt ]; \
then while read -r i ; do echo ; \
echo "---> Installing " '"'$$i'"' ; \
arduino-cli lib install "$$i" ; \
done < requirements.txt ; \
else echo "---> MISSING requirements.txt file"; \
fi

View File

@ -1,28 +0,0 @@
#ifndef _SHIFTLIGHTSDATA_H
#define _SHIFTLIGHTSDATA_H
#include <stdint.h>
#include <stdbool.h>
typedef struct
{
uint8_t litleds;
//unsigned char color_1_red;
//unsigned char color_1_green;
//unsigned char color_1_blue;
//unsigned char color_2_red;
//unsigned char color_2_green;
//unsigned char color_2_blue;
//unsigned char color_3_red;
//unsigned char color_3_green;
//unsigned char color_3_blue;
//unsigned char space_1;
//unsigned char space_2;
//unsigned char space_3;
//uint32_t maxrpm;
//uint32_t rpm;
}
ShiftLightsData;
#endif

View File

@ -1,84 +0,0 @@
#include <FastLED.h>
#include "shiftlights.h"
#define SIMDATA_SIZE sizeof(ShiftLightsData)
#define LED_PIN 7
#define NUM_LEDS 6
#define BRIGHTNESS 40
#define STARTLED 1
#define COLOR1R 0
#define COLOR1G 255
#define COLOR1B 0
#define COLOR2R 255
#define COLOR2G 255
#define COLOR2B 0
#define COLOR3R 255
#define COLOR3G 0
#define COLOR3B 0
CRGB leds[NUM_LEDS];
ShiftLightsData sd;
int maxrpm = 0;
int rpm = 0;
int numlights = NUM_LEDS;
int pin = LED_PIN;
int lights[NUM_LEDS];
void setup()
{
Serial.begin(115200);
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setMaxPowerInVoltsAndMilliamps(5, 500);
FastLED.setBrightness(BRIGHTNESS);
for (int i = 0; i < numlights; i++)
{
leds[i] = CRGB ( 0, 0, 0);
lights[i] = 0;
}
FastLED.clear();
sd.litleds = 0;
//sd.rpm = 0;
//sd.maxrpm = 6500;
}
void loop()
{
int l = 0;
int lit = sd.litleds;
char buff[SIMDATA_SIZE];
if (Serial.available() >= SIMDATA_SIZE)
{
Serial.readBytes(buff, SIMDATA_SIZE);
memcpy(&sd, &buff, SIMDATA_SIZE);
lit = sd.litleds;
}
l = 0;
FastLED.clear();
while (l < lit)
{
if (l >= numlights / 2)
{
leds[l+STARTLED-1] = CRGB ( COLOR2R, COLOR2G, COLOR2B);
}
if (l < numlights / 2)
{
leds[l+STARTLED-1] = CRGB ( COLOR1R, COLOR1G, COLOR1B);
}
if (l == numlights - 1)
{
leds[l+STARTLED-1] = CRGB ( COLOR3R, COLOR3G, COLOR3B);
}
//if (lights[l] <= 0)
//{
// leds[l] = CRGB ( 0, 0, 0);
//}
l++;
}
FastLED.show();
}

View File

@ -1 +0,0 @@
../../monocoque/simulatorapi/simapi/simapi/simdata.h

View File

@ -1,166 +0,0 @@
# Makefile for Arduino based scketches
#
# Copyright 2020 Valerio Di Giampietro http://va.ler.io v@ler.io
# MIT License - see License.txt file
#
# This Makefile uses the arduino-cli, the Arduino command line interface
# and has been designed and tested to run on Linux, not on Windows.
# Probably it will run on a Mac, but it has not been tested.
#
# Please note that:
#
# 1. each sketch must reside in his own folder with this Makefile
#
# 2. the main make targets are:
# - all compiles and upload
# - compile compiles only
# - upload upload via serial port, compile if the binary file is
# not available
# - ota upload Over The Air, automatically find the device
# IP address using the IOT_NAME (device hostname)
# - clean clean the build directory
# - find find OTA updatable devices on the local subnet
# - requirements it the file "requirements.txt" exists it will
# install the libraries listed in this file
#
# default is "all"
#
# 3. it gets the name of the sketch using the wildcard make command;
# the name is *.ino; this means that you must have ONLY a file
# with .ino extension, otherwise this makefile will break. This
# also means that you can use this Makefile, almost unmodified,
# for any sketch as long as you keep a single .ino file in each
# folder
#
# 4. you can split your project in multiple files, if you wish,
# using a single .ino file and multiple .h files, that you can
# include in the .ino file with an '#include "myfile.h"'
# directive
#
# Optionally some environment variables can be set:
#
# FQBN Fully Qualified Board Name; if not set in the environment
# it will be assigned a value in this makefile
#
# SERIAL_DEV Serial device to upload the sketch; if not set in the
# environment it will be assigned:
# /dev/ttyUSB0 if it exists, or
# /dev/ttyACM0 if it exists, or
# unknown
#
# IOT_NAME Name of the IOT device; if not set in the environment
# it will be assigned a value in this makefile. This is
# very useful for OTA update, the device will be searched
# on the local subnet using this name
#
# OTA_PORT Port used by OTA update; if not set in the environment
# it will be assigned the default value of 8266 in this
# makefile
#
# OTA_PASS Password used for OTA update; if not set in the environment
# it will be assigned the default value of an empty string
#
# V verbose flag; can be 0 (quiet) or 1 (verbose); if not set
# in the environment it will be assigned a default value
# in this makefile
MAKE_DIR := $(PWD)
#
# ----- setup wor Wemos D1 mini -----
#FQBN ?= esp8266:esp8266:d1_mini
#IOT_NAME ?= esp8266-meteo
#OTA_PORT ?= 8266
#OTA_PASS ?=
# ----- setup for Arduino Uno
FQBN ?= arduino:avr:uno
# ----- ---------------------
V ?= 0
VFLAG =
ifeq "$(V)" "1"
VFLAG =-v
endif
ifndef SERIAL_DEV
ifneq (,$(wildcard /dev/ttyUSB0))
SERIAL_DEV = /dev/ttyUSB0
else ifneq (,$(wildcard /dev/ttyACM0))
SERIAL_DEV = /dev/ttyACM0
else
SERIAL_DEV = unknown
endif
endif
BUILD_DIR := $(subst :,.,build/$(FQBN))
SRC := $(wildcard *.ino)
HDRS := $(wildcard *.h)
BIN := $(BUILD_DIR)/$(SRC).bin
ELF := $(BUILD_DIR)/$(SRC).elf
$(info FQBN is [${FQBN}])
$(info IOT_NAME is [${IOT_NAME}])
$(info OTA_PORT is [${OTA_PORT}])
$(info OTA_PASS is [${OTA_PASS}])
$(info V is [${V}])
$(info VFLAG is [${VFLAG}])
$(info MAKE_DIR is [${MAKE_DIR}])
$(info BUILD_DIR is [${BUILD_DIR}])
$(info SRC is [${SRC}])
$(info HDRS is [${HDRS}])
$(info BIN is [${BIN}])
$(info SERIAL_DEV is [${SERIAL_DEV}])
all: $(ELF) upload
.PHONY: all
compile: $(ELF)
.PHONY: compile
$(ELF): $(SRC) $(HDRS)
arduino-cli compile -b $(FQBN) $(VFLAG)
@if which arduino-manifest.pl; \
then echo "---> Generating manifest.txt"; \
arduino-manifest.pl -b $(FQBN) $(SRC) $(HDRS) > manifest.txt; \
else echo "---> If you want to generate manifest.txt, listing used libraries and their versions,"; \
echo "---> please install arduino-manifest, see https://github.com/digiampietro/arduino-manifest"; \
fi
upload:
@if [ ! -c $(SERIAL_DEV) ] ; \
then echo "---> ERROR: Serial Device not available, please set the SERIAL_DEV environment variable" ; \
else echo "---> Uploading sketch\n"; \
arduino-cli upload -b $(FQBN) -p $(SERIAL_DEV) $(VFLAG); \
fi
ota:
@PLAT_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.platform.path' | awk -F= '{print $$2}'` ; \
PY_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.tools.python3.path' | awk -F= '{print $$2}'` ; \
IOT_IP=`avahi-browse _arduino._tcp --resolve --parsable --terminate|grep -i ';$(IOT_NAME);'|grep ';$(OTA_PORT);'| awk -F\; '{print $$8}'|head -1`; \
BINFILE=$(wildcard $(BUILD_DIR)/$(SRC)*bin); \
echo "PLAT_PATH is [$$PLAT_PATH]" ; \
echo "PY_PATH: is [$$PY_PATH]" ; \
echo "IOT_IP: is [$$IOT_IP]" ; \
echo "BINFILE: is [$$BINFILE]" ; \
if [ "$$IOT_IP" = "" ] ; \
then echo "Unable to find device IP. Check that the IOT_NAME environment variable is correctly set. Use 'make find' to search devices"; \
else echo "---> Uploading Over The Air"; \
$$PY_PATH/python3 $$PLAT_PATH/tools/espota.py -i $$IOT_IP -p $(OTA_PORT) --auth=$(OTA_PASS) -f $$BINFILE ;\
fi
clean:
@echo "---> Cleaning the build directory"
rm -rf build
find:
avahi-browse _arduino._tcp --resolve --parsable --terminate
requirements:
@if [ -e requirements.txt ]; \
then while read -r i ; do echo ; \
echo "---> Installing " '"'$$i'"' ; \
arduino-cli lib install "$$i" ; \
done < requirements.txt ; \
else echo "---> MISSING requirements.txt file"; \
fi

View File

@ -1 +0,0 @@
../../monocoque/simulatorapi/simapi/simapi/simdata.h

View File

@ -1,21 +0,0 @@
#ifndef _SIMHAPTICDATA_H
#define _SIMHAPTICDATA_H
#include <stdint.h>
#include <stdbool.h>
typedef struct
{
uint8_t motor1;
uint8_t effect1;
uint8_t motor2;
uint8_t effect2;
uint8_t motor3;
uint8_t effect3;
uint8_t motor4;
uint8_t effect4;
}
SimHapticData;
#endif

View File

@ -1,81 +0,0 @@
#include <Adafruit_MotorShield.h>
#include "simhaptic.h"
#define BYTE_SIZE sizeof(SimHapticData)
#define POWER .6
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_DCMotor *myMotor1 = AFMS.getMotor(1);
//Adafruit_DCMotor *myMotor2 = AFMS.getMotor(2);
Adafruit_DCMotor *myMotor3 = AFMS.getMotor(3);
//Adafruit_DCMotor *myMotor4 = AFMS.getMotor(4);
SimHapticData sd;
int motor1 = 0;
int motor2 = 0;
int motor3 = 0;
int motor4 = 0;
int effect1 = 0;
int effect2 = 0;
int effect3 = 0;
int effect4 = 0;
void setup() {
Serial.begin(115200);
if (!AFMS.begin()) {
Serial.println("Could not find Motor Shield. Check wiring.");
while (1);
}
sd.motor1 = 0;
sd.motor2 = 0;
sd.motor3 = 0;
sd.motor4 = 0;
myMotor1->setSpeed(0);
myMotor1->run(FORWARD);
//myMotor2->setSpeed(0);
//myMotor2->run(FORWARD);
myMotor3->setSpeed(0);
myMotor3->run(FORWARD);
//myMotor4->setSpeed(0);
//myMotor4->run(FORWARD);
}
void loop() {
char buff[BYTE_SIZE];
if (Serial.available() >= BYTE_SIZE)
{
Serial.readBytes(buff, BYTE_SIZE);
memcpy(&sd, &buff, BYTE_SIZE);
motor1 = sd.motor1;
motor2 = sd.motor2;
motor3 = sd.motor3;
motor4 = sd.motor4;
effect1 = sd.effect1;
effect2 = sd.effect2;
effect3 = sd.effect3;
effect4 = sd.effect4;
}
if (motor1 >= 1)
{
myMotor1->setSpeed(effect1);
}
//if (motor2 >= 1)
//{
// myMotor2->setSpeed(effect2);
//}
if (motor3 >= 1)
{
myMotor3->setSpeed(effect3);
}
//if (motor4 >= 1)
//{
// myMotor4->setSpeed(effect4);
//}
}

View File

@ -1,166 +0,0 @@
# Makefile for Arduino based scketches
#
# Copyright 2020 Valerio Di Giampietro http://va.ler.io v@ler.io
# MIT License - see License.txt file
#
# This Makefile uses the arduino-cli, the Arduino command line interface
# and has been designed and tested to run on Linux, not on Windows.
# Probably it will run on a Mac, but it has not been tested.
#
# Please note that:
#
# 1. each sketch must reside in his own folder with this Makefile
#
# 2. the main make targets are:
# - all compiles and upload
# - compile compiles only
# - upload upload via serial port, compile if the binary file is
# not available
# - ota upload Over The Air, automatically find the device
# IP address using the IOT_NAME (device hostname)
# - clean clean the build directory
# - find find OTA updatable devices on the local subnet
# - requirements it the file "requirements.txt" exists it will
# install the libraries listed in this file
#
# default is "all"
#
# 3. it gets the name of the sketch using the wildcard make command;
# the name is *.ino; this means that you must have ONLY a file
# with .ino extension, otherwise this makefile will break. This
# also means that you can use this Makefile, almost unmodified,
# for any sketch as long as you keep a single .ino file in each
# folder
#
# 4. you can split your project in multiple files, if you wish,
# using a single .ino file and multiple .h files, that you can
# include in the .ino file with an '#include "myfile.h"'
# directive
#
# Optionally some environment variables can be set:
#
# FQBN Fully Qualified Board Name; if not set in the environment
# it will be assigned a value in this makefile
#
# SERIAL_DEV Serial device to upload the sketch; if not set in the
# environment it will be assigned:
# /dev/ttyUSB0 if it exists, or
# /dev/ttyACM0 if it exists, or
# unknown
#
# IOT_NAME Name of the IOT device; if not set in the environment
# it will be assigned a value in this makefile. This is
# very useful for OTA update, the device will be searched
# on the local subnet using this name
#
# OTA_PORT Port used by OTA update; if not set in the environment
# it will be assigned the default value of 8266 in this
# makefile
#
# OTA_PASS Password used for OTA update; if not set in the environment
# it will be assigned the default value of an empty string
#
# V verbose flag; can be 0 (quiet) or 1 (verbose); if not set
# in the environment it will be assigned a default value
# in this makefile
MAKE_DIR := $(PWD)
#
# ----- setup wor Wemos D1 mini -----
#FQBN ?= esp8266:esp8266:d1_mini
#IOT_NAME ?= esp8266-meteo
#OTA_PORT ?= 8266
#OTA_PASS ?=
# ----- setup for Arduino Uno
FQBN ?= arduino:avr:uno
# ----- ---------------------
V ?= 0
VFLAG =
ifeq "$(V)" "1"
VFLAG =-v
endif
ifndef SERIAL_DEV
ifneq (,$(wildcard /dev/ttyUSB0))
SERIAL_DEV = /dev/ttyUSB0
else ifneq (,$(wildcard /dev/ttyACM0))
SERIAL_DEV = /dev/ttyACM0
else
SERIAL_DEV = unknown
endif
endif
BUILD_DIR := $(subst :,.,build/$(FQBN))
SRC := $(wildcard *.ino)
HDRS := $(wildcard *.h)
BIN := $(BUILD_DIR)/$(SRC).bin
ELF := $(BUILD_DIR)/$(SRC).elf
$(info FQBN is [${FQBN}])
$(info IOT_NAME is [${IOT_NAME}])
$(info OTA_PORT is [${OTA_PORT}])
$(info OTA_PASS is [${OTA_PASS}])
$(info V is [${V}])
$(info VFLAG is [${VFLAG}])
$(info MAKE_DIR is [${MAKE_DIR}])
$(info BUILD_DIR is [${BUILD_DIR}])
$(info SRC is [${SRC}])
$(info HDRS is [${HDRS}])
$(info BIN is [${BIN}])
$(info SERIAL_DEV is [${SERIAL_DEV}])
all: $(ELF) upload
.PHONY: all
compile: $(ELF)
.PHONY: compile
$(ELF): $(SRC) $(HDRS)
arduino-cli compile -b $(FQBN) $(VFLAG)
@if which arduino-manifest.pl; \
then echo "---> Generating manifest.txt"; \
arduino-manifest.pl -b $(FQBN) $(SRC) $(HDRS) > manifest.txt; \
else echo "---> If you want to generate manifest.txt, listing used libraries and their versions,"; \
echo "---> please install arduino-manifest, see https://github.com/digiampietro/arduino-manifest"; \
fi
upload:
@if [ ! -c $(SERIAL_DEV) ] ; \
then echo "---> ERROR: Serial Device not available, please set the SERIAL_DEV environment variable" ; \
else echo "---> Uploading sketch\n"; \
arduino-cli upload -b $(FQBN) -p $(SERIAL_DEV) $(VFLAG); \
fi
ota:
@PLAT_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.platform.path' | awk -F= '{print $$2}'` ; \
PY_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.tools.python3.path' | awk -F= '{print $$2}'` ; \
IOT_IP=`avahi-browse _arduino._tcp --resolve --parsable --terminate|grep -i ';$(IOT_NAME);'|grep ';$(OTA_PORT);'| awk -F\; '{print $$8}'|head -1`; \
BINFILE=$(wildcard $(BUILD_DIR)/$(SRC)*bin); \
echo "PLAT_PATH is [$$PLAT_PATH]" ; \
echo "PY_PATH: is [$$PY_PATH]" ; \
echo "IOT_IP: is [$$IOT_IP]" ; \
echo "BINFILE: is [$$BINFILE]" ; \
if [ "$$IOT_IP" = "" ] ; \
then echo "Unable to find device IP. Check that the IOT_NAME environment variable is correctly set. Use 'make find' to search devices"; \
else echo "---> Uploading Over The Air"; \
$$PY_PATH/python3 $$PLAT_PATH/tools/espota.py -i $$IOT_IP -p $(OTA_PORT) --auth=$(OTA_PASS) -f $$BINFILE ;\
fi
clean:
@echo "---> Cleaning the build directory"
rm -rf build
find:
avahi-browse _arduino._tcp --resolve --parsable --terminate
requirements:
@if [ -e requirements.txt ]; \
then while read -r i ; do echo ; \
echo "---> Installing " '"'$$i'"' ; \
arduino-cli lib install "$$i" ; \
done < requirements.txt ; \
else echo "---> MISSING requirements.txt file"; \
fi

View File

@ -1 +0,0 @@
../../monocoque/simulatorapi/simapi/simapi/simdata.h

View File

@ -1,15 +0,0 @@
#ifndef _SIMWINDDATA_H
#define _SIMWINDDATA_H
#include <stdint.h>
#include <stdbool.h>
typedef struct
{
uint8_t velocity;
uint8_t fanpower;
}
SimWindData;
#endif

View File

@ -1,43 +0,0 @@
#include <Adafruit_MotorShield.h>
#include "simwind.h"
#define BYTE_SIZE sizeof(SimWindData)
#define KPHTOMPH .621317
#define FANPOWER .6
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_DCMotor *myMotor1 = AFMS.getMotor(1);
Adafruit_DCMotor *myMotor2 = AFMS.getMotor(3);
SimWindData sd;
int velocity = 0;
void setup() {
Serial.begin(115200);
if (!AFMS.begin()) {
Serial.println("Could not find Motor Shield. Check wiring.");
while (1);
}
sd.velocity = 10;
myMotor1->setSpeed(0);
myMotor1->run(FORWARD);
myMotor2->setSpeed(0);
myMotor2->run(FORWARD);
}
void loop() {
char buff[BYTE_SIZE];
if (Serial.available() >= BYTE_SIZE)
{
Serial.readBytes(buff, BYTE_SIZE);
memcpy(&sd, &buff, BYTE_SIZE);
velocity = sd.velocity;
}
myMotor1->setSpeed(velocity*FANPOWER);
myMotor2->setSpeed(velocity*FANPOWER);
}

View File

@ -1,166 +0,0 @@
# Makefile for Arduino based scketches
#
# Copyright 2020 Valerio Di Giampietro http://va.ler.io v@ler.io
# MIT License - see License.txt file
#
# This Makefile uses the arduino-cli, the Arduino command line interface
# and has been designed and tested to run on Linux, not on Windows.
# Probably it will run on a Mac, but it has not been tested.
#
# Please note that:
#
# 1. each sketch must reside in his own folder with this Makefile
#
# 2. the main make targets are:
# - all compiles and upload
# - compile compiles only
# - upload upload via serial port, compile if the binary file is
# not available
# - ota upload Over The Air, automatically find the device
# IP address using the IOT_NAME (device hostname)
# - clean clean the build directory
# - find find OTA updatable devices on the local subnet
# - requirements it the file "requirements.txt" exists it will
# install the libraries listed in this file
#
# default is "all"
#
# 3. it gets the name of the sketch using the wildcard make command;
# the name is *.ino; this means that you must have ONLY a file
# with .ino extension, otherwise this makefile will break. This
# also means that you can use this Makefile, almost unmodified,
# for any sketch as long as you keep a single .ino file in each
# folder
#
# 4. you can split your project in multiple files, if you wish,
# using a single .ino file and multiple .h files, that you can
# include in the .ino file with an '#include "myfile.h"'
# directive
#
# Optionally some environment variables can be set:
#
# FQBN Fully Qualified Board Name; if not set in the environment
# it will be assigned a value in this makefile
#
# SERIAL_DEV Serial device to upload the sketch; if not set in the
# environment it will be assigned:
# /dev/ttyUSB0 if it exists, or
# /dev/ttyACM0 if it exists, or
# unknown
#
# IOT_NAME Name of the IOT device; if not set in the environment
# it will be assigned a value in this makefile. This is
# very useful for OTA update, the device will be searched
# on the local subnet using this name
#
# OTA_PORT Port used by OTA update; if not set in the environment
# it will be assigned the default value of 8266 in this
# makefile
#
# OTA_PASS Password used for OTA update; if not set in the environment
# it will be assigned the default value of an empty string
#
# V verbose flag; can be 0 (quiet) or 1 (verbose); if not set
# in the environment it will be assigned a default value
# in this makefile
MAKE_DIR := $(PWD)
#
# ----- setup wor Wemos D1 mini -----
#FQBN ?= esp8266:esp8266:d1_mini
#IOT_NAME ?= esp8266-meteo
#OTA_PORT ?= 8266
#OTA_PASS ?=
# ----- setup for Arduino Uno
FQBN ?= arduino:avr:uno
# ----- ---------------------
V ?= 0
VFLAG =
ifeq "$(V)" "1"
VFLAG =-v
endif
ifndef SERIAL_DEV
ifneq (,$(wildcard /dev/ttyUSB0))
SERIAL_DEV = /dev/ttyUSB0
else ifneq (,$(wildcard /dev/ttyACM0))
SERIAL_DEV = /dev/ttyACM0
else
SERIAL_DEV = unknown
endif
endif
BUILD_DIR := $(subst :,.,build/$(FQBN))
SRC := $(wildcard *.ino)
HDRS := $(wildcard *.h)
BIN := $(BUILD_DIR)/$(SRC).bin
ELF := $(BUILD_DIR)/$(SRC).elf
$(info FQBN is [${FQBN}])
$(info IOT_NAME is [${IOT_NAME}])
$(info OTA_PORT is [${OTA_PORT}])
$(info OTA_PASS is [${OTA_PASS}])
$(info V is [${V}])
$(info VFLAG is [${VFLAG}])
$(info MAKE_DIR is [${MAKE_DIR}])
$(info BUILD_DIR is [${BUILD_DIR}])
$(info SRC is [${SRC}])
$(info HDRS is [${HDRS}])
$(info BIN is [${BIN}])
$(info SERIAL_DEV is [${SERIAL_DEV}])
all: $(ELF) upload
.PHONY: all
compile: $(ELF)
.PHONY: compile
$(ELF): $(SRC) $(HDRS)
arduino-cli compile -b $(FQBN) $(VFLAG)
@if which arduino-manifest.pl; \
then echo "---> Generating manifest.txt"; \
arduino-manifest.pl -b $(FQBN) $(SRC) $(HDRS) > manifest.txt; \
else echo "---> If you want to generate manifest.txt, listing used libraries and their versions,"; \
echo "---> please install arduino-manifest, see https://github.com/digiampietro/arduino-manifest"; \
fi
upload:
@if [ ! -c $(SERIAL_DEV) ] ; \
then echo "---> ERROR: Serial Device not available, please set the SERIAL_DEV environment variable" ; \
else echo "---> Uploading sketch\n"; \
arduino-cli upload -b $(FQBN) -p $(SERIAL_DEV) $(VFLAG); \
fi
ota:
@PLAT_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.platform.path' | awk -F= '{print $$2}'` ; \
PY_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.tools.python3.path' | awk -F= '{print $$2}'` ; \
IOT_IP=`avahi-browse _arduino._tcp --resolve --parsable --terminate|grep -i ';$(IOT_NAME);'|grep ';$(OTA_PORT);'| awk -F\; '{print $$8}'|head -1`; \
BINFILE=$(wildcard $(BUILD_DIR)/$(SRC)*bin); \
echo "PLAT_PATH is [$$PLAT_PATH]" ; \
echo "PY_PATH: is [$$PY_PATH]" ; \
echo "IOT_IP: is [$$IOT_IP]" ; \
echo "BINFILE: is [$$BINFILE]" ; \
if [ "$$IOT_IP" = "" ] ; \
then echo "Unable to find device IP. Check that the IOT_NAME environment variable is correctly set. Use 'make find' to search devices"; \
else echo "---> Uploading Over The Air"; \
$$PY_PATH/python3 $$PLAT_PATH/tools/espota.py -i $$IOT_IP -p $(OTA_PORT) --auth=$(OTA_PASS) -f $$BINFILE ;\
fi
clean:
@echo "---> Cleaning the build directory"
rm -rf build
find:
avahi-browse _arduino._tcp --resolve --parsable --terminate
requirements:
@if [ -e requirements.txt ]; \
then while read -r i ; do echo ; \
echo "---> Installing " '"'$$i'"' ; \
arduino-cli lib install "$$i" ; \
done < requirements.txt ; \
else echo "---> MISSING requirements.txt file"; \
fi

View File

@ -1,15 +0,0 @@
#ifndef _SIMWINDDATA_H
#define _SIMWINDDATA_H
#include <stdint.h>
#include <stdbool.h>
typedef struct
{
uint8_t velocity;
uint8_t fanpower;
}
SimWindData;
#endif

View File

@ -1,56 +0,0 @@
#include "simwind.h"
// ============================================================
// Configuration
// ============================================================
#define BAUD_RATE 115200
#define PWM_PIN_1 9 // Fan PWM output pins
#define PWM_PIN_2 10 //
#define PWM_MAX 320 // Max PWM value for 25kHz (16MHz / 25kHz / 2)
#define SPEED_MIN 0 // Min speed (mph) — fans off below this
#define SPEED_MAX 100 // Max speed (mph) — full fan speed at this
#define SPEED_THRESHOLD 5 // Below this speed fans are off
// ============================================================
#define BYTE_SIZE sizeof(SimWindData)
int velocity = 0;
int fanpower = 0;
void setup() {
Serial.begin(BAUD_RATE);
// Configure Timer1 for Phase Correct PWM at 25kHz duty cycle
// Mode 10: Phase Correct PWM, TOP = ICR1
// WGM13:WGM12:WGM11:WGM10 = 1:0:1:0
TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11);
TCCR1B = _BV(WGM13) | _BV(CS10); // <-- WGM13 restored, no prescaler
ICR1 = PWM_MAX; // TOP = 320 → 16MHz / (2 * 320) = 25kHz
pinMode(PWM_PIN_1, OUTPUT);
pinMode(PWM_PIN_2, OUTPUT);
OCR1A = 0;
OCR1B = 0;
}
void loop() {
char buff[BYTE_SIZE];
if (Serial.available() >= BYTE_SIZE) {
if (Serial.readBytes(buff, BYTE_SIZE) != BYTE_SIZE) return;
union { SimWindData data; char bytes[BYTE_SIZE]; } u;
memcpy(u.bytes, buff, BYTE_SIZE);
velocity = u.data.velocity;
fanpower = u.data.fanpower;
}
int pwm = 0;
if (velocity > SPEED_THRESHOLD) {
// Square root scaling for slightly stronger sensation at lower speeds
// fanpower (0-255) scales the overall intensity
float powerScale = (float)fanpower / 255.0;
pwm = (int)(sqrt((float)velocity / SPEED_MAX) * PWM_MAX * powerScale);
pwm = constrain(pwm, 0, PWM_MAX);
}
OCR1A = pwm;
OCR1B = pwm;
}

View File

@ -7,43 +7,14 @@ set(devices_source_files
sounddevice.c sounddevice.c
serialdevice.h serialdevice.h
serialdevice.c serialdevice.c
serialadapter.h
serialadapter.c
tachdevice.h tachdevice.h
tachdevice.c tachdevice.c
wheeldevice.h
wheeldevice.c
usbhapticdevice.h
usbhapticdevice.c
hapticeffect.h
hapticeffect.c
sound.h
sound.c
usb/revburner.h usb/revburner.h
usb/revburner.c usb/revburner.c
usb/cslelitev3.h sound/usb_generic_shaker.h
usb/cslelitev3.c sound/usb_generic_shaker.c
usb/simagicp1000.h
usb/simagicp1000.c
usb/wheels/logitechg29.h
usb/wheels/logitechg29.c
usb/wheels/cammusc5.h
usb/wheels/cammusc5.c
usb/wheels/cammusc12.h
usb/wheels/cammusc12.c
usb/wheels/gtneo.h
usb/wheels/gtneo.c
sound/usb_generic_shaker_pulse.c
serial/arduino.h serial/arduino.h
serial/arduino.c serial/arduino.c
serial/arduinoledlua.h
serial/arduinoledlua.c
serial/moza.h
serial/moza.c
serial/moza_new.h
serial/moza_new.c
serial/moza_ks_pro_wheel.h
serial/moza_ks_pro_wheel.c
) )
include_directories("." "usb" "sound" "serial") include_directories("." "usb" "sound" "serial")

View File

@ -1,422 +0,0 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <math.h>
#include "usbhapticdevice.h"
#include "../../helper/confighelper.h"
#include "../../simulatorapi/simapi/simapi/simdata.h"
#include "../../simulatorapi/simapi/simapi/simmapper.h"
#include "../../slog/slog.h"
#define kmhtoms 0.277778
#define minspeedinms 0.5
#define minvelocity 50
#define maxbrake 0
#define maxthrottle 0
#define maxXvelocity 0.001
bool hasTyreDiameter(SimData* simdata)
{
if (simdata->tyrediameter[0] <= 0 || simdata->tyrediameter[1] <= 0 || simdata->tyrediameter[2] <= 0 || simdata->tyrediameter[3] <= 0)
{
slogt("failed to find tyre diameter data");
return false;
}
slogt("tyre diameter data found");
return true;
}
int loadtyreconfig(SimData* simdata, char* configfile, bool setDiameters)
{
config_t cfg;
config_init(&cfg);
const config_setting_t* config_cars = NULL;
if (!config_read_file(&cfg, configfile))
{
slogw("Could not open diameters save file.");
config_destroy(&cfg);
return 1;
}
config_cars = config_lookup(&cfg, "cars");
if(config_cars == NULL)
{
slogd("diameters config file corrupted");
config_destroy(&cfg);
return 1;
}
slogt("parsing diameters config file");
const int cars = config_setting_length(config_cars);
bool foundCar = false;
int i = 0;
while (i<cars)
{
DeviceSettings settings;
config_setting_t* config_car = config_setting_get_elem(config_cars, i);
if(config_car != NULL)
{
const char* car;
const char* simstr;
int sim = 0;
double tyre0;
double tyre1;
double tyre2;
double tyre3;
config_setting_lookup_string(config_car, "car", &car);
config_setting_lookup_float(config_car, "tyre0", &tyre0);
config_setting_lookup_float(config_car, "tyre1", &tyre1);
config_setting_lookup_float(config_car, "tyre2", &tyre2);
config_setting_lookup_float(config_car, "tyre3", &tyre3);
int found = config_setting_lookup_int(config_car, "sim", &sim);
//if(found == CONFIG_FALSE)
//{
// int found = config_setting_lookup_int(config_car, "sim", &sim);
//}
//else
//{
// sim = simapi_strtogame(simstr);
//}
if(simdata->car != NULL && car != NULL)
{
if(simdata->car[0] != '\0' && car[0] != '\0')
{
slogt("%s %s %i %i", simdata->car, car, simdata->simexe, sim);
if (strcicmp(car, simdata->car) == 0 && sim == simdata->simexe)
{
slogi("found saved car %s with tyre diameters %f %f %f %f", car, tyre0, tyre1, tyre2, tyre3);
foundCar = true;
if(setDiameters == true)
{
simdata->tyrediameter[0] = tyre0;
simdata->tyrediameter[1] = tyre1;
simdata->tyrediameter[2] = tyre2;
simdata->tyrediameter[3] = tyre3;
}
break;
}
}
}
}
else
{
slogw("Possible corruption in config file on entry %i attempting to continue", i+1);
}
i++;
}
config_destroy(&cfg);
if(foundCar == true)
{
return i;
}
return -1;
}
int savetyreconfig(SimData* simdata, char* configfile)
{
config_t cfg;
config_setting_t* root;
config_setting_t* array;
config_setting_t* carobject;
config_setting_t* setting;
config_init(&cfg);
if (!config_read_file(&cfg, configfile))
{
slogw("Could not open diameters save file, creating new.");
root = config_root_setting(&cfg);
array = config_setting_add(root, "cars", CONFIG_TYPE_LIST);
}
else
{
array = config_lookup(&cfg, "cars");
}
// TODO add check to not add same car-sim combination twice
carobject = config_setting_add(array, "cars", CONFIG_TYPE_GROUP);
setting = config_setting_add(carobject, "car", CONFIG_TYPE_STRING);
config_setting_set_string(setting, simdata->car);
setting = config_setting_add(carobject, "sim", CONFIG_TYPE_INT64);
config_setting_set_int64(setting, simdata->simexe);
setting = config_setting_add(carobject, "tyre0", CONFIG_TYPE_FLOAT);
config_setting_set_float(setting, simdata->tyrediameter[0]);
setting = config_setting_add(carobject, "tyre1", CONFIG_TYPE_FLOAT);
config_setting_set_float(setting, simdata->tyrediameter[1]);
setting = config_setting_add(carobject, "tyre2", CONFIG_TYPE_FLOAT);
config_setting_set_float(setting, simdata->tyrediameter[2]);
setting = config_setting_add(carobject, "tyre3", CONFIG_TYPE_FLOAT);
config_setting_set_float(setting, simdata->tyrediameter[3]);
/* Write out the new configuration. */
if(! config_write_file(&cfg, configfile))
{
slogi("Error while writing file.");
config_destroy(&cfg);
}
slogi("New configuration successfully written to: %s for sim %i, car %s\n", configfile, simdata->simexe, simdata->car);
config_destroy(&cfg);
return 0;
}
void getTyreDiameter(SimData* simdata)
{
if(simdata->velocity > minvelocity && simdata->brake <= maxbrake && simdata->gas <= maxthrottle)
{
double Speedms = kmhtoms * simdata->velocity;
if (simdata->Xvelocity/Speedms < maxXvelocity)
{
for(int i = 0; i < 4; i++)
{
simdata->tyrediameter[i] = Speedms / simdata->tyreRPS[i] * 2;
}
slogi("Successfully set tyre diameters for wheel slip effects.");
}
}
}
double slipeffect(SimData* simdata, int effecttype, int tyre, double threshold, int useconfig, int* configcheck, char* configfile)
{
double play = 0;
double wheelslip[4];
wheelslip[0] = 0;
wheelslip[1] = 0;
wheelslip[2] = 0;
wheelslip[3] = 0;
int sim_slip_ratio =0;
for (int i = 0; i < 4; i++)
{
sim_slip_ratio = abs(simdata->tyreslipratio[i]);
}
if(sim_slip_ratio == 0)
{
slogt("wheel vibration calculation with wheel config set to %i configchecked %i configfile %s car %s sim %i", useconfig, *configcheck, configfile, simdata->car, simdata->simexe);
switch (effecttype)
{
case (EFFECT_TYRESLIP):
case (EFFECT_TYRELOCK):
case (EFFECT_ABSBRAKES):
if(hasTyreDiameter(simdata)==true)
{
double Speedms = kmhtoms * simdata->velocity;
slogt("attempting wheel slip calculation");
if (Speedms > minspeedinms)
{
for(int i = 0; i < 4; i++)
{
wheelslip[i] = (Speedms - simdata->tyrediameter[i] * simdata->tyreRPS[i] / 2) / Speedms;
}
}
else
{
for(int i = 0; i < 4; i++)
{
wheelslip[i] = 0;
}
}
slogt("wheelslip values are %f %f %f %f", wheelslip[0], wheelslip[1], wheelslip[2], wheelslip[3]);
slogt("velocities (x,y,z) are %f %f %f", simdata->Xvelocity, simdata->Yvelocity, simdata->Zvelocity);
}
break;
case EFFECT_SUSPENSION:
break;
default:
slogd("Unknown effect type");
}
if(simdata->Yvelocity <= 0)
{
return 0;
}
if(simdata->Zvelocity > 1 || simdata->Zvelocity < -1)
{
return 0;
}
}
else
{
for (int i = 0; i < 4; i++)
{
wheelslip[i] = simdata->tyreslipratio[i];
}
slogt("wheelslip values from sim are %f %f %f %f", wheelslip[0], wheelslip[1], wheelslip[2], wheelslip[3]);
}
switch (effecttype)
{
case (EFFECT_TYRESLIP):
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
{
if(wheelslip[0] < -threshold)
{
play += fabs(wheelslip[0]) - fabs(threshold);
slogt("slip is %f", play);
}
}
if (tyre == FRONTRIGHT || tyre == FRONTS || tyre == ALLFOUR)
{
if(wheelslip[1] < -threshold)
{
play += fabs(wheelslip[1]) - fabs(threshold);
slogt("slip is %f", play);
}
}
if (tyre == REARLEFT || tyre == REARS || tyre == ALLFOUR)
{
if(wheelslip[2] < -threshold)
{
play += fabs(wheelslip[2]) - fabs(threshold);
slogt("slip is %f", play);
}
}
if (tyre == REARRIGHT || tyre == REARS || tyre == ALLFOUR)
{
if(wheelslip[3] < -threshold)
{
play += fabs(wheelslip[3]) - fabs(threshold);
slogt("slip is %f", play);
}
}
break;
case (EFFECT_TYRELOCK):
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
{
if(wheelslip[0] > threshold)
{
play += wheelslip[0] - threshold;
slogt("lock is %f", play);
}
}
if (tyre == FRONTRIGHT || tyre == FRONTS || tyre == ALLFOUR)
{
if(wheelslip[1] > threshold)
{
play += wheelslip[1] - threshold;
slogt("lock is %f", play);
}
}
if (tyre == REARLEFT || tyre == REARS || tyre == ALLFOUR)
{
if(wheelslip[2] > threshold)
{
play += wheelslip[2] - threshold;
slogt("lock is %f", play);
}
}
if (tyre == REARRIGHT || tyre == REARS || tyre == ALLFOUR)
{
if(wheelslip[3] > threshold)
{
play += wheelslip[3] - threshold;
slogt("lock is %f", play);
}
}
break;
case (EFFECT_ABSBRAKES):
threshold = simdata->abs + threshold;
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
{
if(wheelslip[0] > threshold)
{
play += wheelslip[0] - threshold;
slogt("abs is %f", play);
}
}
if (tyre == FRONTRIGHT || tyre == FRONTS || tyre == ALLFOUR)
{
if(wheelslip[1] > threshold)
{
play += wheelslip[1] - threshold;
slogt("abs is %f", play);
}
}
if (tyre == REARLEFT || tyre == REARS || tyre == ALLFOUR)
{
if(wheelslip[2] > threshold)
{
play += wheelslip[2] - threshold;
slogt("abs is %f", play);
}
}
if (tyre == REARRIGHT || tyre == REARS || tyre == ALLFOUR)
{
if(wheelslip[3] > threshold)
{
play += wheelslip[3] - threshold;
slogt("abs is %f", play);
}
}
if(simdata->abs <= 0)
{
play = 0;
}
break;
case (EFFECT_SUSPENSION):
if (tyre == FRONTLEFT || tyre == FRONTS || tyre == ALLFOUR)
{
if(simdata->suspension[0] > threshold)
{
play += simdata->suspension[0] - threshold;
slogt("suspension is %f", play);
}
}
if (tyre == FRONTRIGHT || tyre == FRONTS || tyre == ALLFOUR)
{
if(simdata->suspension[1] > threshold)
{
play += simdata->suspension[1] - threshold;
slogt("suspension is %f", play);
}
}
if (tyre == REARLEFT || tyre == REARS || tyre == ALLFOUR)
{
if(simdata->suspension[2] > threshold)
{
play += simdata->suspension[2] - threshold;
slogt("suspension is %f", play);
}
}
if (tyre == REARRIGHT || tyre == REARS || tyre == ALLFOUR)
{
if(simdata->suspension[3] > threshold)
{
play += simdata->suspension[3] - threshold;
slogt("suspension is %f", play);
}
}
break;
}
return play;
}

View File

@ -1,25 +0,0 @@
#ifndef _HAPTICEFFECT_H
#define _HAPTICEFFECT_H
#include <stdio.h>
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
typedef struct
{
double threshold;
VibrationEffectType effecttype;
MonocoqueTyreIdentifier tyre;
int useconfig;
int* configcheck;
char* tyrediameterconfig;
}
HapticEffect;
double slipeffect(SimData* simdata, int effecttype, int tyre, double threshold, int useconfig, int* configcheck, char* configfile);
bool hasTyreDiameter(SimData* simdata);
int loadtyreconfig(SimData* simdata, char* configfile, bool setDiameters);
int savetyreconfig(SimData* simdata, char* configfile);
void getTyreDiameter(SimData* simdata);
#endif

View File

@ -1,430 +1,79 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <math.h>
#include "arduino.h" #include "arduino.h"
#include "arduinoledlua.h" #include "../slog/slog.h"
#include "../serialadapter.h"
#include "../../slog/slog.h"
#define arduino_timeout 9000 #define arduino_timeout 2000
int arduino_check(enum sp_return result) int arduino_update(SerialDevice* serialdevice, SimData* simdata)
{ {
int result = 1;
if (serialdevice->port)
{
result = check(sp_blocking_write(serialdevice->port, simdata, sizeof(SimData), arduino_timeout));
}
return result;
}
int arduino_init(SerialDevice* serialdevice)
{
slogi("initializing arduino serial device...");
int error = 0;
char* port_name = "/dev/ttyACM0";
slogd("Looking for port %s.\n", port_name);
error = check(sp_get_port_by_name(port_name, &serialdevice->port));
if (error != 0)
{
return error;
}
slogd("Opening port.\n");
check(sp_open(serialdevice->port, SP_MODE_READ_WRITE));
slogd("Setting port to 9600 8N1, no flow control.\n");
check(sp_set_baudrate(serialdevice->port, 9600));
check(sp_set_bits(serialdevice->port, 8));
check(sp_set_parity(serialdevice->port, SP_PARITY_NONE));
check(sp_set_stopbits(serialdevice->port, 1));
check(sp_set_flowcontrol(serialdevice->port, SP_FLOWCONTROL_NONE));
slogd("Successfully setup arduino serial device...");
return 0;
}
int arduino_free(SerialDevice* serialdevice)
{
check(sp_close(serialdevice->port));
sp_free_port(serialdevice->port);
}
int check(enum sp_return result)
{
/* For this example we'll just exit on any error by calling abort(). */
char* error_message; char* error_message;
switch (result) switch (result)
{ {
case SP_ERR_ARG: case SP_ERR_ARG:
//printf("Error: Invalid argument.\n");
return 1; return 1;
//abort();
case SP_ERR_FAIL: case SP_ERR_FAIL:
error_message = sp_last_error_message(); error_message = sp_last_error_message();
sloge("error: serial write failed: %s", error_message); printf("Error: Failed: %s\n", error_message);
sp_free_error_message(error_message); sp_free_error_message(error_message);
abort();
case SP_ERR_SUPP: case SP_ERR_SUPP:
printf("Error: Not supported.\n"); printf("Error: Not supported.\n");
abort();
case SP_ERR_MEM: case SP_ERR_MEM:
printf("Error: Couldn't allocate memory.\n"); printf("Error: Couldn't allocate memory.\n");
abort();
case SP_OK: case SP_OK:
default: default:
return result; return result;
} }
} }
int arduino_update(SerialDevice* serialdevice, void* data, size_t size)
{
int result = 1;
slogt("copying %i bytes to arduino device", size);
result = monocoque_serial_write(serialdevice->id, data, size, arduino_timeout);
return result;
}
// the input event waiting and flushing is the right way to do this
// most of the time i'm just "bit blasting" and the only receiving
// happens initially to get the number of lights
int GetNumberOfLeds(SerialDevice* serialdevice, int* numlights)
{
int count = 0;
int bytesWaiting;
char buf[256];
int retval = 0;
int i;
//sp_flush(port, SP_BUF_INPUT);
while (count < 4)
{
slogi("Attempting to retrieve num lights from port...");
size_t bufsize1 = 11;
size_t recv_bufsize1 = 5;
char recv_buf1[recv_bufsize1];
char bytes1[bufsize1];
for(int j = 0; j < bufsize1; j++)
{
bytes1[j] = 0x00;
}
bytes1[0] = 0xff;
bytes1[1] = 0xff;
bytes1[2] = 0xff;
bytes1[3] = 0xff;
bytes1[4] = 0xff;
bytes1[5] = 0xff;
bytes1[6] = 0x6c;
bytes1[7] = 0x65;
bytes1[8] = 0x64;
bytes1[9] = 0x73;
bytes1[10] = 0x63;
int result = 0;
unsigned int timeout = 6000;
slogd("Sending message to get num lights");
monocoque_wait_for_event(serialdevice->id, 6);
result = monocoque_serial_write(serialdevice->id, &bytes1, bufsize1, arduino_timeout);
monocoque_wait_for_event(serialdevice->id, 5);
bytesWaiting = monocoque_input_wait(serialdevice->id);
if (bytesWaiting > 0)
{
memset(buf, 0, sizeof(buf));
retval = monocoque_serial_read_block(serialdevice->id, buf, sizeof(buf)-1, 10);
if (retval < 0)
{
retval = -1;
break;
}
else
{
for(i=0; i<retval; i++)
{
if (buf[i] == 13)
{
count++;
}
}
int ret = atoi(buf);
*numlights = ret;
return retval;
}
}
else
if (bytesWaiting < 0)
{
sloge("Error getting bytes available from serial port: %d", bytesWaiting);
retval = -1;
break;
}
retval = 0;
}
return retval;
}
int arduino_init(SerialDevice* serialdevice, const char* portdev)
{
serialdevice->id = monocoque_serial_open(serialdevice, portdev);
return serialdevice->id;
}
int arduino_custom_init(SerialDevice* serialdevice, const char* portdev, const char* luafile, bool uselights)
{
serialdevice->id = monocoque_serial_open(serialdevice, portdev);
int numlights = 0;
monocoque_serial_device serialdev = monocoque_serial_devices[serialdevice->id];
int error = 0;
if(uselights == true)
{
error = GetNumberOfLeds(serialdevice, &numlights);
slogi("numlights is %i\n", numlights);
// close, error and free if numlights is 0
serialdevice->numleds = numlights;
}
if(luafile == NULL)
{
return serialdevice->id;
}
slogi("LUA config specified for this device... initializing...");
lua_State* L = luaL_newstate();
luaL_openlibs(L);
int top=lua_gettop(L);
int status = luaL_loadfile(L, luafile);
if (status) {
/* If something went wrong, error message is at the top of */
/* the stack */
fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
lua_close(serialdevice->m.L);
return -1;
exit(1);
}
lua_setglobal(L,"myFunc");
serialdevice->m.L = L;
slogi("LUA config setup successful.");
return serialdevice->id;
}
int arduino_simled_update(SerialDevice* serialdevice, SimData* simdata)
{
int result = 1;
int total_leds = serialdevice->numleds;
size_t bufsize = (total_leds * 3) + 14;
char bytes[bufsize];
int endled = serialdevice->endled;
int startled = serialdevice->startled;
if(endled == 0)
{
endled = total_leds;
}
int num_avail_leds = endled - startled + 1;
int rpm = simdata->rpms;
int maxrpm = simdata->maxrpm;
int litleds = 0;
if(rpm > 0 && maxrpm > 0)
{
int rpmmargin = ceil(.05*maxrpm);
int rpminterval = (maxrpm-rpmmargin) / (num_avail_leds);
for (int l = 1; l <= (num_avail_leds); l++)
{
if(rpm >= (rpminterval * l))
{
litleds = l;
}
}
for(int j = 0; j < bufsize; j++)
{
bytes[j] = 0x00;
}
bytes[0] = 0xff;
bytes[1] = 0xff;
bytes[2] = 0xff;
bytes[3] = 0xff;
bytes[4] = 0xff;
bytes[5] = 0xff;
bytes[6] = 0x73;
bytes[7] = 0x6c;
bytes[8] = 0x65;
bytes[9] = 0x64;
bytes[10] = 0x73;
bytes[bufsize-1] = 0xfd;
bytes[bufsize-2] = 0xfe;
bytes[bufsize-3] = 0xff;
for(int i = 0; i < litleds; i++)
{
if(i < ((num_avail_leds) / 2))
{
//green
bytes[11 + ((i + startled - 1) * 3) + 1] = 0xff;
}
else
{
if(i < num_avail_leds - 1)
{
//yellow
bytes[11 + ((i + startled - 1) * 3) + 0] = 0xff;
bytes[11 + ((i + startled - 1) * 3) + 1] = 0xff;
}
}
if(i == num_avail_leds - 1)
{
//red
bytes[11 + ((i + startled - 1) * 3) + 0] = 0xff;
}
}
}
slogt("Updating arduino device lights to %i", litleds);
// we can add configs to set all the colors
size_t size = sizeof(bytes);
result = monocoque_serial_write(serialdevice->id, &bytes, size, arduino_timeout);
return result;
}
int arduino_customled_update(SerialDevice* serialdevice, SimData* simdata)
{
int result = 1;
int total_leds = serialdevice->numleds;
size_t bufsize = (total_leds * 3) + 14;
char ledbytes[total_leds * 3];
char bytes[bufsize];
for(int j = 0; j < bufsize; j++)
{
bytes[j] = 0x00;
}
for(int j = 0; j < total_leds * 3; j++)
{
ledbytes[j] = 0x00;
}
bytes[0] = 0xff;
bytes[1] = 0xff;
bytes[2] = 0xff;
bytes[3] = 0xff;
bytes[4] = 0xff;
bytes[5] = 0xff;
bytes[6] = 0x73;
bytes[7] = 0x6c;
bytes[8] = 0x65;
bytes[9] = 0x64;
bytes[10] = 0x73;
bytes[bufsize-1] = 0xfd;
bytes[bufsize-2] = 0xfe;
bytes[bufsize-3] = 0xff;
lua_State* L = serialdevice->m.L;
lua_pushstring(L, "buff");
lua_pushlightuserdata(L, &ledbytes);
lua_settable(L, LUA_REGISTRYINDEX);
simdata_to_lua(L, simdata);
lua_setglobal(L, "simdata");
lua_pushinteger(L, total_leds);
lua_setglobal(L, "TotalLeds");
lua_register(L, "set_led_to_color", set_led_to_color);
lua_register(L, "set_led_range_to_color", set_led_range_to_color);
lua_register(L, "set_led_to_rgb_color", set_led_to_rgb_color);
lua_register(L, "set_led_range_to_rgb_color", set_led_range_to_rgb_color);
lua_register(L, "led_clear_all", led_clear_all);
lua_register(L, "led_clear_range", led_clear_all);
lua_pushinteger(L, LUALEDCOLOR_RED);
lua_setglobal(L, "RED");
lua_pushinteger(L, LUALEDCOLOR_GREEN);
lua_setglobal(L, "GREEN");
lua_pushinteger(L, LUALEDCOLOR_BLUE);
lua_setglobal(L, "BLUE");
lua_pushinteger(L, LUALEDCOLOR_YELLOW);
lua_setglobal(L, "YELLOW");
lua_pushinteger(L, LUALEDCOLOR_ORANGE);
lua_setglobal(L, "ORANGE");
lua_getglobal(L,"myFunc");
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
{
fprintf(stderr, "Error calling Lua script: %s\n", lua_tostring(L, -1));
}
for(int i = 0; i < total_leds; i++)
{
bytes[11 + (i * 3) + 0] = ledbytes[(i * 3) + 0];
bytes[11 + (i * 3) + 1] = ledbytes[(i * 3) + 1];
bytes[11 + (i * 3) + 2] = ledbytes[(i * 3) + 2];
}
size_t size = sizeof(bytes);
result = arduino_update(serialdevice, &bytes, size);
slogt("custom led wrote %i bytes", result);
return result;
}
int arduino_customled_free(SerialDevice* serialdevice, bool lua)
{
size_t bufsize = (serialdevice->numleds * 3) + 14;
char bytes[bufsize];
int endled = serialdevice->endled;
int startled = serialdevice->startled;
if(endled == 0)
for(int j = 0; j < bufsize; j++)
{
bytes[j] = 0x00;
}
bytes[0] = 0xff;
bytes[1] = 0xff;
bytes[2] = 0xff;
bytes[3] = 0xff;
bytes[4] = 0xff;
bytes[5] = 0xff;
bytes[6] = 0x73;
bytes[7] = 0x6c;
bytes[8] = 0x65;
bytes[9] = 0x64;
bytes[10] = 0x73;
bytes[bufsize-1] = 0xfd;
bytes[bufsize-2] = 0xfe;
bytes[bufsize-3] = 0xff;
for(int i = 0; i < serialdevice->numleds; i++)
{
bytes[11 + (i * 3) + 0] = 0x00;
bytes[11 + (i * 3) + 1] = 0x00;
bytes[11 + (i * 3) + 2] = 0x00;
}
size_t size = sizeof(bytes);
int result = monocoque_serial_write_block(serialdevice->id, &bytes, size, arduino_timeout);
if(lua == false)
{
return result;
}
lua_close(serialdevice->m.L);
return result;
}
int arduino_custom_update(SerialDevice* serialdevice, SimData* simdata)
{
size_t bufsize = 14;
char bytes[bufsize];
lua_State* L = serialdevice->m.L;
simdata_to_lua(L, simdata);
lua_setglobal(L, "simdata");
lua_getglobal(L,"myFunc");
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
{
fprintf(stderr, "Error calling Lua script: %s\n", lua_tostring(L, -1));
}
lua_getglobal(L, "Message");
int result = 0;
if (lua_isstring(L, -1)) {
const char *msg = lua_tostring(L, -1);
size_t size = strlen(msg);
result = arduino_update(serialdevice, (void*) msg, size);
slogt("custom arduino wrote message %s of %i bytes", msg, result);
}
// Clean up
lua_pop(L, 1);
return result;
}

View File

@ -1,17 +1,11 @@
#ifndef _ARDUINO_H #ifndef _ARDUINO_H
#define _ARDUINO_H #define _ARDUINO_H
#include "../simdevice.h"
#include "../serialdevice.h" #include "../serialdevice.h"
int arduino_update(SerialDevice* serialdevice, SimData* simdata);
int arduino_simled_update(SerialDevice* serialdevice, SimData* simdata); int arduino_init(SerialDevice* serialdevice);
int arduino_update(SerialDevice* serialdevice, void* data, size_t size);
int arduino_custom_init(SerialDevice* serialdevice, const char* portdev, const char* luafile, bool useleds);
int arduino_customled_update(SerialDevice* serialdevice, SimData* simdata);
int arduino_custom_update(SerialDevice* serialdevice, SimData* simdata);
int arduino_init(SerialDevice* serialdevice, const char* portdev);
int arduino_free(SerialDevice* serialdevice); int arduino_free(SerialDevice* serialdevice);
int arduino_customled_free(SerialDevice* serialdevice, bool lua); int check(enum sp_return result);
#endif #endif

View File

@ -1,430 +0,0 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
#include "arduinoledlua.h"
#include "arduino.h"
#include "../../slog/slog.h"
long long ledTimeInMilliseconds(void) {
struct timeval tv;
gettimeofday(&tv,NULL);
return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000);
}
int simdata_to_lua(lua_State *L, SimData* simdata) {
// make the time up now if we are running in test mode
if(simdata->mtick == 0)
{
simdata->mtick = ledTimeInMilliseconds();
}
lua_newtable(L);
lua_pushstring(L, simdata->gearc);
lua_setfield(L, -2, "gearc");
lua_pushinteger(L, simdata->playerflag);
lua_setfield(L, -2, "playerflag");
lua_pushinteger(L, simdata->rpms);
lua_setfield(L, -2, "rpm");
lua_pushinteger(L, simdata->gear);
lua_setfield(L, -2, "gear");
lua_pushinteger(L, simdata->velocity);
lua_setfield(L, -2, "velocity");
lua_pushinteger(L, simdata->mtick);
lua_setfield(L, -2, "mtick");
lua_pushinteger(L, simdata->maxrpm);
lua_setfield(L, -2, "maxrpm");
lua_pushinteger(L, PROXCARS);
lua_setfield(L, -2, "proxcars");
lua_newtable(L);
for(int i = 0; i < PROXCARS; i++)
{
lua_newtable(L);
lua_pushinteger(L, simdata->pd[i].radius);
lua_setfield(L, -2, "radius");
lua_pushinteger(L, simdata->pd[i].theta);
lua_setfield(L, -2, "theta");
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "pd");
lua_pushnumber(L, simdata->gas);
lua_setfield(L, -2, "gas");
lua_pushnumber(L, simdata->fuel);
lua_setfield(L, -2, "fuel");
lua_pushnumber(L, simdata->turboboost);
lua_setfield(L, -2, "turboboost");
lua_newtable(L);
for(int i = 0; i < 4; i++)
{
lua_pushnumber(L, simdata->tyreRPS[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "tyreRPS");
lua_newtable(L);
for(int i = 0; i < 4; i++)
{
lua_pushnumber(L, simdata->tyrediameter[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "tyrediameter");
lua_newtable(L);
for(int i = 0; i < 4; i++)
{
lua_pushnumber(L, simdata->tyretemp[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "tyretemp");
return 0; // Return the table to Lua
}
uint8_t get_color_rgb_value(int color, int rgb)
{
switch (color)
{
case LUALEDCOLOR_RED:
switch (rgb)
{
case 0:
return 255;
case 1:
return 0;
case 2:
return 0;
}
case LUALEDCOLOR_GREEN:
switch (rgb)
{
case 0:
return 0;
case 1:
return 255;
case 2:
return 0;
}
case LUALEDCOLOR_BLUE:
switch (rgb)
{
case 0:
return 0;
case 1:
return 0;
case 2:
return 255;
}
case LUALEDCOLOR_YELLOW:
switch (rgb)
{
case 0:
return 255;
case 1:
return 255;
case 2:
return 0;
}
case LUALEDCOLOR_ORANGE:
switch (rgb)
{
case 0:
return 255;
case 1:
return 165;
case 2:
return 0;
}
default:
return 0;
}
}
int set_led_range_to_color(lua_State *L)
{
slogt("lua called c function set_led_range_to_color");
int range_start = lua_tonumber(L, 1);
int range_end = lua_tonumber(L, 2);
int color = lua_tonumber(L, 3);
slogd("lua range start is %i", range_start);
slogd("lua range end is %i", range_end);
slogd("lua color is %i", color);
range_start = range_start - 1;
lua_getglobal(L, "TotalLeds");
int numlights = 0;
if (lua_isnumber(L, -1))
{
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
if(range_end > numlights)
{
range_end = numlights;
}
if(range_end == 0)
{
slogt("Invalid range, doing nothing");
return 1;
}
lua_pushstring(L, "buff");
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("first byte of buff is x%02x", bytes[0]);
uint8_t color0 = get_color_rgb_value(color, 0);
uint8_t color1 = get_color_rgb_value(color, 1);
uint8_t color2 = get_color_rgb_value(color, 2);
for( int i = range_start; i < range_end; i++)
{
bytes[(i * 3) + 0] = color0;
bytes[(i * 3) + 1] = color1;
bytes[(i * 3) + 2] = color2;
}
}
int set_led_range_to_rgb_color(lua_State *L)
{
slogt("lua called c function set_led_range_to_rgb_color");
int range_start = lua_tonumber(L, 1);
int range_end = lua_tonumber(L, 2);
int color = lua_tonumber(L, 3);
uint8_t color0 = (color >> 16) & 0xff;
uint8_t color1 = (color >> 8) & 0xff;
uint8_t color2 = (color >> 0) & 0xff;
//int color0 = lua_tonumber(L, 3);
//int color1 = lua_tonumber(L, 4);
//int color2 = lua_tonumber(L, 5);
slogd("lua range start is %i", range_start);
slogd("lua range end is %i", range_end);
slogd("lua color0 is %i", color0);
slogd("lua color1 is %i", color1);
slogd("lua color2 is %i", color2);
range_start = range_start - 1;
lua_getglobal(L, "TotalLeds");
int numlights = 0;
if (lua_isnumber(L, -1))
{
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
if(range_end > numlights)
{
range_end = numlights;
}
if(range_end == 0)
{
return 1;
}
lua_pushstring(L, "buff");
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("first byte of buff is x%02x", bytes[0]);
for( int i = range_start; i < range_end; i++)
{
bytes[(i * 3) + 0] = color0;
bytes[(i * 3) + 1] = color1;
bytes[(i * 3) + 2] = color2;
}
}
int set_led_to_color(lua_State *L)
{
slogt("lua called c function set_led_to_rgb_color");
int led = lua_tonumber(L, 1);
int color = lua_tonumber(L, 2);
slogd("lua led is %i", led);
slogd("lua color is %i", color);
led = led - 1;
lua_getglobal(L, "TotalLeds");
int numlights = 0;
if (lua_isnumber(L, -1))
{
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
if(led > numlights)
{
return 1;
}
lua_pushstring(L, "buff");
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("first byte of buff is x%02x", bytes[0]);
uint8_t color0 = get_color_rgb_value(color, 0);
uint8_t color1 = get_color_rgb_value(color, 1);
uint8_t color2 = get_color_rgb_value(color, 2);
bytes[(led * 3) + 0] = color0;
bytes[(led * 3) + 1] = color1;
bytes[(led * 3) + 2] = color2;
}
int set_led_to_rgb_color(lua_State *L)
{
slogt("lua called c function set_led_to_rgb_color");
int led = lua_tonumber(L, 1);
int color = lua_tonumber(L, 2);
uint8_t color0 = (color >> 16) & 0xff;
uint8_t color1 = (color >> 8) & 0xff;
uint8_t color2 = (color >> 0) & 0xff;
slogd("lua led is %i", led);
slogd("lua color0 is %i", color0);
slogd("lua color1 is %i", color1);
slogd("lua color2 is %i", color2);
led = led - 1;
lua_getglobal(L, "TotalLeds");
int numlights = 0;
if (lua_isnumber(L, -1))
{
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
if(led > numlights)
{
return 1;
}
lua_pushstring(L, "buff");
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("first byte of buff is x%02x", bytes[0]);
bytes[(led * 3) + 0] = color0;
bytes[(led * 3) + 1] = color1;
bytes[(led * 3) + 2] = color2;
}
int led_clear_all(lua_State *L)
{
slogt("lua called c function led_clear_all");
lua_getglobal(L, "TotalLeds");
int numlights = 0;
if (lua_isnumber(L, -1))
{
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
lua_pushstring(L, "buff");
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("first byte of buff is x%02x", bytes[0]);
for( int i = 0; i < numlights; i++)
{
bytes[(i * 3) + 0] = 0x00;
bytes[(i * 3) + 1] = 0x00;
bytes[(i * 3) + 2] = 0x00;
}
}
int led_clear_range(lua_State *L)
{
slogt("lua called c function led_clear_range");
int range_start = lua_tonumber(L, 1);
int range_end = lua_tonumber(L, 2);
slogd("lua range start is %i", range_start);
slogd("lua range end is %i", range_end);
range_start = range_start - 1;
lua_getglobal(L, "TotalLeds");
int numlights = 0;
if (lua_isnumber(L, -1))
{
numlights = lua_tonumber(L, -1);
}
slogd("num leds is %i", numlights);
if(range_end > numlights)
{
range_end = numlights;
}
if(range_end == 0)
{
return 1;
}
lua_pushstring(L, "buff");
lua_gettable(L, LUA_REGISTRYINDEX);
char* bytes = lua_touserdata(L, -1);
slogt("first byte of buff is x%02x", bytes[0]);
for( int i = 0; i < numlights; i++)
{
bytes[(i * 3) + 0] = 0x00;
bytes[(i * 3) + 1] = 0x00;
bytes[(i * 3) + 2] = 0x00;
}
}

View File

@ -1,31 +0,0 @@
#ifndef _ARDUINOLEDLUA_H
#define _ARDUINOLEDLUA_H
//#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#ifdef USE_LUA_55
/* open all libraries */
#define luaL_openlibs(L) luaL_openselectedlibs(L, ~0, 0)
#endif
#include "../../simulatorapi/simapi/simapi/simdata.h"
typedef enum {
LUALEDCOLOR_RED = 1,
LUALEDCOLOR_GREEN,
LUALEDCOLOR_BLUE,
LUALEDCOLOR_YELLOW,
LUALEDCOLOR_ORANGE,
} LUALEDColor;
int simdata_to_lua(lua_State *L, SimData* simdata);
int set_led_range_to_color(lua_State *L);
int set_led_to_color(lua_State *L);
int set_led_range_to_rgb_color(lua_State *L);
int set_led_to_rgb_color(lua_State *L);
int led_clear_all(lua_State *L);
int led_clear_range(lua_State *L);
#endif

View File

@ -1,91 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <hidapi/hidapi.h>
#include "moza.h"
#include "../serialadapter.h"
#include "../../slog/slog.h"
#define MOZA_TIMEOUT 1000
#define MOZA_SERIAL_TEMPLATE {0x7e, 0x06, 0x41, 0x13, 0xfd, 0xde, 0x0, 0x0, 0x0, 0x0, 0x0}
#define MOZA_NUM_AVAILABLE_LEDS 10
#define MOZA_BLINKING_BIT 7
#define MOZA_PAYLOAD_SIZE 11
#define MOZA_MAGIC_VALUE 0x0d
#define BIT(nr) (1UL << (nr))
unsigned char moza_checksum(unsigned char *data, unsigned short size)
{
unsigned int ret = MOZA_MAGIC_VALUE;
for (unsigned short i = 0; i < size; i++)
{
ret += data[i];
}
return ret % 0x100;
}
int moza_update(SerialDevice* serialdevice, unsigned short rpm, unsigned short maxrpm)
{
unsigned char bytes[] = MOZA_SERIAL_TEMPLATE;
unsigned short size = MOZA_PAYLOAD_SIZE;
float perctflt = ((float)rpm/(float)maxrpm)*100;
int perct = round(perctflt);
if (perct >= 10)
bytes[9] |= BIT(0);
if (perct >= 20)
bytes[9] |= BIT(1);
if (perct >= 30)
bytes[9] |= BIT(2);
if (perct >= 40)
bytes[9] |= BIT(3);
if (perct >= 50)
bytes[9] |= BIT(4);
if (perct >= 60)
bytes[9] |= BIT(5);
if (perct >= 70)
bytes[9] |= BIT(6);
if (perct >= 80)
bytes[9] |= BIT(7);
if (perct >= 90)
bytes[8] |= BIT(0);
if (perct >= 92)
bytes[8] |= BIT(1);
// blinking
if (perct >= 94)
bytes[8] |= BIT(MOZA_BLINKING_BIT);
bytes[10] = moza_checksum(bytes, size);
int result = 1;
if (serialdevice->port)
{
slogd("copying %i bytes to moza device", MOZA_PAYLOAD_SIZE);
slogt("writing bytes %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x from rpm %i maxrpm %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], rpm, maxrpm);
result = monocoque_serial_write(serialdevice->id, bytes, size, MOZA_TIMEOUT);
}
return result;
}
int moza_init(SerialDevice* serialdevice, const char* portdev)
{
int id = monocoque_serial_open(serialdevice, portdev);
if (id < 0) return id;
serialdevice->id = id;
return 0;
}

View File

@ -1,11 +0,0 @@
#ifndef _MOZA_H
#define _MOZA_H
#include "../serialdevice.h"
#include "../simdevice.h"
int moza_update(SerialDevice* serialdevice, unsigned short rpm, unsigned short maxrpm);
int moza_init(SerialDevice* serialdevice, const char* portdev);
unsigned char moza_checksum(unsigned char *data, unsigned short size);
#endif

View File

@ -1,142 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <hidapi/hidapi.h>
#include "moza.h"
#include "moza_ks_pro_wheel.h"
#include "../serialadapter.h"
#include "../../slog/slog.h"
#define MOZA_TIMEOUT 1000
#define MOZA_MAGIC_VALUE 0x0d
#define MOZA_RPM_MASK_TEMPLATE {0x7e, 0x06, 0x3f, 0x17, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
#define MOZA_RPM_MASK_PAYLOAD_SIZE 11
#define MOZA_RPM_COLOR_PAYLOAD_1 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0xff, 0, 0, 4, 0xff, 0, 0, 0}
#define MOZA_RPM_COLOR_PAYLOAD_2 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 5, 0xff, 0, 0, 6, 0xff, 0, 0, 7, 0xff, 0, 0, 8, 0xff, 0, 0, 9, 0xff, 0, 0, 0}
#define MOZA_RPM_COLOR_PAYLOAD_3 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 10, 0xff, 0x7f, 0, 11, 0xff, 0x7f, 0, 12, 0xff, 0x7f, 0, 13, 0, 0, 0xff, 14, 0, 0, 0xff, 0}
#define MOZA_BTN_COLOR_PAYLOAD_1 {0x7e, 0x16, 0x3f, 0x17, 0x19, 1, 0, 0xff, 0, 0, 1, 0xff, 0, 0, 2, 0xff, 0, 0, 3, 0xff, 0, 0, 4, 0xff, 0, 0, 0}
#define MOZA_BTN_COLOR_PAYLOAD_2 {0x7e, 0x16, 0x3f, 0x17, 0x19, 1, 5, 0xff, 0, 0, 6, 0xff, 0, 0, 7, 0xff, 0, 0, 8, 0xff, 0, 0, 9, 0xff, 0, 0, 0}
#define MOZA_COLOR_PAYLOAD_SIZE 27
#define MOZA_FLAG_COLOR_YELLOW_LEFT {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 0, 0xff, 0xaa, 0, 1, 0xff, 0xaa, 0, 2, 0xff, 0xaa, 0, 0, 0xff, 0xaa, 0, 1, 0xff, 0xaa, 0, 0}
#define MOZA_FLAG_COLOR_YELLOW_RIGHT {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 15, 0xff, 0xaa, 0, 16, 0xff, 0xaa, 0, 17, 0xff, 0xaa, 0, 15, 0xff, 0xaa, 0, 16, 0xff, 0xaa, 0, 0}
#define MOZA_FLAG_COLOR_OFF_LEFT {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}
#define MOZA_FLAG_COLOR_OFF_RIGHT {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 15, 0, 0, 0, 16, 0, 0, 0, 17, 0, 0, 0, 15, 0, 0, 0, 16, 0, 0, 0, 0}
#define BIT(nr) (1UL << (nr))
int moza_ks_pro_wheel_update(SerialDevice* serialdevice, SimData* simData)
{
static uint8_t last_flag = 0xff;
unsigned char bytes[] = MOZA_RPM_MASK_TEMPLATE;
int size = MOZA_RPM_MASK_PAYLOAD_SIZE;
float perctflt = ((float)simData->rpms/(float)simData->maxrpm)*100;
int perct = round(perctflt);
if (perct >= 98 && (simData->mtick >> 7) & 1 == 1) perct = 0;
if (perct >= 75)
bytes[6] |= BIT(3);
if (perct >= 77)
bytes[6] |= BIT(4);
if (perct >= 79)
bytes[6] |= BIT(5);
if (perct >= 81)
bytes[6] |= BIT(6);
if (perct >= 83)
bytes[6] |= BIT(7);
if (perct >= 85)
bytes[7] |= BIT(0);
if (perct >= 87)
bytes[7] |= BIT(1);
if (perct >= 89)
bytes[7] |= BIT(2);
if (perct >= 91)
bytes[7] |= BIT(3);
if (perct >= 93)
bytes[7] |= BIT(4);
if (perct >= 95)
bytes[7] |= BIT(5);
if (perct >= 97)
bytes[7] |= BIT(6);
if (simData->playerflag == SIMAPI_FLAG_YELLOW) {
bytes[6] |= BIT(0) | BIT(1) | BIT(2); // left 3 flag LEDs (indices 0, 1, 2)
bytes[7] |= BIT(7); // right flag LED (index 15)
bytes[8] |= BIT(0) | BIT(1); // right flag LEDs (indices 16, 17)
}
bytes[10] = moza_checksum(bytes, size);
int result = 1;
if (serialdevice->port)
{
if (simData->playerflag != last_flag) {
last_flag = simData->playerflag;
if (simData->playerflag == SIMAPI_FLAG_YELLOW) {
unsigned char fl[] = MOZA_FLAG_COLOR_YELLOW_LEFT;
unsigned char fr[] = MOZA_FLAG_COLOR_YELLOW_RIGHT;
fl[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(fl, MOZA_COLOR_PAYLOAD_SIZE);
fr[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(fr, MOZA_COLOR_PAYLOAD_SIZE);
monocoque_serial_write(serialdevice->id, fl, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, fr, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
} else {
unsigned char fl[] = MOZA_FLAG_COLOR_OFF_LEFT;
unsigned char fr[] = MOZA_FLAG_COLOR_OFF_RIGHT;
fl[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(fl, MOZA_COLOR_PAYLOAD_SIZE);
fr[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(fr, MOZA_COLOR_PAYLOAD_SIZE);
monocoque_serial_write(serialdevice->id, fl, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, fr, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
}
}
slogd("copying %i bytes to moza device", MOZA_RPM_MASK_PAYLOAD_SIZE);
slogt("writing bytes %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x from rpm %i maxrpm %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], simData->rpms, simData->maxrpm);
result = monocoque_serial_write(serialdevice->id, bytes, size, MOZA_TIMEOUT);
}
return result;
}
int moza_ks_pro_wheel_init(SerialDevice* serialdevice, const char* portdev)
{
serialdevice->id = monocoque_serial_open(serialdevice, portdev);
if (serialdevice->id == -1) return serialdevice->id;
unsigned char p1[] = MOZA_RPM_COLOR_PAYLOAD_1;
unsigned char p2[] = MOZA_RPM_COLOR_PAYLOAD_2;
unsigned char p3[] = MOZA_RPM_COLOR_PAYLOAD_3;
unsigned char p4[] = MOZA_BTN_COLOR_PAYLOAD_1;
unsigned char p5[] = MOZA_BTN_COLOR_PAYLOAD_2;
p1[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p1, MOZA_COLOR_PAYLOAD_SIZE);
p2[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p2, MOZA_COLOR_PAYLOAD_SIZE);
p3[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p3, MOZA_COLOR_PAYLOAD_SIZE);
p4[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p4, MOZA_COLOR_PAYLOAD_SIZE);
p5[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p5, MOZA_COLOR_PAYLOAD_SIZE);
monocoque_serial_write(serialdevice->id, p1, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, p2, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, p3, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, p4, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, p5, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
unsigned char p6[] = MOZA_FLAG_COLOR_OFF_RIGHT;
p6[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p6, MOZA_COLOR_PAYLOAD_SIZE);
monocoque_serial_write(serialdevice->id, p6, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
return serialdevice->id;
}

View File

@ -1,10 +0,0 @@
#ifndef _MOZA_KS_PRO_WHEEL_H
#define _MOZA_KS_PRO_WHEEL_H
#include "../serialdevice.h"
#include "../simdevice.h"
int moza_ks_pro_wheel_update(SerialDevice* serialdevice, SimData* simData);
int moza_ks_pro_wheel_init(SerialDevice* serialdevice, const char* portdev);
#endif

View File

@ -1,98 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <hidapi/hidapi.h>
#include "moza.h"
#include "moza_new.h"
#include "../serialadapter.h"
#include "../../slog/slog.h"
#define MOZA_TIMEOUT 1000
#define MOZA_MAGIC_VALUE 0x0d
#define MOZA_RPM_MASK_TEMPLATE {0x7e, 0x06, 0x3f, 0x17, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
#define MOZA_RPM_MASK_PAYLOAD_SIZE 11
#define MOZA_RPM_COLOR_PAYLOAD_1 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 0, 0, 0xff, 0, 1, 0, 0xff, 0, 2, 0, 0xff, 0, 3, 0xff, 0x7f, 0, 4, 0xff, 0x7f, 0, 0}
#define MOZA_RPM_COLOR_PAYLOAD_2 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 5, 0xff, 0x7f, 0, 6, 0xff, 0x7f, 0, 7, 0xff, 0, 0, 8, 0xff, 0, 0, 9, 0xff, 0, 0, 0}
#define MOZA_BTN_COLOR_PAYLOAD_1 {0x7e, 0x16, 0x3f, 0x17, 0x19, 1, 0, 0xff, 0, 0, 1, 0xff, 0, 0, 2, 0xff, 0, 0, 3, 0xff, 0, 0, 4, 0xff, 0, 0, 0}
#define MOZA_BTN_COLOR_PAYLOAD_2 {0x7e, 0x16, 0x3f, 0x17, 0x19, 1, 5, 0xff, 0, 0, 6, 0xff, 0, 0, 7, 0xff, 0, 0, 8, 0xff, 0, 0, 9, 0xff, 0, 0, 0}
#define MOZA_COLOR_PAYLOAD_SIZE 27
#define BIT(nr) (1UL << (nr))
int moza_new_update(SerialDevice* serialdevice, SimData* simData)
{
unsigned char bytes[] = MOZA_RPM_MASK_TEMPLATE;
int size = MOZA_RPM_MASK_PAYLOAD_SIZE;
float perctflt = ((float)simData->rpms/(float)simData->maxrpm)*100;
int perct = round(perctflt);
if (perct >= 98 && (simData->mtick >> 7) & 1 == 1) perct = 0;
if (perct >= 75)
bytes[6] |= BIT(0);
if (perct >= 79)
bytes[6] |= BIT(1);
if (perct >= 82)
bytes[6] |= BIT(2);
if (perct >= 85)
bytes[6] |= BIT(3);
if (perct >= 87)
bytes[6] |= BIT(4);
if (perct >= 88)
bytes[6] |= BIT(5);
if (perct >= 89)
bytes[6] |= BIT(6);
if (perct >= 90)
bytes[6] |= BIT(7);
if (perct >= 92)
bytes[7] |= BIT(0);
if (perct >= 94)
bytes[7] |= BIT(1);
bytes[10] = moza_checksum(bytes, size);
int result = 1;
if (serialdevice->port)
{
slogd("copying %i bytes to moza device", MOZA_RPM_MASK_PAYLOAD_SIZE);
slogt("writing bytes %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x from rpm %i maxrpm %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], simData->rpms, simData->maxrpm);
result = monocoque_serial_write(serialdevice->id, bytes, size, MOZA_TIMEOUT);
}
return result;
}
int moza_new_init(SerialDevice* serialdevice, const char* portdev)
{
int id = monocoque_serial_open(serialdevice, portdev);
if (id < 0) return id;
serialdevice->id = id;
unsigned char p1[] = MOZA_RPM_COLOR_PAYLOAD_1;
unsigned char p2[] = MOZA_RPM_COLOR_PAYLOAD_2;
unsigned char p3[] = MOZA_BTN_COLOR_PAYLOAD_1;
unsigned char p4[] = MOZA_BTN_COLOR_PAYLOAD_2;
p1[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p1, MOZA_COLOR_PAYLOAD_SIZE);
p2[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p2, MOZA_COLOR_PAYLOAD_SIZE);
p3[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p3, MOZA_COLOR_PAYLOAD_SIZE);
p4[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p4, MOZA_COLOR_PAYLOAD_SIZE);
unsigned char* payloads[] = {p1, p2, p3, p4};
for (int i = 0; i < 4; i++) {
int result = monocoque_serial_write(serialdevice->id, payloads[i], MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
if (result < 0) return result;
}
return 0;
}

View File

@ -1,10 +0,0 @@
#ifndef _MOZA_NEW_H
#define _MOZA_NEW_H
#include "../serialdevice.h"
#include "../simdevice.h"
int moza_new_update(SerialDevice* serialdevice, SimData* simData);
int moza_new_init(SerialDevice* serialdevice, const char* portdev);
#endif

View File

@ -1,280 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "serialadapter.h"
#include "../slog/slog.h"
int msastrcicmp(char const *a, char const *b)
{
for (;; a++, b++) {
int d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
if (d != 0 || !*a)
return d;
}
}
int check(enum sp_return result)
{
char* error_message;
switch (result)
{
case SP_ERR_ARG:
return 1;
case SP_ERR_FAIL:
error_message = sp_last_error_message();
sloge("error: serial write failed: %s", error_message);
sp_free_error_message(error_message);
return result;
case SP_ERR_SUPP:
printf("Error: Not supported.\n");
return result;
case SP_ERR_MEM:
printf("Error: Couldn't allocate memory.\n");
return result;
case SP_OK:
default:
return result;
}
}
int monocoque_serial_free(SerialDevice* serialdevice)
{
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevice->id];
if(monocoque_serial_dev.open == true)
{
while(monocoque_serial_dev.busy == true)
{
slogt("hopefully this doesn't happen long");
continue;
}
monocoque_serial_devices[serialdevice->id].busy = true;
monocoque_serial_devices[serialdevice->id].refs--;
if(monocoque_serial_devices[serialdevice->id].refs == 0)
{
slogd("freeing physical device %s", monocoque_serial_devices[serialdevice->id].portname);
sp_close(monocoque_serial_devices[serialdevice->id].port);
sp_free_port(monocoque_serial_devices[serialdevice->id].port);
free(monocoque_serial_devices[serialdevice->id].portname);
monocoque_serial_devices[serialdevice->id].port = NULL;
monocoque_serial_devices[serialdevice->id].portname = NULL;
monocoque_serial_devices[serialdevice->id].busy = false;
monocoque_serial_devices[serialdevice->id].open = false;
monocoque_serial_devices[serialdevice->id].openfail = false;
}
else
{
monocoque_serial_devices[serialdevice->id].busy = false;
}
}
}
int monocoque_wait_for_event(uint8_t serialdevicenum, int event)
{
slogt("serial device id %i", serialdevicenum);
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevicenum];
int retval;
struct sp_event_set* eventSet = NULL;
retval = sp_new_event_set(&eventSet);
if (retval == SP_OK)
{
retval = sp_add_port_events(eventSet, monocoque_serial_dev.port, event);
if (retval == SP_OK)
{
slogd("set event on port");
retval = sp_wait(eventSet, 5000);
}
else
{
sloge("Unable to add events to port.");
retval = -1;
}
}
else
{
sloge("Unable to create new event set.");
retval = -1;
}
sp_free_event_set(eventSet);
return retval;
}
int monocoque_input_wait(uint8_t serialdevicenum)
{
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevicenum];
return sp_input_waiting(monocoque_serial_dev.port);
}
// Helper function to get and validate device
static monocoque_serial_device* monocoque_get_serial_device(uint8_t serialdevicenum)
{
monocoque_serial_device* dev = &monocoque_serial_devices[serialdevicenum];
slogt("serial device id %i", serialdevicenum);
slogt("port name: %s, busy %i, open %i, openfail %i", dev->portname, dev->busy, dev->open, dev->openfail);
if(dev->port == NULL)
{
sloge("port is null");
}
return dev;
}
int monocoque_serial_write(uint8_t serialdevicenum, void* data, size_t size, int timeout)
{
monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
int result = -1;
if(dev->busy == false && dev->open == true)
{
dev->busy = true;
result = sp_blocking_write(dev->port, data, size, timeout);
}
else
{
slogw("serial device data update ignored due to busy or lost device");
result = -1;
}
slogt("write result is %i", result);
dev->busy = false;
return result;
}
int monocoque_serial_write_block(uint8_t serialdevicenum, void* data, size_t size, int timeout)
{
monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
int result = -1;
if(dev->open == true)
{
while(dev->busy == true)
{
slogt("hopefully this doesn't happen long");
continue;
}
dev->busy = true;
result = sp_blocking_write(dev->port, data, size, timeout);
slogi("actually performed write");
}
dev->busy = false;
return result;
}
int monocoque_serial_read_block(uint8_t serialdevicenum, void* data, size_t size, int timeout)
{
monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
int result = -1;
if(dev->open == true)
{
while(dev->busy == true)
{
slogt("hopefully this doesn't happen long");
continue;
}
dev->busy = true;
result = sp_blocking_read(dev->port, data, size, timeout);
slogi("actually performed read");
}
dev->busy = false;
return result;
}
int monocoque_serial_open(SerialDevice* serialdevice, const char* portdev)
{
int serial_device_num = -1;
bool notfound = true;
slogi("looking to open physical serialdevice %s", portdev);
for(int i = 0; i < 10; i++)
{
if(monocoque_serial_devices[i].open == true && monocoque_serial_devices[i].openfail == false)
{
if(msastrcicmp(monocoque_serial_devices[i].portname, portdev) == 0)
{
notfound = false;
serial_device_num = i;
monocoque_serial_devices[i].refs++;
slogd("found exisiting handle to serial device %s", portdev);
}
}
}
if(notfound == true)
{
slogd("no existing connections found, looking to create new");
int i = -1;
bool avail = false;
while(avail == false)
{
i++;
if(monocoque_serial_devices[i].open == false && monocoque_serial_devices[i].openfail == false)
{
avail = true;
break;
}
}
slogi("opening physical serial device...");
int error = 0;
char* port_name = strdup(portdev);
monocoque_serial_devices[i].portname = strdup(port_name);
struct sp_port* sp;
slogd("Looking for port %s", port_name);
error = check(sp_get_port_by_name(port_name, &sp));
if (error != 0)
{
sloge("Error opening serial port");
free(port_name);
free(monocoque_serial_devices[i].portname);
monocoque_serial_devices[i].portname = NULL;
monocoque_serial_devices[i].open = false;
monocoque_serial_devices[i].openfail = false;
return -1;
}
slogd("Opening port");
check(sp_open(sp, SP_MODE_READ | SP_MODE_WRITE));
slogd("Setting port to %i 8N1, no flow control", serialdevice->baudrate);
check(sp_set_baudrate(sp, serialdevice->baudrate));
check(sp_set_bits(sp, 8));
check(sp_set_parity(sp, SP_PARITY_NONE));
check(sp_set_stopbits(sp, 1));
check(sp_set_flowcontrol(sp, SP_FLOWCONTROL_NONE));
check(sp_set_rts(sp, 1));
check(sp_set_dtr(sp, 1));
monocoque_serial_devices[i].port = sp;
monocoque_serial_devices[i].open = true;
monocoque_serial_devices[i].openfail = false;
monocoque_serial_devices[i].busy = false;
monocoque_serial_devices[i].refs++;
serial_device_num = i;
free(port_name);
slogd("Successfully setup monocoque serial device...");
}
return serial_device_num;
}

View File

@ -1,31 +0,0 @@
#ifndef _SERIALADAPTER_H
#define _SERIALADAPTER_H
#include <stdint.h>
#include <stdbool.h>
#include <libserialport.h>
#include "simdevice.h"
typedef struct
{
char* portname;
struct sp_port* port;
uint8_t refs;
bool open;
bool openfail;
bool busy;
}
monocoque_serial_device;
static monocoque_serial_device monocoque_serial_devices[20];
int monocoque_input_wait(uint8_t serialdevicenum);
int monocoque_wait_for_event(uint8_t serialdevicenum, int event);
int monocoque_serial_write(uint8_t serialdevicenum, void* data, size_t size, int timeout);
int monocoque_serial_write_block(uint8_t serialdevicenum, void* data, size_t size, int timeout);
int monocoque_serial_read_block(uint8_t serialdevicenum, void* data, size_t size, int timeout);
int monocoque_serial_open(SerialDevice* serialdevice, const char* port);
int monocoque_serial_free(SerialDevice* serialdevice);
#endif

View File

@ -2,407 +2,34 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <math.h>
#include "simdevice.h"
#include "serialdevice.h" #include "serialdevice.h"
#include "serialadapter.h"
#include "hapticeffect.h"
#include "serial/arduino.h" #include "serial/arduino.h"
#include "serial/moza.h"
#include "serial/moza_new.h"
#include "serial/moza_ks_pro_wheel.h"
#include "../helper/parameters.h" #include "../helper/parameters.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simdata.h"
#include "../slog/slog.h" #include "../slog/slog.h"
#define KPHTOMPH .621317 int serialdev_update(SerialDevice* serialdevice, SimData* simdata)
int serial_wheel_update(SimDevice* this, SimData* simdata)
{ {
SerialDevice* serialdevice = (void *) this->derived; arduino_update(serialdevice, simdata);
switch (serialdevice->devicetype) {
case SIMDEVSUBTYPE_MOZA_NEW:
moza_new_update(serialdevice, simdata);
break;
case SERIALDEV__MOZA_KS_PRO_WHEEL:
moza_ks_pro_wheel_update(serialdevice, simdata);
break;
case SIMDEVSUBTYPE_MOZAR5:
default:
moza_update(serialdevice, simdata->rpms, simdata->maxrpm);
break;
}
return 0; return 0;
} }
int serialdev_update(SimDevice* this, SimData* simdata) int serialdev_free(SerialDevice* serialdevice)
{ {
SerialDevice* serialdevice = (void *) this->derived; arduino_free(serialdevice);
arduino_update(serialdevice, simdata, sizeof(SimData));
return 0; return 0;
} }
int arduino_custom_updater(SimDevice* this, SimData* simdata) int serialdev_init(SerialDevice* serialdevice)
{ {
SerialDevice* serialdevice = (void *) this->derived; slogi("initializing serial device...");
arduino_custom_update(serialdevice, simdata);
return 0;
}
int arduino_customled_updater(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
arduino_customled_update(serialdevice, simdata);
return 0;
}
int arduino_simled_updater(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
arduino_simled_update(serialdevice, simdata);
return 0;
}
int arduino_shiftlights_update(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
int result = 1;
int num_avail_leds = serialdevice->numlights;
int rpm = simdata->rpms;
int maxrpm = simdata->maxrpm;
int litleds = 0;
if(rpm > 0 && maxrpm > 0)
{
int rpmmargin = ceil(.05*maxrpm);
int rpminterval = (maxrpm-rpmmargin) / (num_avail_leds);
for (int l = 1; l <= (num_avail_leds); l++)
{
if(rpm >= (rpminterval * l))
{
litleds = l;
}
}
}
serialdevice->u.shiftlightsdata.litleds = litleds;
//serialdevice->u.shiftlightsdata.rpm = simdata->rpms;
slogt("Updating arduino device lights to %i", serialdevice->u.shiftlightsdata.litleds);
// we can add configs to set all the colors
// i can move the size to the initialization since it should not change
size_t size = sizeof(ShiftLightsData);
arduino_update(serialdevice, &serialdevice->u.shiftlightsdata, size);
return result;
}
int arduino_simwind_update(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
int result = 1;
// sending over as mph, when you consider 255mph to be a reasonable upper limit
serialdevice->u.simwinddata.velocity = ceil(simdata->velocity*KPHTOMPH);
slogt("Updating arduino device speed to %i", serialdevice->u.simwinddata.velocity);
serialdevice->u.simwinddata.fanpower = (int)(serialdevice->fanpower * 255);
slogt("Sending fanpower: %i (from config: %f)", serialdevice->u.simwinddata.fanpower, serialdevice->fanpower);
size_t size = sizeof(SimWindData);
arduino_update(serialdevice, &serialdevice->u.simwinddata, size);
return result;
}
int arduino_simhaptic_update(SimDevice* this, SimData* simdata)
{
SerialDevice* serialdevice = (void *) this->derived;
int result = 1;
slogt("arduino haptic device updating");
double play = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
double rplay = play;
play = play * serialdevice->ampfactor;
if(play > 1.0)
{
play = 1.0;
}
int effectspeed = ceil(255 * play);
int motor = serialdevice->motorsposition;
if (play != serialdevice->state)
{
if (motor == 0 || motor == 4 || motor == 7 || motor == 8 || motor == 10 || motor == 11 || motor == 13 || motor == 14)
{
serialdevice->u.simhapticdata.effect1 = effectspeed;
serialdevice->u.simhapticdata.motor1 = 1;
slogt("Updating arduino haptic device with effect type %i speed motor speed %i on motor %i from original effect %f with ampfactor %f", this->hapticeffect.effecttype, serialdevice->u.simhapticdata.effect3, serialdevice->motorsposition, rplay, serialdevice->ampfactor);
}
if (motor == 2 || motor == 6 || motor == 8 || motor == 9 || motor == 10 || motor == 11 || motor == 12 || motor == 14)
{
serialdevice->u.simhapticdata.effect3 = effectspeed;
serialdevice->u.simhapticdata.motor3 = 1;
slogt("Updating arduino haptic device with effect type %i speed motor speed %i on motor %i from original effect %f with ampfactor %f", this->hapticeffect.effecttype, serialdevice->u.simhapticdata.effect3, serialdevice->motorsposition, rplay, serialdevice->ampfactor);
}
serialdevice->state = play;
}
size_t size = sizeof(SimHapticData);
arduino_update(serialdevice, &serialdevice->u.simhapticdata, size);
return result;
}
int serialdev_free(SimDevice* this)
{
SerialDevice* serialdevice = (void *) this->derived;
switch (serialdevice->devicetype) {
case (ARDUINODEV__HAPTIC):
serialdevice->u.simhapticdata.effect1 = 0;
serialdevice->u.simhapticdata.motor1 = 1;
serialdevice->u.simhapticdata.effect2 = 0;
serialdevice->u.simhapticdata.motor2 = 1;
serialdevice->u.simhapticdata.effect3 = 0;
serialdevice->u.simhapticdata.motor3 = 1;
serialdevice->u.simhapticdata.effect4 = 0;
serialdevice->u.simhapticdata.motor4 = 1;
serialdevice->state = 0;
size_t size = sizeof(SimHapticData);
monocoque_serial_write_block(serialdevice->id, &serialdevice->u.simhapticdata, size, 9000);
slogi("set zero to arduino device");
break;
case ARDUINODEV__SIMLED__CUSTOM:
//arduino_customled_free(serialdevice, true);
free(serialdevice->m.device_specific_config_file);
break;
case ARDUINODEV__SIMLED:
arduino_customled_free(serialdevice, false);
break;
}
monocoque_serial_free(serialdevice);
free(serialdevice);
return 0;
}
int serial_wheel_free(SimDevice* this)
{
SerialDevice* serialdevice = (void *) this->derived;
monocoque_serial_free(serialdevice);
free(serialdevice);
return 0;
}
int serialdev_init(SerialDevice* serialdevice, DeviceSettings* ds, SimInfo* siminfo)
{
slogi("initializing serial device on port %s to %i...", ds->serialdevsettings.portdev, ds->serialdevsettings.baud);
int error = 0; int error = 0;
serialdevice->type = SERIALDEV_UNKNOWN;
serialdevice->type = SERIALDEV_ARDUINO;
serialdevice->motorsposition = ds->serialdevsettings.motorsposition; error = arduino_init(serialdevice);
serialdevice->baudrate = ds->serialdevsettings.baud;
switch (serialdevice->devicetype)
{
case SERIALDEV__MOZAR5:
// the wheel stuff assumed it was a usb
//error = wheeldev_init(&serialdevice->u.wheeldevice, ds);
// maybe this call a more generic serial wheel init first
error = moza_init(serialdevice, ds->serialdevsettings.portdev);
break;
case SERIALDEV__MOZA_NEW:
error = moza_new_init(serialdevice, ds->serialdevsettings.portdev);
break;
case SERIALDEV__MOZA_KS_PRO_WHEEL:
error = moza_ks_pro_wheel_init(serialdevice, ds->serialdevsettings.portdev);
break;
case ARDUINODEV__SIMLED__CUSTOM:
serialdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
error = arduino_custom_init(serialdevice, ds->serialdevsettings.portdev, serialdevice->m.device_specific_config_file, true);
if(error < 0)
{
free(serialdevice->m.device_specific_config_file);
}
break;
case ARDUINODEV__CUSTOM:
serialdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
error = arduino_custom_init(serialdevice, ds->serialdevsettings.portdev, serialdevice->m.device_specific_config_file, false);
if(error < 0)
{
free(serialdevice->m.device_specific_config_file);
}
break;
case ARDUINODEV__SIMLED:
error = arduino_custom_init(serialdevice, ds->serialdevsettings.portdev, NULL, false);
break;
default:
error = arduino_init(serialdevice, ds->serialdevsettings.portdev);
break;
}
return error; return error;
} }
static const vtable serial_simdevice_vtable = { &serialdev_update, &serialdev_free };
static const vtable arduino_shiftlights_vtable = { &arduino_shiftlights_update, &serialdev_free };
static const vtable arduino_simled_vtable = { &arduino_simled_updater, &serialdev_free };
static const vtable arduino_custom_vtable = { &arduino_custom_updater, &serialdev_free };
static const vtable arduino_simled_custom_vtable = { &arduino_customled_updater, &serialdev_free };
static const vtable arduino_simwind_vtable = { &arduino_simwind_update, &serialdev_free };
static const vtable arduino_simhaptic_vtable = { &arduino_simhaptic_update, &serialdev_free };
static const vtable serialwheel_vtable = { &serial_wheel_update, &serial_wheel_free };
SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) {
SerialDevice* this = (SerialDevice*) malloc(sizeof(SerialDevice));
this->m.update = &update;
this->m.free = &simdevfree;
this->m.derived = this;
this->m.vtable = &serial_simdevice_vtable;
this->type = SERIALDEV_ARDUINO;
int error = 0;
slogt("Attempting to configure arduino device with subtype: %i", ds->dev_subtype);
switch (ds->dev_subtype) {
case (SIMDEVTYPE_SHIFTLIGHTS):
this->devicetype = ARDUINODEV__SHIFTLIGHTS;
this->numlights = ds->serialdevsettings.numlights;
this->m.vtable = &arduino_shiftlights_vtable;
slogi("Initializing arduino device for shiftlights.");
break;
case (SIMDEVTYPE_SIMLED):
if(ds->has_config == true)
{
this->devicetype = ARDUINODEV__SIMLED__CUSTOM;
this->startled = ds->serialdevsettings.startled;
this->endled = ds->serialdevsettings.endled;
this->m.vtable = &arduino_simled_custom_vtable;
slogi("Initializing arduino device for custom simled.");
}
else
{
this->devicetype = ARDUINODEV__SIMLED;
this->numleds = ds->serialdevsettings.numleds;
this->startled = ds->serialdevsettings.startled;
this->endled = ds->serialdevsettings.endled;
this->m.vtable = &arduino_simled_vtable;
slogi("Initializing arduino device for simled.");
}
break;
case (SIMDEVTYPE_SIMWIND):
this->devicetype = ARDUINODEV__SIMWIND;
this->m.vtable = &arduino_simwind_vtable;
this->fanpower = ds->serialdevsettings.fanpower;
slogi("Initializing arduino devices for sim wind with fanpower: %f", this->fanpower);
break;
case (SIMDEVTYPE_ARDUINOCUSTOM):
this->devicetype = ARDUINODEV__CUSTOM;
this->m.vtable = &arduino_custom_vtable;
slogi("Initializing custom arduino device.");
break;
case (SIMDEVTYPE_SERIALHAPTIC):
if(siminfo->SimSupportsHapticEffects == false)
{
error = MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE;
}
this->devicetype = ARDUINODEV__HAPTIC;
this->m.vtable = &arduino_simhaptic_vtable;
this->u.simhapticdata.motor1 = 0;
this->u.simhapticdata.motor2 = 0;
this->u.simhapticdata.motor3 = 0;
this->u.simhapticdata.motor4 = 0;
this->u.simhapticdata.effect1 = 0;
this->u.simhapticdata.effect2 = 0;
this->u.simhapticdata.effect3 = 0;
this->u.simhapticdata.effect4 = 0;
this->state = 0;
this->ampfactor = ds->serialdevsettings.ampfactor;
this->fanpower = ds->serialdevsettings.fanpower;
slogi("Initializing arduino device for haptic effects.");
break;
case (SIMDEVTYPE_SERIALWHEEL):
this->type = SERIALDEV_WHEEL;
switch (ds->dev_subsubtype) {
case SIMDEVSUBTYPE_MOZA_NEW:
slogi("Initializing new firmware Moza serial wheel device.");
this->devicetype = SERIALDEV__MOZA_NEW;
this->m.vtable = &serialwheel_vtable;
break;
case SIMDEVSUBTYPE_MOZA_KS_PRO_WHEEL:
slogi("Initializing new firmware Moza serial wheel device.");
this->devicetype = SERIALDEV__MOZA_KS_PRO_WHEEL;
this->m.vtable = &serialwheel_vtable;
break;
case SIMDEVSUBTYPE_MOZAR5:
default:
//move this stuff to wheeldevice or it's own serial wheel device module
slogi("Initializing Moza serial wheel device.");
this->devicetype = SERIALDEV__MOZAR5;
this->m.vtable = &serialwheel_vtable;
break;
}
}
if(this->devicetype == ARDUINODEV__HAPTIC && error == 0)
{
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.effecttype = ds->effect_type;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
}
if(error == 0)
{
slogi("Starting serial device initialization");
error = serialdev_init(this, ds, siminfo);
}
if (error != 0)
{
slogw("Did not initialize usb device due to error code %i", error);
free(this);
return NULL;
}
return this;
}

View File

@ -2,20 +2,26 @@
#define _SERIALDEVICE_H #define _SERIALDEVICE_H
#include <libserialport.h> #include <libserialport.h>
#include "wheeldevice.h" #include "../helper/parameters.h"
#include "../simulatorapi/simdata.h"
typedef enum typedef enum
{ {
ARDUINODEV__SHIFTLIGHTS = 0, SERIALDEV_UNKNOWN = 0,
ARDUINODEV__SIMWIND = 1, SERIALDEV_ARDUINO = 1
ARDUINODEV__HAPTIC = 2,
SERIALDEV__MOZAR5 = 3,
ARDUINODEV__SIMLED = 4,
ARDUINODEV__SIMLED__CUSTOM = 5,
ARDUINODEV__CUSTOM = 6,
SERIALDEV__MOZA_NEW = 7,
SERIALDEV__MOZA_KS_PRO_WHEEL = 8
} }
SerialDeviceType; SerialType;
typedef struct
{
int id;
SerialType type;
struct sp_port* port;
}
SerialDevice;
int serialdev_update(SerialDevice* serialdevice, SimData* simdata);
int serialdev_init(SerialDevice* serialdevice);
int serialdev_free(SerialDevice* serialdevice);
#endif #endif

View File

@ -1,96 +1,85 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include "simdevice.h" #include "simdevice.h"
#include "../helper/parameters.h" #include "../helper/parameters.h"
#include "../helper/confighelper.h" #include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simdata.h"
#include "../slog/slog.h" #include "../slog/slog.h"
int devupdate(SimDevice* simdevice, SimData* simdata)
int update(SimDevice* this, SimData* simdata)
{ {
((vtable*)this->vtable)->update(this, simdata); if (simdevice->initialized==false)
} {
return 0;
int simdevfree(SimDevice* this) }
{ switch ( simdevice->type )
((vtable*)this->vtable)->free(this); {
} case SIMDEV_USB :
usbdev_update(&simdevice->d.usbdevice, simdata);
int devupdate(SimDevice* this, SimData* simdata) break;
{ case SIMDEV_SOUND :
sounddev_update(&simdevice->d.sounddevice, simdata);
break;
case SIMDEV_SERIAL :
serialdev_update(&simdevice->d.serialdevice, simdata);
break;
}
return 0; return 0;
} }
int devinit(SimDevice* simdevices, SimInfo* siminfo, int numdevices, DeviceSettings* ds, MonocoqueSettings* ms) int devfree(SimDevice* simdevice)
{ {
slogi("initializing simdevices for simapi %i...", siminfo->simulatorapi);
int devices = 0;
for (int j = 0; j < numdevices; j++) if (simdevice->initialized==false)
{ {
simdevices[j].initialized = false; slogw("Attempt to free an uninitialized device");
return MONOCOQUE_ERROR_INVALID_DEV;
if (ds[j].dev_type == SIMDEV_USB) { }
USBDevice* sim = new_usb_device(&ds[j], ms, siminfo); switch ( simdevice->type )
if (sim != NULL)
{ {
simdevices[j] = sim->m; case SIMDEV_USB :
simdevices[j].initialized = true; usbdev_free(&simdevice->d.usbdevice);
simdevices[j].type = SIMDEV_USB; break;
simdevices[j].fps = ds[j].fps; case SIMDEV_SOUND :
devices++; sounddev_free(&simdevice->d.sounddevice);
break;
case SIMDEV_SERIAL :
serialdev_free(&simdevice->d.serialdevice);
break;
} }
else return 0;
{ }
slogw("Could not initialize USB Device");
} int devinit(SimDevice* simdevice, DeviceSettings* ds)
} {
slogi("initializing simdevice...");
if (ds[j].dev_type == SIMDEV_SOUND) { simdevice->initialized = false;
if(ms->disable_audio == true) int err = 0;
{
slogi("skipping configured sound device due to disable_audio being specified..."); switch ( ds->dev_type )
} {
else case SIMDEV_USB :
{ simdevice->type = SIMDEV_USB;
SoundDevice* sim = new_sound_device(&ds[j], ms, siminfo); simdevice->d.usbdevice.type = USBDEV_UNKNOWN;
if (sim != NULL) err = usbdev_init(&simdevice->d.usbdevice, ds);
{ break;
case SIMDEV_SOUND :
simdevices[j] = sim->m; simdevice->type = SIMDEV_SOUND;
simdevices[j].initialized = true; err = sounddev_init(&simdevice->d.sounddevice);
simdevices[j].type = SIMDEV_SOUND; break;
simdevices[j].fps = ds[j].fps; case SIMDEV_SERIAL :
devices++; simdevice->type = SIMDEV_SERIAL;
} err = serialdev_init(&simdevice->d.serialdevice);
else break;
{ default :
slogw("Could not initialize Sound Device"); sloge("Unknown device type");
} err = MONOCOQUE_ERROR_UNKNOWN_DEV;
} break;
} }
if (ds[j].dev_type == SIMDEV_SERIAL) { if (err==0)
{
SerialDevice* sim = new_serial_device(&ds[j], ms, siminfo); simdevice->initialized = true;
if (sim != NULL) }
{ return err;
simdevices[j] = sim->m;
simdevices[j].initialized = true;
simdevices[j].type = SIMDEV_SERIAL;
simdevices[j].fps = ds[j].fps;
devices++;
}
else
{
slogw("Could not initialize Serial Device");
}
}
}
return devices;
} }

View File

@ -2,166 +2,33 @@
#define _SIMDEVICE_H #define _SIMDEVICE_H
#include <stdbool.h> #include <stdbool.h>
#include <hidapi/hidapi.h>
#include <lua.h>
#include "usbdevice.h" #include "usbdevice.h"
#include "sounddevice.h" #include "sounddevice.h"
#include "serialdevice.h" #include "serialdevice.h"
#include "hapticeffect.h"
#include "../helper/confighelper.h" #include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simdata.h"
#include "../../arduino/simwind/simwind.h"
#include "../../arduino/simhaptic/simhaptic.h"
#include "../../arduino/shiftlights/shiftlights.h"
typedef struct SimDevice SimDevice; typedef struct
struct SimDevice
{ {
const void* vtable;
int (*update)(SimDevice*, SimData*);
int (*free)(SimDevice*);
void* derived;
int id; int id;
int fps;
bool initialized; bool initialized;
lua_State* L;
char* device_specific_config_file;
DeviceType type; DeviceType type;
HapticEffect hapticeffect;
};
typedef struct {
int (*update)(SimDevice*, SimData*);
int (*free)(SimDevice*);
} vtable;
/********* Serial Devices *****/
typedef enum
{
SERIALDEV_UNKNOWN = 0,
SERIALDEV_ARDUINO = 1,
SERIALDEV_WHEEL = 2,
}
SerialType;
typedef struct
{
SimDevice m;
int id;
SerialType type;
struct sp_port* port;
SerialDeviceType devicetype;
// move these two they only apply to the haptic device
int motorsposition;
int numlights;
int numleds;
int startled;
int endled;
int baudrate;
double ampfactor;
double fanpower;
double state;
union union
{ {
SimWindData simwinddata; USBDevice usbdevice;
SimHapticData simhapticdata; SoundDevice sounddevice;
ShiftLightsData shiftlightsdata; SerialDevice serialdevice;
WheelDevice wheeldevice; } d;
} u;
} }
SerialDevice; SimDevice;
int arduino_shiftlights_update(SimDevice* this, SimData* simdata);
int arduino_simwind_update(SimDevice* this, SimData* simdata);
int arduino_simhaptic_update(SimDevice* this, SimData* simdata);
int serialdev_free(SimDevice* this);
SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo);
/********* USB HID Devices *****/
typedef enum
{
USBDEV_UNKNOWN = 0,
USBDEV_TACHOMETER = 1,
USBDEV_GENERICHAPTIC = 2,
USBDEV_WHEEL = 3
}
USBType;
typedef struct
{
SimDevice m;
int id;
hid_device* handle;
USBType type;
union
{
TachDevice tachdevice;
WheelDevice wheeldevice;
USBGenericHapticDevice hapticdevice;
} u;
}
USBDevice;
int usbdev_update(SimDevice* this, SimData* simdata);
int usbdev_free(SimDevice* this);
USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo);
/********* Sound Devices *****/
typedef struct
{
SimDevice m;
int id;
int configcheck;
SoundEffectModulationType modulationType;
SoundData sounddata;
//#ifdef USE_PULSEAUDIO
pa_stream *stream;
//#else
//PaStreamParameters outputParameters;
//PaStream* stream;
//#endif
}
SoundDevice;
int sounddev_engine_update(SimDevice* this, SimData* simdata);
int sounddev_gearshift_update(SimDevice* this, SimData* simdata);
int sounddev_tyreslip_update(SimDevice* this, SimData* simdata);
int sounddev_free(SimDevice* this);
SoundDevice* new_sound_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo);
/***** Generic Methods *********/
int update(SimDevice* simdevice, SimData* simdata);
int devupdate(SimDevice* simdevice, SimData* simdata); int devupdate(SimDevice* simdevice, SimData* simdata);
int devinit(SimDevice* simdevices, SimInfo* siminfo, int numdevices, DeviceSettings* ds, MonocoqueSettings* ms); int devinit(SimDevice* simdevice, DeviceSettings* ds);
int devfree(SimDevice* simdevices, int numdevices);
int simdevfree(SimDevice* this);
/****** USB SubTypes ****************/
int wheeldev_update(USBDevice* usbdevice, SimData* simdata);
int wheeldev_init(USBDevice* usbdevice, DeviceSettings* ds);
int wheeldev_free(USBDevice* usbdevice);
int tachdev_update(USBDevice* tachdevice, SimData* simdata);
int tachdev_init(USBDevice* tachdevice, DeviceSettings* ds);
int tachdev_free(USBDevice* tachdevice);
int devfree(SimDevice* simdevice);
#endif #endif

View File

@ -1,77 +0,0 @@
#include "sound.h"
#include "../slog/slog.h"
pa_threaded_mainloop* mainloop;
pa_context* context;
void context_state_cb(pa_context* context, void* mainloop) {
pa_threaded_mainloop_signal(mainloop, 0);
}
int setupsound()
{
pa_mainloop_api *mainloop_api;
slogi("connecting pulseaudio...");
// Get a mainloop and its context
mainloop = pa_threaded_mainloop_new();
mainloop_api = pa_threaded_mainloop_get_api(mainloop);
context = pa_context_new(mainloop_api, "Monocoque");
pa_context_set_state_callback(context, &context_state_cb, mainloop);
// Lock the mainloop so that it does not run and crash before the context is ready
pa_threaded_mainloop_lock(mainloop);
// Start the mainloop
pa_threaded_mainloop_start(mainloop);
pa_context_connect(context, NULL, 0, NULL);
// Wait for the context to be ready
for(;;) {
pa_context_state_t context_state = pa_context_get_state(context);
if (context_state == PA_CONTEXT_READY) break;
pa_threaded_mainloop_wait(mainloop);
}
pa_threaded_mainloop_unlock(mainloop);
slogi("successfully connected pulseaudio...");
return 0;
}
int freesound()
{
if (mainloop) {
pa_threaded_mainloop_lock(mainloop);
}
if (context)
{
pa_context_unref(context);
slogt("freed pulseaudio context");
}
if (mainloop) {
pa_signal_done();
pa_threaded_mainloop_unlock(mainloop);
pa_threaded_mainloop_free(mainloop);
}
return 0;
}
//int setupsound()
//{
// slogi("connecting portaudio...");
//}
//
//
//int freesound()
//{
//
//}

View File

@ -1,13 +0,0 @@
#ifndef _SOUND_H
#define _SOUND_H
#include <pulse/pulseaudio.h>
extern pa_threaded_mainloop* mainloop;
extern pa_context* context;
int setupsound();
int freesound();
#endif

View File

@ -1,5 +1,3 @@
#ifndef USE_PULSEAUDIO
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
@ -12,82 +10,65 @@
#define SAMPLE_RATE (48000) #define SAMPLE_RATE (48000)
#define DURATION 4.0
#define AMPLITUDE .5
#ifndef M_PI #ifndef M_PI
#define M_PI (3.14159265) #define M_PI (3.14159265)
#endif #endif
int patestCallbackEngineRPM(const void* inputBuffer,
int patestCallback(const void* inputBuffer,
void* outputBuffer, void* outputBuffer,
unsigned long framesPerBuffer, unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo, const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags, PaStreamCallbackFlags statusFlags,
void* userData) void* userData)
{ {
SoundData* data = (SoundData*)userData; PATestData* data = (PATestData*)userData;
float* out = (float*)outputBuffer; float* out = (float*)outputBuffer;
memset(out, 0, framesPerBuffer * 2 * sizeof(float)); memset(out, 0, framesPerBuffer * 2 * sizeof(float));
unsigned int i; unsigned int i;
unsigned int n; unsigned int n;
//n = data->n; n = data->n;
(void) inputBuffer; /* Prevent unused argument warning. */ (void) inputBuffer; /* Prevent unused argument warning. */
for( i=0; i<framesPerBuffer; i++,n++ ) for( i=0; i<framesPerBuffer; i++,n++ )
{ {
static double t = 0.0; float v = 0;
double sample = AMPLITUDE * 32767.0 * sin(2.0 * M_PI * data->curr_frequency * t); v = data->amp * sin (2 * M_PI * ((float) n) / (float) SAMPLE_RATE);
t += 1.0 / SAMPLE_RATE; if ( data->gear_sound_data > 0 )
if (t >= DURATION) { {
t = 0.0; if (n>=1764)
{
n=0;
}
}
else
{
if (n>=data->table_size)
{
n=0;
} }
*out++ = sample;
*out++ = sample;
} }
if ( data->gear_sound_data > 0 )
{
// right channel only?
// i have my butt hooked up to right channel... make this configurable?
*out++ = v;
}
else
{
*out++ = v;
*out++ = v;
}
}
data->n=n;
return 0; return 0;
} }
int patestCallbackGearShift(const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData)
{
SoundData* data = (SoundData*)userData;
float* out = (float*)outputBuffer;
memset(out, 0, framesPerBuffer * 2 * sizeof(float));
unsigned int i;
unsigned int n;
//n = data->n;
(void) inputBuffer; /* Prevent unused argument warning. */
for( i=0; i<framesPerBuffer; i++,n++ )
{
static double t = 0.0;
double sample = 0;
if (data->frequency>0)
{
sample = AMPLITUDE * 32767.0 * sin(2.0 * M_PI * data->curr_frequency * data->curr_duration);
}
data->curr_duration += 1.0 / SAMPLE_RATE;
if (data->curr_duration >= data->duration) {
data->curr_duration = 0.0;
data->curr_frequency = 0.0;
}
*out++ = sample;
*out++ = sample;
}
}
int usb_generic_shaker_free(SoundDevice* sounddevice) int usb_generic_shaker_free(SoundDevice* sounddevice)
{ {
int err = 0; int err = 0;
@ -116,28 +97,14 @@ int usb_generic_shaker_init(SoundDevice* sounddevice)
sounddevice->outputParameters.suggestedLatency = Pa_GetDeviceInfo( sounddevice->outputParameters.device )->defaultLowOutputLatency; sounddevice->outputParameters.suggestedLatency = Pa_GetDeviceInfo( sounddevice->outputParameters.device )->defaultLowOutputLatency;
sounddevice->outputParameters.hostApiSpecificStreamInfo = NULL; sounddevice->outputParameters.hostApiSpecificStreamInfo = NULL;
if (sounddevice->m.hapticeffect.effecttype == EFFECT_GEARSHIFT)
{
err = Pa_OpenStream( &sounddevice->stream, err = Pa_OpenStream( &sounddevice->stream,
NULL, /* No input. */ NULL, /* No input. */
&sounddevice->outputParameters, /* As above. */ &sounddevice->outputParameters, /* As above. */
SAMPLE_RATE, SAMPLE_RATE,
440, /* Frames per buffer. */ 440, /* Frames per buffer. */
paClipOff, /* No out of range samples expected. */ paClipOff, /* No out of range samples expected. */
patestCallbackGearShift, patestCallback,
&sounddevice->sounddata ); &sounddevice->sounddata );
}
else
{
err = Pa_OpenStream( &sounddevice->stream,
NULL, /* No input. */
&sounddevice->outputParameters, /* As above. */
SAMPLE_RATE,
440, /* Frames per buffer. */
paClipOff, /* No out of range samples expected. */
patestCallbackEngineRPM,
&sounddevice->sounddata );
}
if( err != paNoError ) if( err != paNoError )
{ {
goto error; goto error;
@ -154,7 +121,8 @@ int usb_generic_shaker_init(SoundDevice* sounddevice)
error: error:
Pa_Terminate(); Pa_Terminate();
//fprintf( stderr, "An error occured while using the portaudio stream\n" );
//fprintf( stderr, "Error number: %d\n", err );
//fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err; return err;
} }
#endif

View File

@ -1,14 +1,9 @@
#ifndef _USB_GENERIC_SHAKER_H #ifndef _USB_GENERIC_SHAKER_H
#define _USB_GENERIC_SHAKER_H #define _USB_GENERIC_SHAKER_H
#include "../simdevice.h" #include "../sounddevice.h"
//#ifdef USE_PULSEAUDIO int usb_generic_shaker_init(SoundDevice* sounddevice);
int usb_generic_shaker_init(SoundDevice* sounddevice, pa_threaded_mainloop* mainloop, pa_context* context, const char* devname, int volume, int pan, int channels, const char* streamname); int usb_generic_shaker_free(SoundDevice* sounddevice);
int usb_generic_shaker_free(SoundDevice* sounddevice, pa_threaded_mainloop* mainloop);
//#else
//int usb_generic_shaker_init(SoundDevice* sounddevice);
//int usb_generic_shaker_free(SoundDevice* sounddevice);
//#endif
#endif #endif

View File

@ -1,231 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include "usb_generic_shaker.h"
#include "../sounddevice.h"
#define FORMAT PA_SAMPLE_S16LE
#define SAMPLE_RATE (44100)
#define AMPLITUDE 1
#define DURATION 1.0
#ifndef M_PI
#define M_PI (3.14159265)
#endif
static double apply_noise(double base_freq, double noise_amount) {
if (noise_amount > 0.0) {
double r = (double) rand() / RAND_MAX * 2.0 - 1.0;
return base_freq + r * noise_amount;
} else {
return base_freq;
}
}
void gear_sound_stream(pa_stream *s, size_t length, void *userdata) {
SoundData* data = (SoundData*)userdata;
size_t num_samples = length / sizeof(int16_t);
int16_t buffer[num_samples];
for (size_t i = 0; i < num_samples; i++) {
double sample = 0;
if (data->curr_frequency>0.0)
{
double t = data->phase;
double a = (double)data->curr_amplitude/100;
// fade-out 5ms to prevent discontinuity
static double fade_out_duration = 0.005;
double remaining = data->duration - data->curr_duration;
if (remaining < fade_out_duration) {
a *= remaining / fade_out_duration;
}
sample = a * 32767.0 * sin(2.0 * M_PI * t);
double f = apply_noise(data->curr_frequency, data->noise);
t += f / SAMPLE_RATE;
if (t >= 1.0) {
t -= floor(t);
}
data->phase = t;
data->curr_duration += 1.0 / SAMPLE_RATE;
if (data->curr_duration >= data->duration) {
data->curr_duration = 0.0;
data->curr_frequency = 0.0;
data->phase = 0.0;
}
}
buffer[i] = (int16_t)sample;
}
pa_stream_write(s, buffer, length, NULL, 0LL, PA_SEEK_RELATIVE);
}
void engine_sound_stream(pa_stream *s, size_t length, void *userdata) {
SoundData* data = (SoundData*)userdata;
size_t num_samples = length / sizeof(int16_t);
//size_t num_samples = (size_t) (DURATION * SAMPLE_RATE);
int16_t buffer[num_samples];
//double t = 0;
for (size_t i = 0; i < num_samples; i++) {
double t = data->phase;
double sample = ((double)data->curr_amplitude/100) * 32767.0 * sin( 2.0 * M_PI * t );
buffer[i] = (int16_t)sample;
double f = apply_noise(data->curr_frequency, data->noise);
t += f / SAMPLE_RATE;
if (t >= 1.0) {
t -= floor(t);
}
data->phase = t;
}
pa_stream_write(s, buffer, length, NULL, 0LL, PA_SEEK_RELATIVE);
}
void stream_success_cb(pa_stream *stream, int success, void *userdata) {
return;
}
void stream_state_cb(pa_stream *s, void *mainloop) {
pa_threaded_mainloop_signal(mainloop, 0);
}
int usb_generic_shaker_free(SoundDevice* sounddevice, pa_threaded_mainloop* mainloop)
{
if(!mainloop)
{
// if this happens we are in trouble
return 1;
}
pa_threaded_mainloop_lock(mainloop);
int err = 0;
if (sounddevice->stream)
{
pa_stream_disconnect(sounddevice->stream);
pa_stream_unref(sounddevice->stream);
// why is this wrong
}
pa_threaded_mainloop_unlock(mainloop);
return err;
}
int usb_generic_shaker_init(SoundDevice* sounddevice, pa_threaded_mainloop* mainloop, pa_context* context, const char* devname, int volume, int pan, int channels, const char* streamname)
{
pa_threaded_mainloop_lock(mainloop);
pa_stream *stream;
// Create a playback stream
pa_sample_spec sample_specifications;
sample_specifications.format = FORMAT;
sample_specifications.rate = SAMPLE_RATE;
sample_specifications.channels = channels;
pa_channel_map channel_map;
pa_channel_map_init_auto(&channel_map, channels, PA_CHANNEL_MAP_DEFAULT);
//pa_channel_map_init_stereo(&channel_map);
// not sure about what to do here
if(channels == 2)
{
pa_channel_map_parse(&channel_map, "front-left,front-right");
}
if(channels == 4)
{
pa_channel_map_parse(&channel_map, "front-left,front-right,rear-left,rear-right");
}
if(channels == 6)
{
pa_channel_map_parse(&channel_map, "front-left,front-right,front-center,lfe,rear-left,rear-right");
}
if(channels == 8)
{
pa_channel_map_parse(&channel_map, "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right");
}
stream = pa_stream_new(context, streamname, &sample_specifications, &channel_map);
pa_stream_set_state_callback(stream, stream_state_cb, mainloop);
if (sounddevice->m.hapticeffect.effecttype == EFFECT_GEARSHIFT)
{
pa_stream_set_write_callback(stream, gear_sound_stream, &sounddevice->sounddata);
}
else
{
pa_stream_set_write_callback(stream, engine_sound_stream, &sounddevice->sounddata);
}
// recommended settings, i.e. server uses sensible values
pa_buffer_attr buffer_attr;
buffer_attr.maxlength = (uint32_t) 32767;
buffer_attr.tlength = (uint32_t) 2048;
buffer_attr.prebuf = (uint32_t) -1;
buffer_attr.minreq = (uint32_t) -1;
pa_cvolume cv;
pa_cvolume_mute(&cv, channels);
pa_volume_t channel_volume = PA_CLAMP_VOLUME((pa_volume_t)((volume/100.d)*PA_VOLUME_NORM));
pa_stream_flags_t stream_flags;
stream_flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_NOT_MONOTONIC | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY | PA_STREAM_START_CORKED;
// for now i'm only supporting playing on one specified channel which is the concept you should build your setups around
cv.values[pan] = channel_volume;
pa_stream_connect_playback(stream, devname, &buffer_attr, stream_flags, &cv, NULL);
//pa_stream_connect_playback(stream, devname, &buffer_attr, stream_flags, &cv, NULL);
// Wait for the stream to be ready
for(;;)
{
pa_stream_state_t stream_state = pa_stream_get_state(stream);
PA_STREAM_IS_GOOD(stream_state);
//PA_STREAM_IS_GOOD(stream_state);
if (stream_state == PA_STREAM_READY) break;
pa_threaded_mainloop_wait(mainloop);
}
// Uncork the stream so it will start playing
pa_stream_cork(stream, 0, stream_success_cb, mainloop);
sounddevice->stream = stream;
pa_threaded_mainloop_unlock(mainloop);
return 0;
}

View File

@ -1,347 +1,38 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include "sound.h"
#include "simdevice.h"
#include "sounddevice.h" #include "sounddevice.h"
#include "hapticeffect.h"
#include "sound/usb_generic_shaker.h" #include "sound/usb_generic_shaker.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simdata.h"
#include "../helper/parameters.h" #include "../helper/parameters.h"
#include "../slog/slog.h" #include "../slog/slog.h"
int gear_sound_set(SoundDevice* sounddevice, SimData* simdata) int sounddev_update(SoundDevice* sounddevice, SimData* simdata)
{ {
if (sounddevice->sounddata.last_gear != simdata->gear && simdata->gear > 1) sounddevice->sounddata.table_size = 44100/(simdata->rpms/60);
sounddevice->sounddata.gear_sound_data = 0;
if (sounddevice->sounddata.last_gear != simdata->gear)
{ {
//sounddevice->sounddata.gear_sound_data = 3.14; sounddevice->sounddata.gear_sound_data = sounddevice->sounddata.amp;
sounddevice->sounddata.curr_frequency = sounddevice->sounddata.frequency;
sounddevice->sounddata.curr_amplitude = sounddevice->sounddata.amplitude;
sounddevice->sounddata.curr_duration = 0;
} }
sounddevice->sounddata.last_gear = simdata->gear; sounddevice->sounddata.last_gear = simdata->gear;
slogt("set gear frequency to %f", sounddevice->sounddata.curr_frequency);
} }
int sounddev_free(SoundDevice* sounddevice)
double modulate(SoundDevice* sounddevice, double raw_effect, SoundEffectModulationType modulation)
{ {
double modulated_effect = raw_effect; return usb_generic_shaker_free(sounddevice);
switch (modulation)
{
case SOUND_EFFECT_MODULATION_FREQUENCY:
modulated_effect = ((sounddevice->sounddata.frequencyMax - sounddevice->sounddata.frequency) * raw_effect) + sounddevice->sounddata.frequency;
sounddevice->sounddata.curr_frequency = modulated_effect;
sounddevice->sounddata.curr_amplitude = sounddevice->sounddata.amplitude;
slogt("set curr frequency to %f from raw effect %f and base frequency %i", sounddevice->sounddata.curr_frequency, raw_effect, sounddevice->sounddata.frequency);
break;
case SOUND_EFFECT_MODULATION_AMPLIFY:
modulated_effect = ((sounddevice->sounddata.amplitudeMax - sounddevice->sounddata.amplitude) * raw_effect) + sounddevice->sounddata.amplitude;
sounddevice->sounddata.curr_amplitude = modulated_effect;
sounddevice->sounddata.curr_frequency = sounddevice->sounddata.frequency;
slogt("set curr amplitude to %i from raw effect %f and base amplitude %i", sounddevice->sounddata.curr_amplitude, raw_effect, sounddevice->sounddata.amplitude);
break;
case SOUND_EFFECT_MODULATION_NONE:
default:
sounddevice->sounddata.curr_frequency = sounddevice->sounddata.frequency;
sounddevice->sounddata.curr_amplitude = sounddevice->sounddata.amplitude;
break;
}
return modulated_effect;
} }
// we could make a vtable for these different effects too int sounddev_init(SoundDevice* sounddevice)
int sounddev_engine_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
double effect = ((double)simdata->rpms/60)/((double)simdata->maxrpm/60);
slogt("Set base effect of %f from rpms of %i", effect, simdata->rpms);
modulate(sounddevice, effect, sounddevice->modulationType);
}
int sounddev_tyreslip_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
double effect = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
slogt("Updating sound device frequency from original tyre slip effect %f", effect);
if (effect > 0)
{
modulate(sounddevice, effect, sounddevice->modulationType);
}
else
{
sounddevice->sounddata.curr_frequency = 0.0;
sounddevice->sounddata.curr_amplitude = 0;
sounddevice->sounddata.curr_duration = 0;
}
}
int sounddev_tyrelock_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
double play = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
slogt("Updating sound device frequency from original tyre lock effect %f", play);
if (play > 0)
{
modulate(sounddevice, play, sounddevice->modulationType);
}
else
{
sounddevice->sounddata.curr_frequency = 0.0;
sounddevice->sounddata.curr_amplitude = 0;
sounddevice->sounddata.curr_duration = 0;
}
}
int sounddev_absbrakes_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
double play = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
slogt("Updating sound device frequency from original abs effect %f", play);
if (play > 0)
{
modulate(sounddevice, play, sounddevice->modulationType);
}
else
{
sounddevice->sounddata.curr_frequency = 0.0;
sounddevice->sounddata.curr_amplitude = 0;
sounddevice->sounddata.curr_duration = 0;
}
}
int sounddev_suspension_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
double effect = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
effect = effect * 10;
slogt("Updating sound device output from original suspension travel effect %f", effect);
if (effect > 0)
{
effect = modulate(sounddevice, effect, sounddevice->modulationType);
}
else
{
sounddevice->sounddata.curr_frequency = 0.0;
sounddevice->sounddata.curr_amplitude = 0;
sounddevice->sounddata.curr_duration = 0;
}
}
int sounddev_gearshift_update(SimDevice* this, SimData* simdata)
{
SoundDevice* sounddevice = (void *) this->derived;
gear_sound_set(sounddevice, simdata);
}
int sounddev_free(SimDevice* this)
{
SoundDevice* sounddevice = (void *) this->derived;
usb_generic_shaker_free(sounddevice, mainloop);
free(sounddevice);
return 0;
}
int sounddev_init(SoundDevice* sounddevice, const char* devname, MonocoqueTyreIdentifier tyre, SoundDeviceSettings sds)
{ {
slogi("initializing standalone sound device..."); slogi("initializing standalone sound device...");
sounddevice->sounddata.pitch = 1;
slogi("volume is: %i", sds.volume); sounddevice->sounddata.pitch = 261.626;
slogi("Modulation type: %i", sds.modulation); sounddevice->sounddata.amp = 32;
slogi("frequency is: %i", sds.frequency); sounddevice->sounddata.left_phase = sounddevice->sounddata.right_phase = 0;
slogi("frequency Max is: %i", sds.frequencyMax); sounddevice->sounddata.table_size = 44100/(100/60);
slogi("amplitude is: %i", sds.amplitude);
slogi("amplitude Max is: %i", sds.amplitudeMax);
slogi("pan is: %i", sds.pan);
slogi("channels is: %i", sds.channels);
slogi("duration is: %f", sds.duration);
slogi("noise is: %i", sds.noise);
sounddevice->modulationType = sds.modulation;
sounddevice->sounddata.frequency = sds.frequency;
sounddevice->sounddata.frequencyMax = sds.frequencyMax;
sounddevice->sounddata.amplitude = sds.amplitude;
sounddevice->sounddata.amplitudeMax = sds.amplitudeMax;
sounddevice->sounddata.noise = (double)sds.noise;
sounddevice->sounddata.curr_duration = 0;
sounddevice->sounddata.phase = 0;
sounddevice->sounddata.curr_amplitude = 0;
sounddevice->sounddata.curr_frequency = 0.0;
const char* streamname= "Engine";
switch (sounddevice->m.hapticeffect.effecttype) {
case (EFFECT_GEARSHIFT):
sounddevice->sounddata.last_gear = 0; sounddevice->sounddata.last_gear = 0;
sounddevice->sounddata.duration = sds.duration;
streamname = "Gear";
break;
case (EFFECT_TYRESLIP):
streamname = "TyreSlip";
break;
case (EFFECT_TYRELOCK):
streamname = "TyreLock";
break;
case (EFFECT_ABSBRAKES):
streamname = "ABS";
break;
case (EFFECT_SUSPENSION):
streamname = "Suspension";
break;
case (EFFECT_ENGINERPM):
default:
streamname = "Engine";
break;
}
usb_generic_shaker_init(sounddevice);
usb_generic_shaker_init(sounddevice, mainloop, context, devname, sds.volume, sds.pan, sds.channels, streamname);
//usb_generic_shaker_init(sounddevice);
}
static const vtable engine_sound_simdevice_vtable = { &sounddev_engine_update, &sounddev_free };
static const vtable gear_sound_simdevice_vtable = { &sounddev_gearshift_update, &sounddev_free };
static const vtable tyreslip_sound_simdevice_vtable = { &sounddev_tyreslip_update, &sounddev_free };
static const vtable tyrelock_sound_simdevice_vtable = { &sounddev_tyrelock_update, &sounddev_free };
static const vtable absbrakes_sound_simdevice_vtable = { &sounddev_absbrakes_update, &sounddev_free };
static const vtable suspension_sound_simdevice_vtable = { &sounddev_suspension_update, &sounddev_free };
SoundDevice* new_sound_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) {
SoundDevice* this = (SoundDevice*) malloc(sizeof(SoundDevice));
this->m.update = &update;
this->m.free = &simdevfree;
this->m.derived = this;
int error = 0;
switch (ds->effect_type)
{
case (EFFECT_TYRESLIP):
case (EFFECT_TYRELOCK):
case (EFFECT_ABSBRAKES):
case (EFFECT_SUSPENSION):
if(siminfo->SimSupportsHapticEffects == false)
{
slogw("Skipping sound effect setup because sim does not support haptic effects");
error = MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE;
}
defaut:
error = 0;
}
if(error == 0)
{
slogt("Attempting to configure sound device with subtype: %i", ds->effect_type);
switch (ds->effect_type)
{
case (EFFECT_ENGINERPM):
this->m.hapticeffect.effecttype = EFFECT_ENGINERPM;
this->m.vtable = &engine_sound_simdevice_vtable;
slogi("Initializing sound device for engine vibrations.");
break;
case (EFFECT_GEARSHIFT):
this->m.hapticeffect.effecttype = EFFECT_GEARSHIFT;
this->m.vtable = &gear_sound_simdevice_vtable;
slogi("Initializing sound device for gear shift vibrations.");
break;
case (EFFECT_TYRESLIP):
this->m.hapticeffect.effecttype = EFFECT_TYRESLIP;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &tyreslip_sound_simdevice_vtable;
slogi("Initializing sound device for tyre slip vibrations.");
break;
case (EFFECT_TYRELOCK):
this->m.hapticeffect.effecttype = EFFECT_TYRELOCK;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &tyrelock_sound_simdevice_vtable;
slogi("Initializing sound device for tyre slip vibrations.");
break;
case (EFFECT_ABSBRAKES):
this->m.hapticeffect.effecttype = EFFECT_ABSBRAKES;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &absbrakes_sound_simdevice_vtable;
slogi("Initializing sound device for abs vibrations.");
break;
case (EFFECT_SUSPENSION):
this->m.hapticeffect.effecttype = EFFECT_SUSPENSION;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i on tyre %i", this->m.hapticeffect.effecttype, ds->effect_type, ds->tyre);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &suspension_sound_simdevice_vtable;
slogi("Initializing sound device for suspension vibrations.");
break;
}
}
if(error == 0)
{
slogt("Attempting to use sound device %s", ds->sounddevsettings.dev);
error = sounddev_init(this, ds->sounddevsettings.dev, ds->tyre, ds->sounddevsettings);
}
if (error != 0)
{
free(this);
return NULL;
}
return this;
} }

View File

@ -1,37 +1,45 @@
#ifndef _SOUNDDEVICE_H #ifndef _SOUNDDEVICE_H
#define _SOUNDDEVICE_H #define _SOUNDDEVICE_H
//#ifdef USE_PULSEAUDIO #include "portaudio.h"
#include <pulse/pulseaudio.h>
//#else
//#include "portaudio.h"
//#endif
#include "../simulatorapi/simdata.h"
#include "../helper/parameters.h"
typedef enum typedef enum
{ {
SOUND_EFFECT_MODULATION_NONE = 0, SOUNDDEV_UNKNOWN = 0,
SOUND_EFFECT_MODULATION_FREQUENCY = 1, SOUNDDEV_SHAKER = 1
SOUND_EFFECT_MODULATION_AMPLIFY = 2,
} }
SoundEffectModulationType; SoundType;
#define MAX_TABLE_SIZE (6000) #define MAX_TABLE_SIZE (6000)
typedef struct typedef struct
{ {
uint8_t last_gear; float sine[MAX_TABLE_SIZE];
int volume; float pitch;
uint32_t frequency; int last_gear;
uint32_t frequencyMax; int left_phase;
uint32_t amplitude; int right_phase;
uint32_t amplitudeMax; int n;
double duration; int table_size;
double curr_frequency; int amp;
uint32_t curr_amplitude; int gear_sound_data;
double curr_duration;
double phase;
double noise;
} }
SoundData; PATestData;
typedef struct
{
int id;
SoundType type;
PATestData sounddata;
PaStreamParameters outputParameters;
PaStream* stream;
}
SoundDevice;
int sounddev_update(SoundDevice* sounddevice, SimData* simdata);
int sounddev_init(SoundDevice* sounddevice);
int sounddev_free(SoundDevice* sounddevice);
#endif #endif

View File

@ -2,16 +2,14 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
//#include "tachdevice.h" #include "tachdevice.h"
#include "simdevice.h" #include "revburner.h"
#include "usb/revburner.h" #include "../../helper/confighelper.h"
#include "../helper/confighelper.h" #include "../../simulatorapi/simdata.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../../slog/slog.h"
#include "../slog/slog.h"
int tachdev_update(USBDevice* usbdevice, SimData* simdata) int tachdev_update(TachDevice* tachdevice, SimData* simdata)
{ {
TachDevice* tachdevice = &usbdevice->u.tachdevice;
// current plan is to just use the revburner xml format for other possible tachometer devices // current plan is to just use the revburner xml format for other possible tachometer devices
// with that assumption this same logic is assumed the same for other tachometer devices // with that assumption this same logic is assumed the same for other tachometer devices
// the only difference then being in communication to the physical device // the only difference then being in communication to the physical device
@ -45,33 +43,30 @@ int tachdev_update(USBDevice* usbdevice, SimData* simdata)
} }
} }
slogt("Settings tachometer pulses to %i", pulses); slogt("Settings tachometer pulses to %i", pulses);
revburner_update(usbdevice, pulses); revburner_update(tachdevice, pulses);
break; break;
} }
return 0; return 0;
} }
int tachdev_free(USBDevice* usbdevice) int tachdev_free(TachDevice* tachdevice)
{ {
TachDevice* tachdevice = &usbdevice->u.tachdevice;
switch ( tachdevice->type ) switch ( tachdevice->type )
{ {
case TACHDEV_UNKNOWN : case TACHDEV_UNKNOWN :
case TACHDEV_REVBURNER : case TACHDEV_REVBURNER :
revburner_update(usbdevice, 0); revburner_update(tachdevice, 0);
revburner_free(usbdevice); revburner_free(tachdevice);
break; break;
} }
return 0; return 0;
} }
int tachdev_init(USBDevice* usbdevice, DeviceSettings* ds) int tachdev_init(TachDevice* tachdevice, DeviceSettings* ds)
{ {
slogi("initializing tachometer device..."); slogi("initializing tachometer device...");
TachDevice* tachdevice = &usbdevice->u.tachdevice;
int error = 0; int error = 0;
// detection of tach device model // detection of tach device model
tachdevice->type = TACHDEV_UNKNOWN; tachdevice->type = TACHDEV_UNKNOWN;
@ -79,7 +74,7 @@ int tachdev_init(USBDevice* usbdevice, DeviceSettings* ds)
tachdevice->tachsettings = ds->tachsettings; tachdevice->tachsettings = ds->tachsettings;
error = revburner_init(usbdevice); error = revburner_init(tachdevice);
return error; return error;
} }

View File

@ -1,9 +1,9 @@
#ifndef _TACHDEVICE_H #ifndef _TACHDEVICE_H
#define _TACHDEVICE_H #define _TACHDEVICE_H
#include <hidapi/hidapi.h>
#include "../helper/confighelper.h" #include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simdata.h"
//typedef int (*tachdev_update)(int revs); //typedef int (*tachdev_update)(int revs);
@ -19,10 +19,13 @@ typedef struct
int id; int id;
TachType type; TachType type;
bool use_pulses; bool use_pulses;
hid_device* handle;
TachometerSettings tachsettings; TachometerSettings tachsettings;
} }
TachDevice; TachDevice;
int tachdev_update(TachDevice* tachdevice, SimData* simdata);
int tachdev_init(TachDevice* tachdevice, DeviceSettings* ds);
int tachdev_free(TachDevice* tachdevice);
#endif #endif

View File

@ -1,105 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glob.h>
#include "usbhapticdevice.h"
#include "../../helper/confighelper.h"
#include "../slog/slog.h"
const char* SYSFSRUMBLEPATH = "/sys/module/hid_fanatec/drivers/hid:f*/0003:0EB7:183B.*/rumble";
int cslelitev3_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play)
{
int res = 0;
int value = 0;
if (play > 0)
{
switch (effecttype)
{
case (EFFECT_TYRESLIP):
value = 0xff0000;
break;
case (EFFECT_TYRELOCK):
value = 0xff00;
break;
case (EFFECT_ABSBRAKES):
value = 0xffff00;
break;
}
}
fprintf(usbhapticdevice->filehandle, "%i\n", value);
fflush(usbhapticdevice->filehandle);
return res;
}
int cslelitev3_free(USBGenericHapticDevice* usbhapticdevice)
{
int res = 0;
free(usbhapticdevice->dev);
fflush(usbhapticdevice->filehandle);
fclose(usbhapticdevice->filehandle);
return res;
}
int cslelitev3_init(USBGenericHapticDevice* usbhapticdevice)
{
slogi("initializing CSL Elite V3 Pedals...");
int res = 0;
glob_t globlist;
int i = 0;
if (glob(SYSFSRUMBLEPATH, GLOB_PERIOD, NULL, &globlist) == GLOB_NOSPACE || glob(SYSFSRUMBLEPATH, GLOB_PERIOD, NULL, &globlist) == GLOB_NOMATCH)
{
res = 1;
}
if (glob(SYSFSRUMBLEPATH, GLOB_PERIOD, NULL, &globlist) == GLOB_ABORTED)
{
res = 2;
}
if (res == 0)
{
while (globlist.gl_pathv[i])
{
if (i == 0)
{
usbhapticdevice->dev = strdup(globlist.gl_pathv[i]);
}
i++;
}
}
globfree(&globlist);
if (res == 1) {
sloge("Could not find attach Club Sport Elite V3 Pedals");
return res;
}
if (res == 2) {
sloge("Permissions issue finding Club Sport Elite V3 Pedals");
return res;
}
usbhapticdevice->filehandle = fopen(usbhapticdevice->dev, "w");
if (!usbhapticdevice->filehandle)
{
sloge("Could not open pedal device...");
return res;
}
slogd("CSL Elite V3 Pedals Successfully initialized...");
return res;
}

View File

@ -1,8 +0,0 @@
#ifndef _CSLELITEV3_H
#define _CSLELITEV3_H
int cslelitev3_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play);
int cslelitev3_init(USBGenericHapticDevice* usbhapticdevice);
int cslelitev3_free(USBGenericHapticDevice* usbhapticdevice);
#endif

View File

@ -2,19 +2,19 @@
#include <hidapi/hidapi.h> #include <hidapi/hidapi.h>
#include "../simdevice.h" #include "tachdevice.h"
#include "../../slog/slog.h" #include "../slog/slog.h"
const size_t REVBURNER_BUFSIZE = 8; const int buf_size = 65;
int revburner_update(USBDevice* tachdevice, int pulses) int revburner_update(TachDevice* tachdevice, int pulses)
{ {
int res = 0; int res = 0;
unsigned char bytes[REVBURNER_BUFSIZE]; unsigned char bytes[buf_size];
for (int x = 0; x < REVBURNER_BUFSIZE; x++) for (int x = 0; x < buf_size; x++)
{ {
bytes[x] = 0x00; bytes[x] = 0x00;
} }
@ -27,7 +27,7 @@ int revburner_update(USBDevice* tachdevice, int pulses)
if (tachdevice->handle) if (tachdevice->handle)
{ {
res = hid_write(tachdevice->handle, bytes, REVBURNER_BUFSIZE); res = hid_write(tachdevice->handle, bytes, buf_size);
} }
else else
{ {
@ -37,7 +37,7 @@ int revburner_update(USBDevice* tachdevice, int pulses)
return res; return res;
} }
int revburner_free(USBDevice* tachdevice) int revburner_free(TachDevice* tachdevice)
{ {
int res = 0; int res = 0;
@ -47,7 +47,7 @@ int revburner_free(USBDevice* tachdevice)
return res; return res;
} }
int revburner_init(USBDevice* tachdevice) int revburner_init(TachDevice* tachdevice)
{ {
slogi("initializing revburner tachometer..."); slogi("initializing revburner tachometer...");
//tachdevice->update_tachometer = revburner_device_update; //tachdevice->update_tachometer = revburner_device_update;
@ -64,6 +64,6 @@ int revburner_init(USBDevice* tachdevice)
res = hid_exit(); res = hid_exit();
return 1; return 1;
} }
slogd("Found RevBurner Tachometer handle %i...", tachdevice->handle); slogd("Found RevBurner Tachometer...");
return res; return res;
} }

View File

@ -1,10 +1,8 @@
#ifndef _REVBURNER_H #ifndef _REVBURNER_H
#define _REVBURNER_H #define _REVBURNER_H
#include "../simdevice.h" int revburner_update(TachDevice* tachdevice, int pulses);
int revburner_init(TachDevice* tachdevice);
int revburner_update(USBDevice* tachdevice, int pulses); int revburner_free(TachDevice* tachdevice);
int revburner_init(USBDevice* tachdevice);
int revburner_free(USBDevice* tachdevice);
#endif #endif

View File

@ -1,136 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hidapi/hidapi.h>
#include "usbhapticdevice.h"
#include "../../helper/confighelper.h"
#include "../slog/slog.h"
#define SIMAGIC_VENDOR_ID 0x0483
#define SIMAGIC_PRODUCT_ID_P1000 0x0525
const size_t SIMAGIC_P1000_BUFSIZE = 49;
const char* SIMAGICP1000DEVSTRING = "SIMAGIC P1000 Pedals";
int senddevreport(USBGenericHapticDevice* usbhapticdevice, unsigned char* buf, size_t bufsize)
{
int res = 0;
if (usbhapticdevice->handle)
{
res = hid_send_feature_report(usbhapticdevice->handle, buf, SIMAGIC_P1000_BUFSIZE);
slogd("sent %i bytes to %s", bufsize, SIMAGICP1000DEVSTRING);
slogt("sent bytes %02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
}
else
{
slogd("no handle");
}
return res != bufsize;
}
int simagicp1000_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play)
{
int res = 0;
int value = 0;
unsigned char bytes[SIMAGIC_P1000_BUFSIZE];
for (int x = 0; x < SIMAGIC_P1000_BUFSIZE; x++)
{
bytes[x] = 0x00;
}
if (play > 0)
{
bytes[0] = 241;
bytes[1] = 0xEC;
// we can add config settings for these values
bytes[3] = 0x01;
bytes[4] = 0x0A;
bytes[5] = 0xFF;
switch (effecttype)
{
case (EFFECT_TYRESLIP):
bytes[2] = 0x02;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
break;
case (EFFECT_TYRELOCK):
bytes[2] = 0x01;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
break;
case (EFFECT_ABSBRAKES):
bytes[2] = 0x02;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
bytes[2] = 0x01;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
break;
}
}
else
{
bytes[0] = 241;
bytes[2] = 0x01;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
bytes[2] = 0x02;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
}
return res;
}
int simagicp1000_free(USBGenericHapticDevice* usbhapticdevice)
{
int res = 0;
hid_close(usbhapticdevice->handle);
res = hid_exit();
return res;
return res;
}
int simagicp1000_init(USBGenericHapticDevice* usbhapticdevice)
{
slogi("initializing %s...", SIMAGICP1000DEVSTRING);
int res = 0;
res = hid_init();
usbhapticdevice->handle = hid_open(SIMAGIC_VENDOR_ID, SIMAGIC_PRODUCT_ID_P1000, NULL);
if (!usbhapticdevice->handle)
{
sloge("Could not find attached %s", SIMAGICP1000DEVSTRING);
res = hid_exit();
return 1;
}
else
{
unsigned char bytes[SIMAGIC_P1000_BUFSIZE];
for (int x = 0; x < SIMAGIC_P1000_BUFSIZE; x++)
{
bytes[x] = 0x00;
}
bytes[0] = 241;
bytes[1] = 0xF1;
bytes[2] = 0x17;
bytes[3] = 0x00;
bytes[4] = 0x00;
bytes[5] = 0x00;
bytes[6] = 0x01;
bytes[7] = 0x02;
res = senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE);
slogd("Initialization returned %i", res);
if(res != 0)
{
slogw("Problem with initialization of %s", SIMAGICP1000DEVSTRING);
}
}
slogd("Found %s...", SIMAGICP1000DEVSTRING);
return res;
}

View File

@ -1,8 +0,0 @@
#ifndef _SIMAGICP1000_H
#define _SIMAGICP1000_H
int simagicp1000_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play);
int simagicp1000_init(USBGenericHapticDevice* usbhapticdevice);
int simagicp1000_free(USBGenericHapticDevice* usbhapticdevice);
#endif

View File

@ -1,231 +0,0 @@
#include <stdio.h>
#include <math.h>
#include <hidapi/hidapi.h>
#include "cammusc12.h"
#include "../../serial/arduinoledlua.h"
#include "../../../slog/slog.h"
const int cammusc12_hidupdate_buf_size = 16;
const int cammusc12_hidledupdate_buf_size = 7;
const int cammusc12_total_leds = 23;
int cammusc12_update(USBDevice* usbdevice, int maxrpm, int rpm, int gear, int velocity)
{
int res = 0;
unsigned char bytes[cammusc12_hidupdate_buf_size];
for (int x = 0; x < cammusc12_hidupdate_buf_size; x++)
{
bytes[x] = 0x00;
}
// byte 1 must be fc it seems
bytes[0] = 0xFA;
bytes[1] = 0xFB;
bytes[2] = 0xD4;
int perct = 0;
if(rpm > 0 && maxrpm > 0)
{
double rpmperct = (double) rpm / (double) maxrpm;
perct = trunc(nearbyint( rpmperct * 100 ));
}
bytes[3] = perct;
// bytes 2 and 3 are a 16 bit velocity
if ( velocity > 0 )
{
bytes[5] = (velocity >> 8) & 0xFF;
bytes[4] = velocity & 0xFF;
}
// byte 4 is gear
bytes[6] = gear-1;
if (usbdevice->handle)
{
slogt("writing bytes x%02xx%02xx%02xx%02xx%02xx%02xx%02x from rpm %i velocity %i gear %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], rpm, velocity, gear);
res = hid_write(usbdevice->handle, bytes, cammusc12_hidupdate_buf_size);
}
else
{
slogd("no handle");
}
return res;
}
int cammusc12_free(USBDevice* wheeldevice)
{
int res = 0;
hid_close(wheeldevice->handle);
res = hid_exit();
if(wheeldevice->u.wheeldevice.useLua == true)
{
slogt("closing lua");
lua_close(wheeldevice->m.L);
}
return res;
}
int cammusc12_init_(USBDevice* wheeldevice)
{
slogi("initializing cammus c12 wheel...");
int res = 0;
res = hid_init();
wheeldevice->handle = hid_open(0x3416, 0x1023, NULL);
if (!wheeldevice->handle)
{
sloge("Could not find attached Cammus C12 Wheel");
res = hid_exit();
return 1;
}
slogd("Found Cammus C12 Wheel...");
return res;
}
int cammusc12_init(USBDevice* wheeldevice, const char* luafile)
{
wheeldevice->u.wheeldevice.useLua = false;
int res = cammusc12_init_(wheeldevice);
if(!wheeldevice->handle)
{
return 1;
}
if(luafile == NULL)
{
return res;
}
slogt("Using lua file for cammus c12 device");
lua_State* L = luaL_newstate();
luaL_openlibs(L);
//int top=lua_gettop(L);
int status = luaL_loadfile(L, luafile);
if (status) {
/* If something went wrong, error message is at the top of */
/* the stack */
sloge("There is an issue with your lua script");
fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
lua_close(L);
return -1;
//exit(1);
}
wheeldevice->u.wheeldevice.useLua = true;
lua_setglobal(L,"myFunc");
wheeldevice->m.L = L;
return res;
}
int cammusc12_customled_update(USBDevice* usbdevice, SimData* simdata)
{
int result = 1;
size_t bufsize = (cammusc12_total_leds * 3);
char ledbytes[bufsize];
for(int x = 0; x < bufsize; x++)
{
ledbytes[x] = 0x00;
}
lua_State* L = usbdevice->m.L;
lua_pushstring(L, "buff");
lua_pushlightuserdata(L, &ledbytes);
lua_settable(L, LUA_REGISTRYINDEX);
simdata_to_lua(L, simdata);
lua_setglobal(L, "simdata");
lua_pushinteger(L, cammusc12_total_leds);
lua_setglobal(L, "TotalLeds");
lua_register(L, "set_led_to_color", set_led_to_color);
lua_register(L, "set_led_range_to_color", set_led_range_to_color);
lua_register(L, "set_led_to_rgb_color", set_led_to_rgb_color);
lua_register(L, "set_led_range_to_rgb_color", set_led_range_to_rgb_color);
lua_register(L, "led_clear_all", led_clear_all);
lua_pushinteger(L, LUALEDCOLOR_RED);
lua_setglobal(L, "RED");
lua_pushinteger(L, LUALEDCOLOR_GREEN);
lua_setglobal(L, "GREEN");
lua_pushinteger(L, LUALEDCOLOR_BLUE);
lua_setglobal(L, "BLUE");
lua_pushinteger(L, LUALEDCOLOR_YELLOW);
lua_setglobal(L, "YELLOW");
lua_pushinteger(L, LUALEDCOLOR_ORANGE);
lua_setglobal(L, "ORANGE");
lua_getglobal(L,"myFunc");
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
{
fprintf(stderr, "Error calling Lua script: %s\n", lua_tostring(L, -1));
}
int res = 0;
unsigned char bytes[cammusc12_hidledupdate_buf_size];
for(int x = 0; x < cammusc12_hidledupdate_buf_size; x++)
{
bytes[x] = 0x00;
}
bytes[0] = 0xFA;
bytes[1] = 0xFB;
bytes[2] = 0x02;
for(int i = 0; i < cammusc12_total_leds; i++)
{
if(i >= 15) // make this a setting
{
for (int x = 3; x < cammusc12_hidledupdate_buf_size; x++)
{
bytes[x] = 0x00;
}
uint8_t led = i+1;
uint8_t red = ledbytes[(i * 3) + 0];
uint8_t green = ledbytes[(i * 3) + 1];
uint8_t blue = ledbytes[(i * 3) + 2];
bytes[3] = led;
bytes[4] = red;
bytes[5] = green;
bytes[6] = blue;
if (usbdevice->handle)
{
res = hid_write(usbdevice->handle, bytes, cammusc12_hidupdate_buf_size);
slogt("writing bytes x%02xx%02xx%02xx%02xx%02xx%02xx%02x from red %i green %i blue %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], red, green, blue);
}
else
{
slogd("no handle");
}
}
}
return res;
}

View File

@ -1,13 +0,0 @@
#ifndef _CAMMUSC12_H
#define _CAMMUSC12_H
#include "../../wheeldevice.h"
#include "../../simdevice.h"
int cammusc12_update(USBDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity);
int cammusc12_customled_update(USBDevice* usbdevice, SimData* simdata);
int cammusc12_init(USBDevice* wheeldevice, const char* luafile);
int cammusc12_free(USBDevice* wheeldevice);
#endif

View File

@ -1,99 +0,0 @@
#include <stdio.h>
#include <math.h>
#include <hidapi/hidapi.h>
#include "cammusc5.h"
#include "../../../slog/slog.h"
const int cammusc5_hidupdate_buf_size = 14;
const int num_avail_leds = 9;
int cammusc5_update(USBDevice* usbdevice, int maxrpm, int rpm, int gear, int velocity)
{
int res = 0;
unsigned char bytes[cammusc5_hidupdate_buf_size];
for (int x = 0; x < cammusc5_hidupdate_buf_size; x++)
{
bytes[x] = 0x00;
}
// byte 1 must be fc it seems
bytes[0] = 0xFC;
// byte 2 is number of lit leds, assuming 9 available leds,
// if we send 10, all leds will blink singling a gear change
// attempting to build in a margin before the maxrpm is achieved
int litleds = 0;
if(rpm > 0 && maxrpm > 0)
{
int rpmmargin = ceil(.05*maxrpm);
int rpminterval = (maxrpm-rpmmargin) / (num_avail_leds+1);
for (int l = 1; l <= (num_avail_leds+1); l++)
{
if(rpm >= (rpminterval * l))
{
litleds = l;
}
}
}
bytes[1] = litleds;
// bytes 2 and 3 are a 16 bit velocity
if ( velocity > 0 )
{
bytes[2] = (velocity >> 8) & 0xFF;
bytes[3] = velocity & 0xFF;
}
// byte 4 is gear
bytes[4] = gear-1;
slogt("writing bytes x%02xx%02xx%02xx%02xx%02x from rpm %i velocity %i gear %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], rpm, velocity, gear);
if (usbdevice->handle)
{
res = hid_write(usbdevice->handle, bytes, cammusc5_hidupdate_buf_size);
}
else
{
slogd("no handle");
}
return res;
}
int cammusc5_free(USBDevice* usbdevice)
{
int res = 0;
hid_close(usbdevice->handle);
res = hid_exit();
return res;
}
int cammusc5_init(USBDevice* usbdevice)
{
slogi("initializing cammus c5 wheel...");
int res = 0;
res = hid_init();
usbdevice->handle = hid_open(0x3416, 0x1021, NULL);
if (!usbdevice->handle)
{
sloge("Could not find attached Cammus C5 Wheel");
res = hid_exit();
return 1;
}
slogd("Found Cammus C5 Wheel...");
return res;
}

View File

@ -1,10 +0,0 @@
#ifndef _CAMMUSC5_H
#define _CAMMUSC5_H
#include "../../simdevice.h"
int cammusc5_update(USBDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity);
int cammusc5_init(USBDevice* wheeldevice);
int cammusc5_free(USBDevice* wheeldevice);
#endif

View File

@ -1,225 +0,0 @@
#include <stdio.h>
#include <math.h>
#include <unistd.h>
#include <hidapi/hidapi.h>
#include "gtneo.h"
#include <string.h>
#include "../../../slog/slog.h"
#include "../../serial/arduinoledlua.h"
// Simagic allows setting any led, therefore LED index should be defined.
struct LED {
uint8_t index;
uint8_t red;
uint8_t green;
uint8_t blue;
};
// Simagic is using SEND_FEATURE_REPORT
// ReportID is 240 (0xf0)
// Data length is 64 bytes
// Prefix is 00 00 00 00 00
// Then we have some descriptor. I had 0xEC and 0x03.
// Then: length of LED array
// Then: number of LED in hex
// RGB value for that LED
void prepare_device(hid_device *device) {
uint8_t prepare_report[SIMAGIC_MAX_PAYLOAD_SIZE] = {0};
prepare_report[0] = 0x00;
prepare_report[1] = 0x00;
prepare_report[2] = 0x00;
prepare_report[3] = 0x00;
prepare_report[4] = 0x00;
prepare_report[5] = SIMAGIC_LED_DESCRIPTOR_LED_CONTROL;
prepare_report[6] = SIMAGIC_LED_DESCRIPTOR_PREPARE_LEDS;
prepare_report[7] = 0x01;
uint8_t report_id_with_data[SIMAGIC_MAX_PAYLOAD_SIZE] = {SIMAGIC_LED_REPORT_ID}; // Report ID at the start
memcpy(&report_id_with_data[1], prepare_report, SIMAGIC_MAX_PAYLOAD_SIZE-1);
// Send the HID feature report
int res = hid_send_feature_report(device, report_id_with_data, sizeof(report_id_with_data));
if (res < 0) {
fprintf(stderr, "Failed to send HID feature report\n");
}
}
/*
Arbitrary function for sending out leds to simagic.
Inputs:
device: hid_device handle
leds: array of the struct LED
led_count: count of leds in leds array
*/
int send_leds(hid_device *device, struct LED* leds, size_t led_count) {
uint8_t descriptor[2] = {SIMAGIC_LED_DESCRIPTOR_LED_CONTROL, SIMAGIC_LED_DESCRIPTOR_SET_LEDS};
prepare_device(device);
int result = 0;
size_t led_i = 0;
while (led_i < led_count) {
size_t chunk_length = (SIMAGIC_MAX_PAYLOAD_SIZE -1-9) / 4; // 1 for reportid, 9 bytes for header, rest for LED data (each LED is 4 bytes)
size_t chunk_size = chunk_length * 4;
uint8_t report[SIMAGIC_MAX_PAYLOAD_SIZE] = {0}; // SIMAGIC_MAX_PAYLOAD_SIZE bytes per report
memcpy(report, (uint8_t[]){0x00, 0x00, 0x00, 0x00, 0x00}, 5); // Set the prefix (5 bytes)
memcpy(&report[5], descriptor, 2); // Set the descriptor (2 bytes)
// Fill in the LED data
for (size_t i = 0; i < chunk_length && led_i + i < led_count; ++i) {
memcpy(&report[8+i*4], &leds[led_i+i], 4);
report[7]++; // Set the LED array length for this chunk
}
uint8_t report_id_with_data[SIMAGIC_MAX_PAYLOAD_SIZE] = {SIMAGIC_LED_REPORT_ID};
memcpy(&report_id_with_data[1], report, SIMAGIC_MAX_PAYLOAD_SIZE-1);
// Send the HID feature report
int res = hid_send_feature_report(device, report_id_with_data, sizeof(report_id_with_data));
if (res < 0) {
fprintf(stderr, "Failed to send HID feature report chunk\n");
return -1;
}
result += res;
// Move to the next chunk of LEDs
led_i += chunk_length;
}
return result;
}
// default simpro profile:
// 97: b b b b b b b b b b b b b b b //flashing
// 93: b b b b b b b b b b b b b b b //flashing
// 90: g g g g g g g g g y y y w w w
// 88: g g g g g g g g g y y y
// 85: g g g g g g g g g
// 83: g g g g g g g
// 78: g g g g g
// 75: g g g
// 70: g
// less: empty (000000)
// Telemetry LEDs are 58-72
int simagic_gtneo_customled_update(USBDevice* usbdevice, SimData* simdata)
{
int res = 1;
size_t led_count = GT_NEO_LEDS_TOTAL-GT_NEO_LEDS_TELEMETRY_START; // Expose whole LED array
size_t bufsize = (led_count * 3);
char ledbytes[bufsize];
for(int x = 0; x < bufsize; x++)
ledbytes[x] = 0x00;
lua_State* L = usbdevice->m.L;
lua_pushstring(L, "buff");
lua_pushlightuserdata(L, &ledbytes);
lua_settable(L, LUA_REGISTRYINDEX);
simdata_to_lua(L, simdata);
lua_setglobal(L, "simdata");
lua_pushinteger(L, led_count);
lua_setglobal(L, "TotalLeds");
lua_register(L, "set_led_to_color", set_led_to_color);
lua_register(L, "set_led_range_to_color", set_led_range_to_color);
lua_register(L, "set_led_to_rgb_color", set_led_to_rgb_color);
lua_register(L, "set_led_range_to_rgb_color", set_led_range_to_rgb_color);
lua_register(L, "led_clear_all", led_clear_all);
lua_pushinteger(L, LUALEDCOLOR_RED);
lua_setglobal(L, "RED");
lua_pushinteger(L, LUALEDCOLOR_GREEN);
lua_setglobal(L, "GREEN");
lua_pushinteger(L, LUALEDCOLOR_BLUE);
lua_setglobal(L, "BLUE");
lua_pushinteger(L, LUALEDCOLOR_YELLOW);
lua_setglobal(L, "YELLOW");
lua_pushinteger(L, LUALEDCOLOR_ORANGE);
lua_setglobal(L, "ORANGE");
lua_getglobal(L,"myFunc");
if (lua_pcall(L, 0, 0, 0) != LUA_OK)
{
fprintf(stderr, "Error calling Lua script: %s\n", lua_tostring(L, -1));
}
// ledbytes is an array of leds. We need to copy colours to our struct LED and call 'send_leds'
struct LED teleleds[led_count];
for (int i = 0; i < led_count; i++) {
teleleds[i] = (struct LED){GT_NEO_LEDS_TELEMETRY_START+i, 0x00, 0x00, 0x00};
teleleds[i].red = ledbytes[(i * 3) + 0];
teleleds[i].green = ledbytes[(i * 3) + 1];
teleleds[i].blue = ledbytes[(i * 3) + 2];
}
res = send_leds(usbdevice->handle, teleleds, led_count);
return res;
}
int simagic_gtneo_init(USBDevice* wheeldevice, const char* luafile)
{
wheeldevice->u.wheeldevice.useLua = false;
slogi("initializing Simagic GT Neo wheel...");
int res = 0;
res = hid_init();
wheeldevice->handle = hid_open(SIMAGIC_GTNEO_VENDORID, SIMAGIC_GTNEO_PRODUCTID, NULL);
if (!wheeldevice->handle)
{
sloge("Could not find attached GT Neo Wheel");
res = hid_exit();
return 1;
}
slogd("Found Simagic GT Neo Wheel...");
slogt("Using lua file");
lua_State* L = luaL_newstate();
luaL_openlibs(L);
//int top=lua_gettop(L);
int status = luaL_loadfile(L, luafile);
if (status) {
/* If something went wrong, error message is at the top of */
/* the stack */
sloge("There is an issue with your lua script");
fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));
lua_close(L);
return -1;
//exit(1);
}
wheeldevice->u.wheeldevice.useLua = true;
lua_setglobal(L,"myFunc");
wheeldevice->m.L = L;
return res;
}
int simagic_gtneo_free(USBDevice* wheeldevice)
{
int res = 0;
hid_close(wheeldevice->handle);
res = hid_exit();
return res;
}

View File

@ -1,26 +0,0 @@
#ifndef _GTNEO_H
#define _GTNEO_H
#include "../../wheeldevice.h"
#include "../../simdevice.h"
#define SIMAGIC_GTNEO_VENDORID 0x3670
#define SIMAGIC_GTNEO_PRODUCTID 0x0805
#define SIMAGIC_MAX_PAYLOAD_SIZE 64
#define SIMAGIC_LED_REPORT_ID 0xf0 // LED control report id?
#define SIMAGIC_LED_DESCRIPTOR_LED_CONTROL 0xEC // LED control?
#define SIMAGIC_LED_DESCRIPTOR_PREPARE_LEDS 0x02 //probably something like 'prepare led canvas'
#define SIMAGIC_LED_DESCRIPTOR_SET_LEDS 0x03 //probably 'set temporare leds'
// expose whole LED array to the lua.
// Telemetry leds are 58-72
#define GT_NEO_LEDS_TELEMETRY_START 0
#define GT_NEO_LEDS_TOTAL 73
int simagic_gtneo_init(USBDevice* wheeldevice, const char* luafile);
int simagic_gtneo_free(USBDevice* wheeldevice);
int simagic_gtneo_customled_update(USBDevice* usbdevice, SimData* simdata);
#endif

View File

@ -1,98 +0,0 @@
#include <stdio.h>
#include <math.h>
#include <hidapi/hidapi.h>
#include "logitechg29.h"
#include "../../../slog/slog.h"
const int logitechg29_hidupdate_buf_size = 7;
int logitechg29_update(USBDevice* usbdevice, int maxrpm, int rpm, int gear, int velocity)
{
int res = 0;
uint8_t leds = 0;
float percent = 0;
if(maxrpm > 0)
{
percent = (float) rpm / (float) maxrpm;
if (percent > .84)
{
leds = 0b11111;
}
else if (percent > .69)
{
leds = 0b1111;
}
else if (percent > .39)
{
leds = 0b111;
}
else if (percent > .19)
{
leds = 0b11;
}
else if (percent > .4)
{
leds = 0b1;
}
else
{
leds = 0b0;
}
}
uint8_t bytes[7] = {
0xF8,
0x12,
leds,
0x00,
0x00,
0x00,
0x01
};
slogt("writing bytes x%02xx%02xx%02xx%02xx%02x from rpm %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[6], rpm);
if (usbdevice->handle)
{
res = hid_write(usbdevice->handle, bytes, logitechg29_hidupdate_buf_size);
}
else
{
slogd("no handle");
}
return res;
}
int logitechg29_free(USBDevice* usbdevice)
{
int res = 0;
hid_close(usbdevice->handle);
res = hid_exit();
return res;
}
int logitechg29_init(USBDevice* usbdevice)
{
slogi("initializing Logitech G29 wheel...");
int res = 0;
res = hid_init();
usbdevice->handle = hid_open(0x046D, 0xC24F, NULL);
if (!usbdevice->handle)
{
sloge("Could not find attached Logitech G29 Wheel");
res = hid_exit();
return 1;
}
slogd("Found Logitech G29 Wheel...");
return res;
}

View File

@ -1,10 +0,0 @@
#ifndef _LOGITECHG29_H
#define _LOGITECHG29_H
#include "../../simdevice.h"
int logitechg29_update(USBDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity);
int logitechg29_init(USBDevice* wheeldevice);
int logitechg29_free(USBDevice* wheeldevice);
#endif

View File

@ -1,126 +1,49 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include "usbdevice.h" #include "usbdevice.h"
#include "simdevice.h"
#include "../helper/parameters.h" #include "../helper/parameters.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simdata.h"
#include "../slog/slog.h" #include "../slog/slog.h"
int usbdev_update(SimDevice* this, SimData* simdata) int usbdev_update(USBDevice* usbdevice, SimData* simdata)
{ {
USBDevice* usbdevice = (void *) this->derived;
switch ( usbdevice->type ) switch ( usbdevice->type )
{ {
case USBDEV_UNKNOWN : case USBDEV_UNKNOWN :
case USBDEV_TACHOMETER : case USBDEV_TACHOMETER :
tachdev_update(usbdevice, simdata); tachdev_update(&usbdevice->u.tachdevice, simdata);
break;
case USBDEV_WHEEL :
wheeldev_update(usbdevice, simdata);
break;
case USBDEV_GENERICHAPTIC :
usbhapticdev_update(&usbdevice->u.hapticdevice, simdata, this->hapticeffect.tyre, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
break; break;
} }
return 0; return 0;
} }
int usbdev_free(SimDevice* this) int usbdev_free(USBDevice* usbdevice)
{ {
USBDevice* usbdevice = (void *) this->derived;
slogt("Usb device free");
switch ( usbdevice->type ) switch ( usbdevice->type )
{ {
case USBDEV_UNKNOWN : case USBDEV_UNKNOWN :
case USBDEV_TACHOMETER : case USBDEV_TACHOMETER :
tachdev_free(usbdevice); tachdev_free(&usbdevice->u.tachdevice);
break;
case USBDEV_WHEEL :
wheeldev_free(usbdevice);
break;
case USBDEV_GENERICHAPTIC :
usbhapticdev_free(&usbdevice->u.hapticdevice);
break; break;
} }
free(usbdevice);
return 0; return 0;
} }
int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds, SimInfo* siminfo) int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds)
{ {
slogi("initializing usb device..."); slogi("initializing usb device...");
int error = 0; int error = 0;
//usbdevice->type = USBDEV_TACHOMETER;
switch ( usbdevice->type ) switch ( usbdevice->type )
{ {
case USBDEV_UNKNOWN : case USBDEV_UNKNOWN :
case USBDEV_TACHOMETER : case USBDEV_TACHOMETER :
error = tachdev_init(usbdevice, ds); error = tachdev_init(&usbdevice->u.tachdevice, ds);
break;
case USBDEV_WHEEL :
error = wheeldev_init(usbdevice, ds);
break;
case USBDEV_GENERICHAPTIC :
error = usbhapticdev_init(&usbdevice->u.hapticdevice, ds, siminfo);
break; break;
} }
return error; return error;
} }
static const vtable usb_simdevice_vtable = { &usbdev_update, &usbdev_free };
USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) {
USBDevice* this = (USBDevice*) malloc(sizeof(USBDevice));
this->m.update = &update;
this->m.free = &simdevfree;
this->m.derived = this;
this->m.vtable = &usb_simdevice_vtable;
// TODO: turn this into a switch when we get more devices
this->type = USBDEV_TACHOMETER;
if(ds->dev_subtype == SIMDEVTYPE_USBWHEEL)
{
this->type = USBDEV_WHEEL;
}
// really generic haptic isn't and shouldn't be it's own type
// it's an attribute that is added to a device via composition
// same if that haptic device is a serial device
if (ds->dev_subtype == SIMDEVTYPE_USBHAPTIC)
{
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.effecttype = ds->effect_type;
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->type = USBDEV_GENERICHAPTIC;
}
int error = usbdev_init(this, ds, siminfo);
if (error != 0)
{
slogw("Did not initialize usb device due to error code %i", error);
free(this);
return NULL;
}
this->m.initialized = true;
return this;
}

View File

@ -2,7 +2,29 @@
#define _USBDEVICE_H #define _USBDEVICE_H
#include "tachdevice.h" #include "tachdevice.h"
#include "wheeldevice.h" #include "../helper/confighelper.h"
#include "usbhapticdevice.h" #include "../simulatorapi/simdata.h"
typedef enum
{
USBDEV_UNKNOWN = 0,
USBDEV_TACHOMETER = 1
}
USBType;
typedef struct
{
int id;
USBType type;
union
{
TachDevice tachdevice;
} u;
}
USBDevice;
int usbdev_update(USBDevice* usbdevice, SimData* simdata);
int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds);
int usbdev_free(USBDevice* usbdevice);
#endif #endif

View File

@ -1,115 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "usbhapticdevice.h"
#include "hapticeffect.h"
#include "usb/cslelitev3.h"
#include "usb/simagicp1000.h"
#include "../../helper/confighelper.h"
#include "../../simulatorapi/simapi/simapi/simdata.h"
#include "../../slog/slog.h"
int usbhapticdev_update(USBGenericHapticDevice* usbhapticdevice, SimData* simdata, int tyre, int useconfig, int* configcheck, char* configfile)
{
double play = slipeffect(simdata, usbhapticdevice->effecttype, tyre, usbhapticdevice->threshold, useconfig, configcheck, configfile);
if (play != usbhapticdevice->state)
{
int rplay = 0;
if(play > 0)
{
rplay = 1;
switch ( usbhapticdevice->type )
{
case USBHAPTIC_CSLELITEV3PEDALS:
cslelitev3_update(usbhapticdevice, usbhapticdevice->effecttype, rplay);
break;
case USBHAPTIC_SIMAGICP1000PEDALS:
simagicp1000_update(usbhapticdevice, usbhapticdevice->effecttype, rplay);
break;
}
}
else
{
switch ( usbhapticdevice->type )
{
case USBHAPTIC_CSLELITEV3PEDALS:
cslelitev3_update(usbhapticdevice, usbhapticdevice->effecttype, rplay);
break;
case USBHAPTIC_SIMAGICP1000PEDALS:
simagicp1000_update(usbhapticdevice, usbhapticdevice->effecttype, rplay);
break;
}
}
usbhapticdevice->state = play;
}
return 0;
}
int usbhapticdev_free(USBGenericHapticDevice* usbhapticdevice)
{
slogt("closing usb haptic device");
switch ( usbhapticdevice->type )
{
case USBHAPTIC_CSLELITEV3PEDALS:
cslelitev3_free(usbhapticdevice);
break;
case USBHAPTIC_SIMAGICP1000PEDALS:
simagicp1000_free(usbhapticdevice);
break;
}
return 0;
}
int usbhapticdev_init(USBGenericHapticDevice* usbhapticdevice, DeviceSettings* ds, SimInfo* siminfo)
{
if(siminfo->SimSupportsHapticEffects == false)
{
slogi("This sim does not support haptic effects");
return MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE;
}
int error = 0;
usbhapticdevice->state = 0;
usbhapticdevice->value0 = ds->usbdevsettings.value0;
usbhapticdevice->value1 = ds->usbdevsettings.value1;
usbhapticdevice->state = usbhapticdevice->value0;
usbhapticdevice->effecttype = ds->effect_type;
usbhapticdevice->threshold = ds->threshold;
if(ds->effect_type == EFFECT_TYRESLIP)
{
usbhapticdevice->effecttype = EFFECT_TYRESLIP;
}
if(ds->effect_type == EFFECT_TYRELOCK)
{
usbhapticdevice->effecttype = EFFECT_TYRELOCK;
}
if(ds->effect_type == EFFECT_ABSBRAKES)
{
usbhapticdevice->effecttype = EFFECT_ABSBRAKES;
}
slogi("initializing standalone usb haptic device...");
// detection of usb device model
switch (ds->dev_subsubtype) {
case (SIMDEVSUBTYPE_SIMAGICP1000PEDALS):
usbhapticdevice->type = USBHAPTIC_SIMAGICP1000PEDALS;
error = simagicp1000_init(usbhapticdevice);
break;
case (SIMDEVSUBTYPE_CSLELITEV3PEDALS):
usbhapticdevice->type = USBHAPTIC_CSLELITEV3PEDALS;
error = cslelitev3_init(usbhapticdevice);
break;
default:
slogw("Possibly unknown device");
}
return error;
}

View File

@ -1,37 +0,0 @@
#ifndef _USBHAPTICDEVICE_H
#define _USBHAPTICDEVICE_H
#include <hidapi/hidapi.h>
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
typedef enum
{
USBHAPTIC_UNKNOWN = 0,
USBHAPTIC_CSLELITEV3PEDALS = 1,
USBHAPTIC_SIMAGICP1000PEDALS = 2
}
HapticType;
typedef struct
{
int id;
HapticType type;
double state;
double threshold;
int value0;
int value1;
VibrationEffectType effecttype;
FILE* filehandle;
hid_device* handle;
char* dev;
}
USBGenericHapticDevice;
int usbhapticdev_update(USBGenericHapticDevice* hapticdevice, SimData* simdata, int tyre, int useconfig, int* configcheck, char* configfile);
int usbhapticdev_init(USBGenericHapticDevice* hapticdevice, DeviceSettings* ds, SimInfo* siminfo);
int usbhapticdev_free(USBGenericHapticDevice* hapticdevice);
#endif

View File

@ -1,151 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "usb/wheels/logitechg29.h"
#include "usb/wheels/cammusc5.h"
#include "usb/wheels/cammusc12.h"
#include "usb/wheels/gtneo.h"
#include "serial/moza.h"
#include "simdevice.h"
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
#include "../slog/slog.h"
int wheeldev_update(USBDevice* usbdevice, SimData* simdata)
{
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
switch ( wheeldevice->type )
{
case WHEELDEV_UNKNOWN :
case WHEELDEV_CAMMUSC5 :
cammusc5_update(usbdevice, simdata->maxrpm, simdata->rpms, simdata->gear, simdata->velocity);
break;
case WHEELDEV_LOGITECHG29 :
logitechg29_update(usbdevice, simdata->maxrpm, simdata->rpms, simdata->gear, simdata->velocity);
break;
case WHEELDEV_CAMMUSC12 :
if(usbdevice->u.wheeldevice.useLua == true)
{
cammusc12_customled_update(usbdevice, simdata);
}
else
{
cammusc12_update(usbdevice, simdata->maxrpm, simdata->rpms, simdata->gear, simdata->velocity);
}
break;
case WHEELDEV_SIMAGICGTNEO :
if(usbdevice->u.wheeldevice.useLua == true)
{
simagic_gtneo_customled_update(usbdevice, simdata);
}
else
{
sloge("GT Neo requires config file");
}
break;
}
return 0;
}
int wheeldev_free(USBDevice* usbdevice)
{
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
slogt("wheel device free");
switch ( wheeldevice->type )
{
case WHEELDEV_UNKNOWN :
case WHEELDEV_CAMMUSC5 :
cammusc5_update(usbdevice, 0, 0, 0, 0);
cammusc5_free(usbdevice);
break;
case WHEELDEV_LOGITECHG29 :
logitechg29_update(usbdevice, 0, 0, 0, 0);
logitechg29_free(usbdevice);
break;
case WHEELDEV_CAMMUSC12 :
cammusc12_free(usbdevice);
if(wheeldevice->useLua == true)
{
free(usbdevice->m.device_specific_config_file);
}
break;
}
return 0;
}
int wheeldev_init(USBDevice* usbdevice, DeviceSettings* ds)
{
slogi("initializing wheel device...");
int error = 0;
// detection of wheel model
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
switch (ds->dev_subsubtype) {
case SIMDEVSUBTYPE_CAMMUSC5:
wheeldevice->type = WHEELDEV_CAMMUSC5;
slogi("Attempting to initialize cammus C5");
error = cammusc5_init(usbdevice);
break;
case SIMDEVSUBTYPE_LOGITECH_G29:
wheeldevice->type = WHEELDEV_LOGITECHG29;
slogi("Attempting to initialize Logitech G29");
error = logitechg29_init(usbdevice);
break;
case SIMDEVSUBTYPE_CAMMUSC12:
wheeldevice->type = WHEELDEV_CAMMUSC12;
slogi("Attempting to initialize cammus C12");
if(ds->has_config == true)
{
usbdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
error = cammusc12_init(usbdevice, usbdevice->m.device_specific_config_file);
if(error != 0)
{
if(usbdevice->m.device_specific_config_file != NULL)
{
free(usbdevice->m.device_specific_config_file);
}
}
}
else
{
error = cammusc12_init(usbdevice, NULL);
}
break;
case SIMDEVSUBTYPE_SIMAGICGTNEO:
wheeldevice->type = WHEELDEV_SIMAGICGTNEO;
slogi("Attempting to initialize Simagic GT Neo");
if(ds->has_config == true)
{
usbdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
error = simagic_gtneo_init(usbdevice, usbdevice->m.device_specific_config_file);
if(error != 0)
{
if(usbdevice->m.device_specific_config_file != NULL)
{
free(usbdevice->m.device_specific_config_file);
}
}
}
else
{
sloge("Simagic GT Neo requires lua config file to function");
error = -1;
}
break;
default:
wheeldevice->type = WHEELDEV_UNKNOWN;
slogw("Unknown cammus wheel detected, trying C5");
error = cammusc5_init(usbdevice);
break;
}
return error;
}

View File

@ -1,30 +0,0 @@
#ifndef _WHEELDEVICE_H
#define _WHEELDEVICE_H
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
//typedef int (*tachdev_update)(int revs);
typedef enum
{
WHEELDEV_UNKNOWN = 0,
WHEELDEV_CAMMUSC5 = 1,
WHEELDEV_CAMMUSC12 = 2,
WHEELDEV_MOZAR5 = 3,
WHEELDEV_SIMAGICGTNEO = 4,
WHEELDEV_LOGITECHG29 = 5
}
WheelType;
typedef struct
{
int id;
WheelType type;
bool useLua;
char* port;
}
WheelDevice;
#endif

View File

@ -3,7 +3,6 @@ set(gameloop_source_files
gameloop.h gameloop.h
tachconfig.c tachconfig.c
tachconfig.h tachconfig.h
loopdata.h
) )
set(LIBXML_INCLUDE_DIR /usr/include/libxml2) set(LIBXML_INCLUDE_DIR /usr/include/libxml2)

View File

@ -1,4 +1,3 @@
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -6,49 +5,16 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <poll.h> #include <poll.h>
#include <termios.h> #include <termios.h>
#include <signal.h>
#include <uv.h>
#include "gameloop.h" #include "gameloop.h"
#include "loopdata.h" #include "../helper/parameters.h"
#include "../helper/confighelper.h" #include "../helper/confighelper.h"
#include "../devices/simdevice.h" #include "../devices/simdevice.h"
#include "../devices/hapticeffect.h" #include "../simulatorapi/simdata.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simmapper.h"
#include "../simulatorapi/simapi/simapi/simmapper.h"
#include "../slog/slog.h" #include "../slog/slog.h"
#define DEFAULT_UPDATE_RATE 240.0 #define DEFAULT_UPDATE_RATE 120.0
#define SIM_CHECK_RATE 1.0
bool go = false;
bool go2 = false;
struct sigaction act;
uv_idle_t idler;
uv_timer_t datachecktimer;
uv_timer_t showstatstimer;
uv_timer_t datamaptimer;
uv_timer_t tyrediametertimer;
uv_udp_t recv_socket;
bool doui = false;
int appstate = 0;
void shmdatamapcallback(uv_timer_t* handle);
void showstatscallback(uv_timer_t* handle);
void datacheckcallback(uv_timer_t* handle);
void tyrediametercheckcallback(uv_timer_t* handle);
void startdatalogger(MonocoqueSettings* ms, loop_data* l);
static void close_walk_cb(uv_handle_t* handle, void* arg)
{
if (!uv_is_closing(handle))
{
uv_close(handle, NULL);
}
}
#define ASSERT(expr) expr
int showstats(SimData* simdata) int showstats(SimData* simdata)
{ {
@ -199,441 +165,19 @@ int showstats(SimData* simdata)
fflush(stdout); fflush(stdout);
} }
void simapilib_loginfo(char* message) int looper(SimDevice* devices[], int numdevices, Simulator simulator)
{
slogi(message);
}
void simapilib_logdebug(char* message)
{
slogd(message);
}
void simapilib_logtrace(char* message)
{
slog_display(SLOG_TRACE, 1, message);
}
void on_timer_close_complete(uv_handle_t* handle)
{
free(handle);
}
void devicetimercallback(uv_timer_t* handle)
{
void* b = uv_handle_get_data((uv_handle_t*) handle);
device_loop_data* f = (device_loop_data*) b;
SimData* simdata = f->simdata;
SimDevice* device = f->simdevice;
device->update(device, simdata);
}
void tyrediametercheckcallback(uv_timer_t* handle)
{
void* b = uv_handle_get_data((uv_handle_t*) handle);
device_loop_data* f = (device_loop_data*) b;
SimData* simdata = f->simdata;
if(simdata->car != NULL || simdata->car[0] != 0)
{
slogi("car is %s", simdata->car);
// check for saved tyre diameter in config file
// if not saved version exists get tyre diameter and save it
// use config check variable to track if the config check has been performed
// avoid many opens of the same file
int error = 0;
if(hasTyreDiameter(simdata)==false && f->ms->configcheck == 0)
{
slogi("attempting load of tyre diameter config");
error = loadtyreconfig(simdata, f->ms->tyre_diameter_config, true);
f->ms->configcheck = 1;
}
if(hasTyreDiameter(simdata)==false)
{
slogt("could not find tyre diameter in config file, attempting to calculate new");
getTyreDiameter(simdata);
// if this successfully calculates data the diameters will be saved to the file after the
// sim stops actively mapping data
}
}
if(hasTyreDiameter(simdata)==true)
{
int a = loadtyreconfig(simdata, f->ms->tyre_diameter_config, false);
if(a < 0)
{
slogi("saving new tyre diameter config for car %s");
savetyreconfig(simdata, f->ms->tyre_diameter_config);
}
uv_timer_stop(handle);
}
}
void looprun(MonocoqueSettings* ms, loop_data* f, SimData* simdata)
{
if (doui == true)
{
slogi("looking for ui config %s pass 1 simapi %i", ms->config_str, f->siminfo.simulatorapi);
int confignum = getconfigtouse2(ms->config_str, simdata->car, f->siminfo.simulatorapi);
slogi("first pass finished");
if(confignum == -1)
{
slogi("looking for ui config %s pass 2", ms->config_str);
confignum = getconfigtouse1(ms->config_str, simdata->car, f->siminfo.simulatorapi);
}
if(confignum == -1)
{
slogi("looking for ui config %s pass 3", ms->config_str);
confignum = getconfigtouse(ms->config_str, simdata->car, f->siminfo.simulatorapi);
}
int configureddevices;
configcheck(ms->config_str, confignum, &configureddevices);
DeviceSettings* ds = malloc(configureddevices * sizeof(DeviceSettings));
slogd("loading confignum %i, with %i devices.", confignum, configureddevices);
f->numdevices = uiloadconfig(ms->config_str, confignum, configureddevices, ms, ds);
if(ms->useconfig == 1)
{
ms->configcheck = 0;
}
f->simdevices = malloc(f->numdevices * sizeof(SimDevice));
int initdevices = devinit(f->simdevices, &f->siminfo, configureddevices, ds, ms);
slogi("initialized %i devices", initdevices);
for( int i = 0; i < configureddevices; i++)
{
settingsfree(ds[i]);
}
free(ds);
int numdevices = f->numdevices;
SimDevice* devices = f->simdevices;
f->device_timers = (uv_timer_t*) (malloc(uv_handle_size(UV_TIMER) * numdevices));
f->device_batons = (device_loop_data*) (malloc(sizeof(device_loop_data) * numdevices));
f->started_tyre_calc_thread = false;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
device_loop_data* dld = &f->device_batons[x];
dld->simdevice = &devices[x];
dld->simdata = simdata;
uv_timer_t* dt = &f->device_timers[x];
uv_timer_init(uv_default_loop(), dt);
uv_handle_set_data((uv_handle_t*) dt, (void*) dld);
int interval = 1000/devices[x].fps;
uv_timer_start(dt, devicetimercallback, 0, interval);
slogi("starting device type %i at id at %i fps: %i (%i ms ticks)", devices[x].type, x, devices[x].fps, interval);
SimInfo siminfo = f->siminfo;
if(f->started_tyre_calc_thread == false)
{
if(devices[x].hapticeffect.effecttype == EFFECT_TYRELOCK || devices[x].hapticeffect.effecttype == EFFECT_TYRESLIP || devices[x].hapticeffect.effecttype == EFFECT_ABSBRAKES)
{
if(siminfo.SimCalculatesTyreDiameter == false && siminfo.SimSupportsHapticEffects == true && siminfo.SimCalculatesSlipRatio == false && f->ms->useconfig == 1 && f->ms->tyre_diameter_config != NULL)
{
slogi("Starting thread to calculate tyre diameters and save to config file");
f->started_tyre_calc_thread = true;
f->tyrebaton = (device_loop_data*) malloc(sizeof(device_loop_data));
f->tyrebaton->simdata = simdata;
f->tyrebaton->ms = f->ms;
uv_handle_set_data((uv_handle_t*) &tyrediametertimer, (void*) f->tyrebaton);
uv_timer_start(&tyrediametertimer, tyrediametercheckcallback, 0, 1000);
}
}
}
}
}
uv_timer_start(&showstatstimer, showstatscallback, 0, 100);
doui = false;
}
}
void showstatscallback(uv_timer_t* handle)
{
void* b = uv_handle_get_data((uv_handle_t*) handle);
loop_data* f = (loop_data*) b;
SimData* simdata = f->simdata;
if(appstate == 2)
{
showstats(simdata);
}
}
void releaseloop(loop_data* f, SimData* simdata, SimMap* simmap)
{
slogi("release loop");
if(f->releasing == false)
{
f->releasing = true;
uv_timer_stop(&datamaptimer);
uv_timer_stop(&showstatstimer);
if (uv_is_active((uv_handle_t*)&recv_socket))
{
uv_udp_recv_stop(&recv_socket);
}
// Close the socket handle so it can be reinitialized with a different port
//if (!uv_is_closing((uv_handle_t*)&recv_socket))
//{
// uv_close((uv_handle_t*)&recv_socket, NULL);
//}
slogi("releasing devices, please wait");
//attempt tyre diameter saving
uv_timer_stop(&tyrediametertimer);
if(f->started_tyre_calc_thread == true)
{
free(f->tyrebaton);
}
f->uion = false;
SimDevice* devices = f->simdevices;
int numdevices = f->numdevices;
// help things spin down
simdata->simstatus = 0;
simdata->rpms = 0;
simdata->velocity = 0;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
uv_timer_t* dt = &f->device_timers[x];
slogt("attempting device timer stop and release");
slogt("timer active status %i", uv_is_active((uv_handle_t*) dt));
uv_timer_stop(dt);
//uv_close((uv_handle_t*) dt, on_timer_close_complete);
}
}
free(f->device_batons);
free(f->device_timers);
slogt("stopped device timers");
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(1);
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].free(&devices[x]);
}
}
free(devices);
int r = simapi_sim_clear(simdata, simmap, false);
slogd("simfree returned %i", r);
f->numdevices = 0;
slogi("stopped mapping data, press q again to quit");
//stopui(ms->ui_type, f);
// free loop data
if(appstate > 0)
{
slogi("restarting checking for data...");
uv_timer_start(&datachecktimer, datacheckcallback, 0, 1000);
}
f->releasing = false;
if(appstate > 1)
{
appstate = 1;
}
}
}
void shmdatamapcallback(uv_timer_t* handle)
{
void* b = uv_handle_get_data((uv_handle_t*) handle);
loop_data* f = (loop_data*) b;
SimData* simdata = f->simdata;
SimMap* simmap = f->simmap;
MonocoqueSettings* ms = f->ms;
//appstate = 2;
if (appstate == 2)
{
simapi_datamap(simdata, simmap, f->siminfo.mapapi, false, NULL);
looprun(ms, f, simdata);
}
if (f->siminfo.isSimOn == false || simdata->simstatus <= 1 || appstate <= 1)
{
releaseloop(f, simdata, simmap);
}
}
void on_alloc(uv_handle_t* client, size_t suggested_size, uv_buf_t* buf) {
buf->base = malloc(suggested_size);
buf->len = suggested_size;
bzero(buf->base, suggested_size);
slogt("udp malloc:%lu %p\n",buf->len,buf->base);
}
static void on_udp_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* rcvbuf, const struct sockaddr* addr, unsigned flags) {
if (nread > 0) {
slogt("udp data received");
}
if (nread <= 0) {
free(rcvbuf->base);
return;
}
char* a;
a = rcvbuf->base;
void* b = uv_handle_get_data((uv_handle_t*) handle);
loop_data* f = (loop_data*) b;
SimData* simdata = f->simdata;
SimMap* simmap = f->simmap;
MonocoqueSettings* ms = f->ms;
if (appstate == 2)
{
simapi_datamap(simdata, simmap, f->siminfo.mapapi, true, a);
looprun(ms, f, simdata);
}
if (f->siminfo.isSimOn == false || simdata->simstatus <= 1 || appstate <= 1)
{
releaseloop(f, simdata, simmap);
}
slogt("udp free :%lu %p\n",rcvbuf->len,rcvbuf->base);
free(rcvbuf->base);
}
int startudp(int port)
{
struct sockaddr_in recv_addr;
uv_ip4_addr("0.0.0.0", port, &recv_addr);
int err = uv_udp_bind(&recv_socket, (const struct sockaddr *) &recv_addr, UV_UDP_REUSEADDR);
slogt("initial udp error is %i", err);
return err;
}
void udpstart(MonocoqueSettings* sms, loop_data* f, SimData* simdata, SimMap* simmap)
{
if (appstate == 2)
{
simapi_datamap(simdata, simmap, f->siminfo.simulatorapi, true, NULL);
if (doui == true)
{
looprun(sms, f, simdata);
}
}
}
void datacheckcallback(uv_timer_t* handle)
{
void* b = uv_handle_get_data((uv_handle_t*) handle);
loop_data* f = (loop_data*) b;
SimData* simdata = f->simdata;
SimMap* simmap = f->simmap;
if ( appstate == 1 )
{
f->siminfo = simapi_get_sim(simdata, simmap, f->ms->force_udp_mode, startudp, false);
if(f->ms->force_udp_mode == true)
{
f->use_udp = true;
}
}
if (f->siminfo.isSimOn == true && simdata->simstatus >= 2)
{
if ( appstate == 1 )
{
appstate++;
doui = true;
simdata->tyrediameter[0] = -1;
simdata->tyrediameter[1] = -1;
simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1;
if(f->use_udp == true || f->siminfo.SimUsesUDP == true)
{
slogt("starting udp receive loop");
udpstart(f->ms, f, simdata, simmap);
uv_udp_recv_start(&recv_socket, on_alloc, on_udp_recv);
slogt("udp receive loop started");
}
else
{
int interval = 1000 / f->ms->fps;
slogd("starting telemetry mapping at %i fps (%i ms ticks)", f->ms->fps, interval);
uv_timer_start(&datamaptimer, shmdatamapcallback, 2000, interval);
}
}
if(appstate == 2)
{
f->siminfo = simapi_get_sim(simdata, simmap, f->ms->force_udp_mode, NULL, false);
if(f->siminfo.isSimOn == false)
{
appstate = 1;
releaseloop(f, simdata, simmap);
}
}
}
if (appstate == 0)
{
slogi("stopped checking for data");
uv_timer_stop(handle);
}
}
void cb(uv_poll_t* handle, int status, int events)
{
void* b = uv_handle_get_data((uv_handle_t*) handle);
loop_data* f = (loop_data*) b;
char ch;
scanf("%c", &ch);
if (ch == 'q')
{
if(f->releasing == false && doui == false)
{
appstate--;
fprintf(stdout, "\nUser requested stop appstate is now %i\n", appstate);
fflush(stdout);
slogi("User requested stop appstate is now %i", appstate);
}
}
if (appstate == 0)
{
slogi("Monocoque is exiting...");
uv_udp_recv_stop(&recv_socket);
uv_timer_stop(&datamaptimer);
uv_timer_stop(&showstatstimer);
// at this point these below should be the only active threads
uv_timer_stop(&datachecktimer);
uv_poll_stop(handle);
}
}
int monocoque_mainloop(MonocoqueSettings* ms)
{ {
slogi("preparing game loop with %i devices...", numdevices);
SimData* simdata = malloc(sizeof(SimData)); SimData* simdata = malloc(sizeof(SimData));
SimMap* simmap = simapi_simmap_create(); SimMap* simmap = malloc(sizeof(SimMap));
int error = siminit(simdata, simmap, simulator);
if (error != MONOCOQUE_ERROR_NONE)
{
return error;
}
struct termios newsettings, canonicalmode; struct termios newsettings, canonicalmode;
tcgetattr(0, &canonicalmode); tcgetattr(0, &canonicalmode);
@ -645,378 +189,52 @@ int monocoque_mainloop(MonocoqueSettings* ms)
char ch; char ch;
struct pollfd mypoll = { STDIN_FILENO, POLLIN|POLLPRI }; struct pollfd mypoll = { STDIN_FILENO, POLLIN|POLLPRI };
uv_poll_t* poll = (uv_poll_t*) malloc(uv_handle_size(UV_POLL)); double update_rate = DEFAULT_UPDATE_RATE;
int t=0;
loop_data* baton = (loop_data*) malloc(sizeof(loop_data)); int go = true;
baton->simmap = simmap; while (go == true)
baton->simdata = simdata;
baton->ms = ms;
baton->uion = false;
baton->releasing = false;
baton->use_udp = false;
baton->req.data = (void*) baton;
simapi_set_log_info(simapilib_loginfo);
simapi_set_log_debug(simapilib_logdebug);
simapi_set_log_trace(simapilib_logtrace);
if (0 != uv_poll_init(uv_default_loop(), poll, 0))
{ {
return 1; simdatamap(simdata, simmap, simulator);
}; showstats(simdata);
t++;
uv_udp_init(uv_default_loop(), &recv_socket); if(simdata->rpms<250)
uv_timer_init(uv_default_loop(), &datachecktimer);
uv_timer_init(uv_default_loop(), &showstatstimer);
uv_timer_init(uv_default_loop(), &datamaptimer);
uv_timer_init(uv_default_loop(), &tyrediametertimer);
slogd("setting initial app state");
appstate = 1;
uv_handle_set_data((uv_handle_t*) &datachecktimer, (void*) baton);
uv_handle_set_data((uv_handle_t*) &datamaptimer, (void*) baton);
uv_handle_set_data((uv_handle_t*) &showstatstimer, (void*) baton);
uv_handle_set_data((uv_handle_t*) &recv_socket, (void*) baton);
uv_handle_set_data((uv_handle_t*) poll, (void*) baton);
if (0 != uv_poll_start(poll, UV_READABLE, cb))
{ {
return 2; simdata->rpms=250;
}; }
uv_timer_start(&datachecktimer, datacheckcallback, 1000, 1000); for (int x = 0; x < numdevices; x++)
{
fprintf(stdout, "Searching for sim data... Press q to quit...\n"); if (devices[x]->type == SIMDEV_SERIAL)
uv_run(uv_default_loop(), UV_RUN_DEFAULT); {
if(t>=update_rate)
uv_stop(uv_default_loop()); {
//uv_walk(uv_default_loop(), close_walk_cb, NULL); devupdate(devices[x], simdata);
uv_run(uv_default_loop(), UV_RUN_DEFAULT); }
uv_loop_close(uv_default_loop()); }
uv_library_shutdown(); else
slogi("All threads stopped..."); {
devupdate(devices[x], simdata);
}
}
if(t>=update_rate)
{
t=0;
}
if( poll(&mypoll, 1, 1000.0/update_rate) )
{
scanf("%c", &ch);
if(ch == 'q')
{
go = false;
}
}
}
fprintf(stdout, "\n"); fprintf(stdout, "\n");
fflush(stdout); fflush(stdout);
tcsetattr(0, TCSANOW, &canonicalmode); tcsetattr(0, TCSANOW, &canonicalmode);
free(baton);
free(simdata); free(simdata);
free(simmap); free(simmap);
return 0; return 0;
} }
int tester(SimDevice* devices, int numdevices)
{
slogi("preparing test with %i devices...", numdevices);
SimData* simdata = malloc(sizeof(SimData));
struct termios newsettings, canonicalmode;
tcgetattr(0, &canonicalmode);
newsettings = canonicalmode;
newsettings.c_lflag &= (~ICANON & ~ECHO);
newsettings.c_cc[VMIN] = 1;
newsettings.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &newsettings);
fprintf(stdout, "\n");
simdata->car[0] = 'C';
simdata->car[1] = 'A';
simdata->car[2] = 'R';
simdata->car[3] = '\0';
simdata->gear = SIMAPI_GEAR_NEUTRAL;
simdata->gearc[0] = 0x4e;
simdata->gearc[1] = 0;
simdata->velocity = 16;
simdata->rpms = 100;
simdata->maxrpm = 8000;
simdata->abs = 0;
simdata->tyrediameter[0] = -1;
simdata->tyrediameter[1] = -1;
simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1;
simdata->Xvelocity = 0;
simdata->Yvelocity = 100;
simdata->Zvelocity = 0;
sleep(1);
fprintf(stdout, "Revving rpm from 1000 to 8000 and back\n");
// TODO: look into this, my serial leds make this hang
for (int r = 0; r < 8000; r += 3)
{
simdata->rpms = 1000 + r;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
usleep(1000);
}
for (int r = 0; r < 8000; r += 3)
{
simdata->rpms = 9000 - r;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
usleep(1000);
}
fprintf(stdout, "Setting rpms to 1000\n");
simdata->rpms = 1000;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Green Flag!\n");
simdata->playerflag = SIMAPI_FLAG_GREEN;
fprintf(stdout, "Shifting into first gear\n");
simdata->gear = SIMAPI_GEAR_FIRST;
simdata->gearc[0] = 0x31;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Setting speed to 100\n");
simdata->velocity = 100;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "testing wheel spin\n");
simdata->velocity = 15;
simdata->tyreRPS[0] = 50;
simdata->tyreRPS[1] = 50;
simdata->tyreRPS[2] = 50;
simdata->tyreRPS[3] = 50;
simdata->tyrediameter[0] = 0.638636385206394;
simdata->tyrediameter[1] = 0.633384434597093;
simdata->tyrediameter[2] = 0.710475735564615;
simdata->tyrediameter[3] = 0.710475735564615;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Testing wheel Lock\n");
simdata->tyreRPS[0] = 25;
simdata->tyreRPS[1] = 25;
simdata->tyreRPS[2] = 25;
simdata->tyreRPS[3] = 25;
simdata->velocity = 150;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Shifting into second gear\n");
simdata->tyreRPS[0] = 0;
simdata->tyreRPS[1] = 0;
simdata->tyreRPS[2] = 0;
simdata->tyreRPS[3] = 0;
simdata->tyrediameter[0] = -1;
simdata->tyrediameter[1] = -1;
simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1;
simdata->abs = 0;
simdata->gear = SIMAPI_GEAR_SECOND;
simdata->gearc[0] = 0x32;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Testing abs brake lock Lock\n");
simdata->tyreRPS[0] = 50;
simdata->tyreRPS[1] = 50;
simdata->tyreRPS[2] = 50;
simdata->tyreRPS[3] = 50;
simdata->tyrediameter[0] = 0.638636385206394;
simdata->tyrediameter[1] = 0.633384434597093;
simdata->tyrediameter[2] = 0.710475735564615;
simdata->tyrediameter[3] = 0.710475735564615;
simdata->abs = .11;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Setting speed to 200\n");
simdata->tyreRPS[0] = 0;
simdata->tyreRPS[1] = 0;
simdata->tyreRPS[2] = 0;
simdata->tyreRPS[3] = 0;
simdata->tyrediameter[0] = -1;
simdata->tyrediameter[1] = -1;
simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1;
simdata->abs = 0;
simdata->velocity = 200;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Yellow Flag!\n");
simdata->playerflag = SIMAPI_FLAG_YELLOW;
fprintf(stdout, "Shifting into third gear\n");
simdata->gear = SIMAPI_GEAR_THIRD;
simdata->gearc[0] = 0x33;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Setting rpms to 2000\n");
simdata->rpms = 2000;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Blue Flag!\n");
simdata->playerflag = SIMAPI_FLAG_BLUE;
fprintf(stdout, "Setting rpms to 4000\n");
simdata->rpms = 4000;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Shifting into fourth gear\n");
simdata->gear = SIMAPI_GEAR_FOURTH;
simdata->gearc[0] = 0x34;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Setting speed to 300\n");
simdata->velocity = 300;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Setting rpms to 7000\n");
simdata->rpms = 7000;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3);
fprintf(stdout, "Red Flag!\n");
simdata->playerflag = SIMAPI_FLAG_RED;
fprintf(stdout, "Setting rpms to 8000\n");
simdata->rpms = 8000;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
for(int x = 0; x < 100; x++)
{
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
}
sleep(3);
simdata->velocity = 0;
simdata->rpms = 100;
simdata->gear = SIMAPI_GEAR_NEUTRAL;
simdata->gearc[0] = 0x4e;
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(1);
fflush(stdout);
tcsetattr(0, TCSANOW, &canonicalmode);
free(simdata);
return 0;
}

View File

@ -1,7 +1,4 @@
#include "../devices/simdevice.h" #include "../devices/simdevice.h"
#include "../helper/parameters.h" #include "../helper/parameters.h"
int tester(SimDevice* devices, int numdevices); int looper (SimDevice* devices[], int numdevices, Simulator simulator);
int looper(SimDevice* devices, int numdevices, Parameters* p);
int monocoque_mainloop(MonocoqueSettings* ms);

View File

@ -1,44 +0,0 @@
#ifndef _LOOPDATA_H
#define _LOOPDATA_H
#include <uv.h>
#include "../helper/parameters.h"
#include "../helper/confighelper.h"
#include "../devices/simdevice.h"
#include "../simulatorapi/simapi/simapi/simdata.h"
#include "../simulatorapi/simapi/simapi/simmapper.h"
typedef struct device_loop_data
{
// these are all pointers to pointers and freed independently, usually in releaseloop
uv_work_t req;
SimData* simdata;
SimDevice* simdevice;
MonocoqueSettings* ms;
SimInfo siminfo;
} device_loop_data;
typedef struct loop_data
{
uv_work_t req;
bool use_udp;
bool uion;
bool releasing;
bool started_tyre_calc_thread;
int numdevices;
SimInfo siminfo;
// monocoque settings is a pointer from monocoque.c and freed there
MonocoqueSettings* ms;
// these are freed at the end of the main gameloop
SimData* simdata;
SimMap* simmap;
// these all get malloced in looprun and freed in releaseloop
SimDevice* simdevices;
uv_timer_t* device_timers;
device_loop_data* device_batons;
device_loop_data* tyrebaton;
} loop_data;
#endif

View File

@ -15,7 +15,7 @@
#include <libxml/tree.h> #include <libxml/tree.h>
#include "../devices/simdevice.h" #include "../devices/simdevice.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simdata.h"
#include "../slog/slog.h" #include "../slog/slog.h"
#define DEFAULT_UPDATE_RATE 30.0 #define DEFAULT_UPDATE_RATE 30.0
@ -183,7 +183,7 @@ int config_tachometer(int max_revs, int granularity, const char* save_file, SimD
WriteXmlFromArrays(nodes, rpm_array, values_array, max_revs, save_file); WriteXmlFromArrays(nodes, rpm_array, values_array, max_revs, save_file);
sleep(2); sleep(2);
simdata->pulses = 0; simdata->pulses = 0;
simdevice->update(simdevice, simdata); devupdate(simdevice, simdata);
fflush(stdout); fflush(stdout);
return 0; return 0;

View File

@ -2,7 +2,7 @@
#define _TACHCONFIG_H #define _TACHCONFIG_H
#include "../devices/simdevice.h" #include "../devices/simdevice.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simdata.h"
int config_tachometer(int max_revs, int granularity, const char* save_file, SimDevice* simdevice, SimData* simdata); int config_tachometer(int max_revs, int granularity, const char* save_file, SimDevice* simdevice, SimData* simdata);

View File

@ -1,237 +1,59 @@
#include <dirent.h>
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <ctype.h>
#include <libxml/parser.h> #include <libxml/parser.h>
#include <libxml/xmlreader.h> #include <libxml/xmlreader.h>
#include <libxml/tree.h> #include <libxml/tree.h>
#include "confighelper.h" #include "confighelper.h"
#include "dirhelper.h"
#include "../slog/slog.h" #include "../slog/slog.h"
#include "parameters.h"
int strtogame(const char* game, MonocoqueSettings* ms)
#include "../simulatorapi/simapi/simapi/simmapper.h"
#include <pulse/pulseaudio.h>
int strcicmp(char const *a, char const *b)
{ {
for (;; a++, b++) { slogd("Checking for %s in list of supported simulators.", game);
int d = tolower((unsigned char)*a) - tolower((unsigned char)*b); if (strcmp(game, "ac") == 0)
if (d != 0 || !*a)
return d;
}
}
int strtoeffecttype(const char* effect, DeviceSettings* ds)
{
ds->is_valid = false;
if (strcicmp(effect, "Engine") == 0)
{ {
ds->is_valid = true; slogd("Setting simulator to Assetto Corsa");
ds->effect_type = EFFECT_ENGINERPM; ms->sim_name = SIMULATOR_ASSETTO_CORSA;
} }
if (strcicmp(effect, "Gear") == 0) else if (strcmp(game, "rf2") == 0)
{ {
ds->is_valid = true; slogd("Setting simulator to RFactor 2");
ds->effect_type = EFFECT_GEARSHIFT; ms->sim_name = SIMULATOR_RFACTOR2;
} }
if (strcicmp(effect, "Suspension") == 0) else
if (strcmp(game, "test") == 0)
{ {
ds->is_valid = true; slogd("Setting simulator to Test Data");
ds->effect_type = EFFECT_SUSPENSION; ms->sim_name = SIMULATOR_MONOCOQUE_TEST;
} }
if (strcicmp(effect, "ABS") == 0) else
{ {
ds->is_valid = true; slogi("%s does not appear to be a supported simulator.", game);
slogt("found abas effect set"); return MONOCOQUE_ERROR_INVALID_SIM;
ds->effect_type = EFFECT_ABSBRAKES;
}
if ((strcicmp(effect, "SLIP") == 0) || (strcicmp(effect, "TYRESLIP") == 0) || (strcicmp(effect, "TIRESLIP") == 0))
{
ds->is_valid = true;
slogt("found tyreslip effect set");
ds->effect_type = EFFECT_TYRESLIP;
}
if ((strcicmp(effect, "LOCK") == 0) || (strcicmp(effect, "TYRELOCK") == 0) || (strcicmp(effect, "TIRELOCK") == 0))
{
ds->is_valid = true;
slogt("found tyreslock effect set");
ds->effect_type = EFFECT_TYRELOCK;
}
if (ds->is_valid == false)
{
slogw("effect %s is not a valid effect", effect);
}
ds->is_valid = true;
return MONOCOQUE_ERROR_NONE;
}
int strtodevsubsubtype(const char* device_subsubtype, DeviceSettings* ds)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_UNKNOWN;
bool devfound = false;
if (strcicmp(device_subsubtype, "CammusC5") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_CAMMUSC5;
devfound = true;
}
if (strcicmp(device_subsubtype, "CammusC12") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_CAMMUSC12;
devfound = true;
}
if (strcicmp(device_subsubtype, "MozaNew") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZA_NEW;
devfound = true;
}
if (strcicmp(device_subsubtype, "MozaR5") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZAR5;
devfound = true;
}
if (strcicmp(device_subsubtype, "LogitechG29") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_LOGITECH_G29;
devfound = true;
}
if (strcicmp(device_subsubtype, "MozaR8") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZAR5;
devfound = true;
}
if (strcicmp(device_subsubtype, "MozaR3") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZAR5;
devfound = true;
}
if (strcicmp(device_subsubtype, "MozaKSProWheel") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZA_KS_PRO_WHEEL;
devfound = true;
}
if (strcicmp(device_subsubtype, "CSLELITEV3PEDALS") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_CSLELITEV3PEDALS;
devfound = true;
}
if (strcicmp(device_subsubtype, "SIMAGICP1000PEDALS") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_SIMAGICP1000PEDALS;
devfound = true;
}
if (strcicmp(device_subsubtype, "SIMAGICGTNEO") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_SIMAGICGTNEO;
devfound = true;
}
if(devfound == false)
{
slogw("%s does not appear to be a valid device sub sub type, but attempting to continue with other devices", device_subsubtype);
return MONOCOQUE_ERROR_INVALID_DEV;
} }
return MONOCOQUE_ERROR_NONE; return MONOCOQUE_ERROR_NONE;
} }
int strtodevsubtype(const char* device_subtype, DeviceSettings* ds, int simdev) int strtodev(const char* device_type, DeviceSettings* ds)
{ {
ds->is_valid = false; ds->is_valid = false;
ds->dev_subtype = SIMDEVTYPE_UNKNOWN; if (strcmp(device_type, "USB") == 0)
switch (simdev) {
case SIMDEV_USB:
if (strcicmp(device_subtype, "Tachometer") == 0)
{
ds->dev_subtype = SIMDEVTYPE_TACHOMETER;
break;
}
if (strcicmp(device_subtype, "Wheel") == 0 || strcicmp(device_subtype, "UsbWheel") == 0)
{
ds->dev_subtype = SIMDEVTYPE_USBWHEEL;
break;
}
if (strcicmp(device_subtype, "UsbHaptic") == 0 || strcicmp(device_subtype, "Haptic") == 0)
{
ds->dev_subtype = SIMDEVTYPE_USBHAPTIC;
break;
}
case SIMDEV_SERIAL:
if (strcicmp(device_subtype, "ShiftLights") == 0)
{
ds->dev_subtype = SIMDEVTYPE_SHIFTLIGHTS;
break;
}
if (strcicmp(device_subtype, "Simleds") == 0)
{
ds->dev_subtype = SIMDEVTYPE_SIMLED;
break;
}
if (strcicmp(device_subtype, "ArduinoCustom") == 0 || strcicmp(device_subtype, "Custom") == 0)
{
ds->dev_subtype = SIMDEVTYPE_ARDUINOCUSTOM;
break;
}
if (strcicmp(device_subtype, "SimWind") == 0)
{
ds->dev_subtype = SIMDEVTYPE_SIMWIND;
break;
}
if (strcicmp(device_subtype, "SerialHaptic") == 0 || strcicmp(device_subtype, "Haptic") == 0)
{
slogt("found serial haptic device settings");
ds->dev_subtype = SIMDEVTYPE_SERIALHAPTIC;
break;
}
if (strcicmp(device_subtype, "Wheel") == 0)
{
ds->dev_subtype = SIMDEVTYPE_SERIALWHEEL;
break;
}
case SIMDEV_SOUND:
ds->is_valid = true;
break;
default:
ds->is_valid = false;
slogw("%s does not appear to be a valid device sub type, but attempting to continue with other devices", device_subtype);
return MONOCOQUE_ERROR_INVALID_DEV;
}
ds->is_valid = true;
return MONOCOQUE_ERROR_NONE;
}
int strtodev(const char* device_type, const char* device_subtype, DeviceSettings* ds)
{
ds->is_valid = false;
if (strcicmp(device_type, "USB") == 0)
{ {
ds->dev_type = SIMDEV_USB; ds->dev_type = SIMDEV_USB;
strtodevsubtype(device_subtype, ds, SIMDEV_USB);
} }
else else
if (strcicmp(device_type, "Sound") == 0) if (strcmp(device_type, "Sound") == 0)
{ {
ds->dev_type = SIMDEV_SOUND; ds->dev_type = SIMDEV_SOUND;
strtodevsubtype(device_subtype, ds, SIMDEV_SOUND);
} }
else else
if (strcicmp(device_type, "Serial") == 0) if (strcmp(device_type, "Serial") == 0)
{ {
ds->dev_type = SIMDEV_SERIAL; ds->dev_type = SIMDEV_SERIAL;
strtodevsubtype(device_subtype, ds, SIMDEV_SERIAL);
} }
else else
{ {
@ -243,252 +65,34 @@ int strtodev(const char* device_type, const char* device_subtype, DeviceSettings
return MONOCOQUE_ERROR_NONE; return MONOCOQUE_ERROR_NONE;
} }
int getsimfromconfig(config_setting_t* c) int strtodevtype(const char* device_subtype, DeviceSettings* ds)
{ {
int sim = 0; ds->is_valid = false;
const char* simstr = NULL; if (strcmp(device_subtype, "Tachometer") == 0)
int found = config_setting_lookup_string(c, "sim", &simstr);
if(found == 0)
{ {
int found = config_setting_lookup_int(c, "sim", &sim); ds->dev_subtype = SIMDEVTYPE_TACHOMETER;
}
else
if (strcmp(device_subtype, "ShiftLights") == 0)
{
ds->dev_subtype = SIMDEVTYPE_SHIFTLIGHTS;
}
else
if (strcmp(device_subtype, "Shaker") == 0)
{
ds->dev_subtype = SIMDEVTYPE_SHAKER;
} }
else else
{ {
sim = simapi_strtogame(simstr); ds->is_valid = false;
slogi("%s does not appear to be a valid device sub type, but attempting to continue with other devices", device_subtype);
return MONOCOQUE_ERROR_INVALID_DEV;
} }
return sim; ds->is_valid = true;
return MONOCOQUE_ERROR_NONE;
} }
int getNumberOfConfigs(const char* config_file_str) int loadtachconfig(const char* config_file, DeviceSettings* ds)
{
config_t cfg;
config_init(&cfg);
if (!config_read_file(&cfg, config_file_str))
{
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
return -1;
}
config_setting_t* config = NULL;
config_setting_t* config_widgets = NULL;
config = config_lookup(&cfg, "configs");
int configs = config_setting_length(config);
config_destroy(&cfg);
return configs;
}
int getconfigtouse2(const char* config_file_str, char* car, int sim)
{
slogt("inside first pass");
config_t cfg;
config_init(&cfg);
if (!config_read_file(&cfg, config_file_str))
{
sloge("config read error on pass 1");
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
return -1;
}
slogt("config validates");
config_setting_t* config = NULL;
config_setting_t* config_widgets = NULL;
config = config_lookup(&cfg, "configs");
int configs = config_setting_length(config);
const char* temp;
config_setting_t* config_config = NULL;
int j = 0;
if ( configs == 1 )
{
config_destroy(&cfg);
return -1;
}
int confignum = -1;
slogt("Multiple configs found");
for (j = 0; j < configs; j++)
{
config_config = config_setting_get_elem(config, j);
int found = 0;
int csim = 0;
slogt("sim is %i", sim);
csim = getsimfromconfig(config_config);
if (csim != sim)
{
slogt("rejected config %i", j);
continue;
}
slogt("checking if car is matched %i", j);
temp = NULL;
found = config_setting_lookup_string(config_config, "car", &temp);
slogt("config car is %s found is %i", temp, found);
if(temp != NULL && found > 0 && car > 0 && car != NULL)
{
slogt("checking against sim car of %s", car);
if(strcicmp(temp, car) == 0)
{
confignum = j;
}
}
if(confignum>=0)
{
break;
}
}
config_destroy(&cfg);
return confignum;
}
int getconfigtouse1(const char* config_file_str, char* car, int sim)
{
config_t cfg;
config_init(&cfg);
if (!config_read_file(&cfg, config_file_str))
{
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
return -1;
}
config_setting_t* config = NULL;
config_setting_t* config_widgets = NULL;
config = config_lookup(&cfg, "configs");
int configs = config_setting_length(config);
const char* temp;
config_setting_t* config_config = NULL;
int j = 0;
if ( configs == 1 )
{
config_destroy(&cfg);
return -1;
}
int confignum = -1;
slogt("Multiple configs found");
for (j = 0; j < configs; j++)
{
config_config = config_setting_get_elem(config, j);
int found = 0;
int csim = 0;
slogt("sim is %i", sim);
csim = getsimfromconfig(config_config);
if (csim != sim)
{
slogt("rejected config %i", j);
continue;
}
slogt("checking if car is matched %i", j);
temp = NULL;
found = config_setting_lookup_string(config_config, "car", &temp);
slogt("config car is %s found is %i", temp, found);
if(temp != NULL && found > 0 && car > 0 && car != NULL)
{
slogt("checking against sim car of %s", car);
if(strcicmp(temp, car) == 0)
{
confignum = j;
}
if(strcicmp("default", temp) == 0)
{
slogt("matched default car");
confignum = j;
}
}
else
{
slogt("assuming default car");
confignum = j;
}
slogt("bomb");
if(confignum>=0)
{
break;
}
}
config_destroy(&cfg);
return confignum;
}
int getconfigtouse(const char* config_file_str, char* car, int sim)
{
config_t cfg;
config_init(&cfg);
if (!config_read_file(&cfg, config_file_str))
{
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
return -1;
}
config_setting_t* config = NULL;
config_setting_t* config_widgets = NULL;
config = config_lookup(&cfg, "configs");
int configs = config_setting_length(config);
const char* temp;
config_setting_t* config_config = NULL;
int j = 0;
if ( configs == 1 )
{
config_destroy(&cfg);
return 0;
}
int confignum = 0;
slogt("Multiple configs found");
for (j = 0; j < configs; j++)
{
config_config = config_setting_get_elem(config, j);
int found = 0;
int csim = 0;
slogt("sim is %i", sim);
csim = getsimfromconfig(config_config);
if (csim != sim && csim != 0)
{
slogt("rejected config %i", j);
continue;
}
slogt("checking if car is matched %i", j);
temp = NULL;
found = config_setting_lookup_string(config_config, "car", &temp);
slogt("config car is %s found is %i", temp, found);
if(temp != NULL && found > 0 && car > 0 && car != NULL)
{
slogt("checking against sim car of %s", car);
if(strcicmp(temp, car) == 0)
{
confignum = j;
}
if(strcicmp("default", temp) == 0)
{
slogt("matched default car");
confignum = j;
}
}
else
{
slogt("assuming default car");
confignum = j;
}
slogt("bomb");
if(confignum<configs-1)
{
break;
}
}
config_destroy(&cfg);
return confignum;
}
int loadtachconfig(char* config_file, DeviceSettings* ds)
{ {
@ -527,7 +131,7 @@ int loadtachconfig(char* config_file, DeviceSettings* ds)
{ {
slogt("Xml Element name %s", cursubsubnode->name); slogt("Xml Element name %s", cursubsubnode->name);
} }
if (strcicmp(cursubsubnode->name, "SettingsItem") == 0) if (strcmp(cursubsubnode->name, "SettingsItem") == 0)
{ {
arraysize++; arraysize++;
} }
@ -549,13 +153,13 @@ int loadtachconfig(char* config_file, DeviceSettings* ds)
{ {
for (cursubsubsubnode = cursubsubnode->children; cursubsubsubnode; cursubsubsubnode = cursubsubsubnode->next) for (cursubsubsubnode = cursubsubnode->children; cursubsubsubnode; cursubsubsubnode = cursubsubsubnode->next)
{ {
if (strcicmp(cursubsubsubnode->name, "Value") == 0) if (strcmp(cursubsubsubnode->name, "Value") == 0)
{ {
xmlChar* a = xmlNodeGetContent(cursubsubsubnode); xmlChar* a = xmlNodeGetContent(cursubsubsubnode);
rpms_array[i] = strtol((char*) a, &buf, 10); rpms_array[i] = strtol((char*) a, &buf, 10);
xmlFree(a); xmlFree(a);
} }
if (strcicmp(cursubsubsubnode->name, "TimeValue") == 0) if (strcmp(cursubsubsubnode->name, "TimeValue") == 0)
{ {
xmlChar* a = xmlNodeGetContent(cursubsubsubnode); xmlChar* a = xmlNodeGetContent(cursubsubsubnode);
pulses_array[i] = strtol((char*) a, &buf, 10); pulses_array[i] = strtol((char*) a, &buf, 10);
@ -581,126 +185,40 @@ int loadtachconfig(char* config_file, DeviceSettings* ds)
return 0; return 0;
} }
int gettyre(config_setting_t* device_settings, DeviceSettings* ds) { int loadconfig(const char* config_file, DeviceSettings* ds)
const char* temp;
int found = config_setting_lookup_string(device_settings, "tyre", &temp);
ds->tyre = ALLFOUR;
if (strcicmp(temp, "FRONTS") == 0)
{
ds->tyre = FRONTS;
}
if (strcicmp(temp, "REARS") == 0)
{
ds->tyre = REARS;
}
if (strcicmp(temp, "FRONTLEFT") == 0)
{
ds->tyre = FRONTLEFT;
}
if (strcicmp(temp, "FRONTRIGHT") == 0)
{
ds->tyre = FRONTRIGHT;
}
if (strcicmp(temp, "REARLEFT") == 0)
{
ds->tyre = REARLEFT;
}
if (strcicmp(temp, "REARRIGHT") == 0)
{
ds->tyre = REARRIGHT;
}
}
static int load_device_specific_config(const char* config_file, DeviceSettings* ds)
{ {
ds->has_config = false;
if(config_file == NULL)
{
slogt("config set to none");
}
else
{
ds->has_config = true;
if(strcicmp(config_file, "none") == 0)
{
ds->has_config = false;
ds->specific_config_file = NULL;
}
else
{
ds->specific_config_file = strdup(config_file);
ds->specific_config_file = expand_tilde(ds->specific_config_file);
slogt("will try to load config file at %s", ds->specific_config_file);
}
}
// in the case of the revburner tachometer, we can parse once and store
if (ds->dev_subtype == SIMDEVTYPE_TACHOMETER) if (ds->dev_subtype == SIMDEVTYPE_TACHOMETER)
{ {
if(ds->has_config == false) return loadtachconfig(config_file, ds);
{
slogw("Tachometer must have a device specific config file!");
return 1;
}
loadtachconfig(ds->specific_config_file, ds);
} }
return 0; return 0;
} }
int configcheck(const char* config_file_str, int confignum, int* devices)
{
slogt("ui config check");
config_t cfg;
config_init(&cfg);
if (!config_read_file(&cfg, config_file_str))
{
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
}
config_setting_t* config = NULL;
config = config_lookup(&cfg, "configs");
config_setting_t* selectedconfig = config_setting_get_elem(config, confignum);
slogt("selected num %i", confignum);
config_setting_t* config_devices = NULL;
config_devices = config_setting_lookup(selectedconfig, "devices");
*devices = config_setting_length(config_devices);
config_destroy(&cfg);
return 0;
//return cfg;
}
int devsetup(const char* device_type, const char* device_subtype, const char* config_file, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings) int devsetup(const char* device_type, const char* device_subtype, const char* config_file, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings)
{ {
int error = MONOCOQUE_ERROR_NONE; int error = MONOCOQUE_ERROR_NONE;
//slogt("Called device setup with %s %s %s", device_type, device_subtype, config_file); slogi("Called device setup with %s %s %s", device_type, device_subtype, config_file);
ds->dev_type = SIMDEV_UNKNOWN; ds->dev_type = SIMDEV_UNKNOWN;
ds->dev_subtype = SIMDEVTYPE_UNKNOWN;
error = strtodev(device_type, device_subtype, ds); error = strtodev(device_type, ds);
if (error != MONOCOQUE_ERROR_NONE) if (error != MONOCOQUE_ERROR_NONE)
{ {
return error; return error;
} }
error = strtodevtype(device_subtype, ds);
if (ms->program_action == A_PLAY || ms->program_action == A_TEST) if (error != MONOCOQUE_ERROR_NONE)
{ {
error = load_device_specific_config(config_file, ds); return error;
}
if (ms->program_action == A_PLAY)
{
error = loadconfig(config_file, ds);
} }
if (error != MONOCOQUE_ERROR_NONE) if (error != MONOCOQUE_ERROR_NONE)
{ {
return error; return error;
} }
ds->fps = 60;
config_setting_lookup_int(device_settings, "fps", &ds->fps);
if (ds->dev_subtype == SIMDEVTYPE_TACHOMETER) if (ds->dev_subtype == SIMDEVTYPE_TACHOMETER)
{ {
if (device_settings != NULL) if (device_settings != NULL)
@ -714,285 +232,11 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
slogi("Tachometer granularity set to %i", ds->tachsettings.granularity); slogi("Tachometer granularity set to %i", ds->tachsettings.granularity);
} }
ds->tachsettings.use_pulses = true; ds->tachsettings.use_pulses = true;
if (ms->program_action == A_PLAY || ms->program_action == A_TEST) if (ms->program_action == A_PLAY)
{ {
ds->tachsettings.use_pulses = false; ds->tachsettings.use_pulses = false;
} }
} }
if (ds->dev_subtype == SIMDEVTYPE_USBHAPTIC || ds->dev_subtype == SIMDEVTYPE_USBWHEEL || ds->dev_subtype == SIMDEVTYPE_SERIALWHEEL)
{
// logic for different devices
int b = 0;
const char* temp;
int found = config_setting_lookup_string(device_settings, "subtype", &temp);
if(temp != NULL && found > 0)
{
b = strtodevsubsubtype(temp, ds);
}
}
if (ds->dev_subtype == SIMDEVTYPE_USBHAPTIC || ds->dev_type == SIMDEV_SOUND || ds->dev_subtype == SIMDEVTYPE_SERIALHAPTIC)
{
slogt("analysing haptic effect settings");
const char* effect;
config_setting_lookup_string(device_settings, "effect", &effect);
strtoeffecttype(effect, ds);
if (ds->effect_type == EFFECT_TYRESLIP || ds->effect_type == EFFECT_TYRELOCK || ds->effect_type == EFFECT_ABSBRAKES || ds->effect_type == EFFECT_SUSPENSION )
{
gettyre(device_settings, ds);
ds->threshold = 0;
int found = config_setting_lookup_float(device_settings, "threshold", &ds->threshold);
}
if (ds->dev_type == SIMDEV_SOUND)
{
slogi("reading configured sound device settings");
ds->sounddevsettings.frequency = 0;
ds->sounddevsettings.frequencyMax = 0;
ds->sounddevsettings.amplitude = 50;
ds->sounddevsettings.amplitudeMax = 50;
ds->sounddevsettings.volume = 0;
ds->sounddevsettings.pan = 0;
ds->sounddevsettings.channels = 1;
ds->sounddevsettings.duration = 2.0;
ds->sounddevsettings.noise = 0;
if (ds->effect_type == EFFECT_GEARSHIFT)
{
ds->sounddevsettings.duration = .125;
}
if (device_settings != NULL)
{
config_setting_lookup_int(device_settings, "volume", &ds->sounddevsettings.volume);
config_setting_lookup_int(device_settings, "frequency", &ds->sounddevsettings.frequency);
config_setting_lookup_int(device_settings, "frequencyMax", &ds->sounddevsettings.frequencyMax);
config_setting_lookup_int(device_settings, "amplitude", &ds->sounddevsettings.amplitude);
config_setting_lookup_int(device_settings, "amplitudeMax", &ds->sounddevsettings.amplitudeMax);
config_setting_lookup_int(device_settings, "pan", &ds->sounddevsettings.pan);
config_setting_lookup_int(device_settings, "channels", &ds->sounddevsettings.channels);
config_setting_lookup_float(device_settings, "duration", &ds->sounddevsettings.duration);
config_setting_lookup_int(device_settings, "noise", &ds->sounddevsettings.noise);
const char* temp = NULL;
int found = 0;
found = config_setting_lookup_string(device_settings, "devid", &temp);
if (found == CONFIG_FALSE)
{
ds->sounddevsettings.dev = NULL;
}
else
{
if(temp != NULL)
{
ds->sounddevsettings.dev = strdup(temp);
}
}
found = config_setting_lookup_string(device_settings, "modulation", &temp);
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_NONE;
if (found == 0)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_NONE;
slogd("Effect modulation not found, set to none");
}
else
{
if(strcicmp(temp, "FREQUENCY") == 0)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_FREQUENCY;
if(ds->sounddevsettings.frequencyMax == 0 || ds->sounddevsettings.frequencyMax < ds->sounddevsettings.frequency)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_NONE;
slogw("Falling back to no frequency modulation since frequencyMax is either not set or set below target frequency");
}
else
{
slogi("Effect modulation found, set to FREQUENCY");
}
}
else if(strcicmp(temp, "AMPLIFY") == 0)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_AMPLIFY;
slogi("Effect modulation found, set to AMPLIFY");
}
else
{
slogw("%s is not a valid modulation type, falling back to no effect modulation");
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_NONE;
}
}
}
}
}
if (ds->dev_type == SIMDEV_SERIAL)
{
if (device_settings != NULL)
{
const char* temp;
int found = config_setting_lookup_string(device_settings, "subtype", &temp);
if(temp != NULL && found > 0)
{
strtodevsubsubtype(temp, ds);
}
config_setting_lookup_string(device_settings, "devpath", &temp);
ds->serialdevsettings.portdev = strdup(temp);
int motorposition = 8;
config_setting_lookup_int(device_settings, "motors", &motorposition);
ds->serialdevsettings.motorsposition = motorposition;
int numlights = 6;
config_setting_lookup_int(device_settings, "numlights", &numlights);
ds->serialdevsettings.numlights = numlights;
int numleds = 6;
config_setting_lookup_int(device_settings, "numleds", &numleds);
ds->serialdevsettings.numleds = numleds;
int startled = 1;
config_setting_lookup_int(device_settings, "startled", &startled);
ds->serialdevsettings.startled = startled;
int endled = 1;
config_setting_lookup_int(device_settings, "endled", &endled);
ds->serialdevsettings.endled = endled;
int baud = 9600;
config_setting_lookup_int(device_settings, "baud", &baud);
ds->serialdevsettings.baud = baud;
double ampfactor = 1.0;
ds->serialdevsettings.ampfactor = 1.0;
found = config_setting_lookup_float(device_settings, "ampfactor", &ampfactor);
ds->serialdevsettings.ampfactor = ampfactor;
double fanpower = 0.6;
config_setting_lookup_float(device_settings, "fanpower", &fanpower);
ds->serialdevsettings.fanpower = fanpower;
slogt("set port baud rate to %i, ampfactor %f, fanpower %f", baud, ampfactor, fanpower);
}
}
return error; return error;
} }
int uiloadconfig(const char* config_file_str, int confignum, int configureddevices, MonocoqueSettings* ms, DeviceSettings* ds)
{
int numdevices = 0;
config_t cfg;
config_init(&cfg);
if (!config_read_file(&cfg, config_file_str))
{
fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
}
else
{
slogi("Parsing config file");
config_setting_t* config = NULL;
config = config_lookup(&cfg, "configs");
config_setting_t* selectedconfig = config_setting_get_elem(config, confignum);
config_setting_t* config_devices = NULL;
config_devices = config_setting_lookup(selectedconfig, "devices");
int i = 0;
int error = MONOCOQUE_ERROR_NONE;
while (i<configureddevices)
{
error = MONOCOQUE_ERROR_NONE;
DeviceSettings settings;
config_setting_t* config_device = config_setting_get_elem(config_devices, i);
const char* device_type = NULL;
const char* device_subtype = NULL;
const char* device_config_file = NULL;
int found = 0;
config_setting_lookup_string(config_device, "device", &device_type);
config_setting_lookup_string(config_device, "type", &device_subtype);
found = config_setting_lookup_string(config_device, "config", &device_config_file);
slogt("device type: %s", device_type);
slogt("device sub type: %s", device_subtype);
if(found == CONFIG_FALSE)
{
device_config_file = NULL;
}
else
{
slogt("device config file: %s", device_config_file);
}
if (error == MONOCOQUE_ERROR_NONE)
{
error = devsetup(device_type, device_subtype, device_config_file, ms, &settings, config_device);
}
if (error == MONOCOQUE_ERROR_NONE)
{
numdevices++;
}
ds[i] = settings;
i++;
}
}
config_destroy(&cfg);
return numdevices;
}
int settingsfree(DeviceSettings ds)
{
if (ds.dev_type == SIMDEV_SERIAL)
{
if (ds.serialdevsettings.portdev != NULL)
{
free(ds.serialdevsettings.portdev);
}
}
if (ds.dev_type == SIMDEV_SOUND)
{
if (ds.sounddevsettings.dev != NULL)
{
free(ds.sounddevsettings.dev);
}
}
if(ds.has_config && ds.specific_config_file != NULL)
{
free(ds.specific_config_file);
}
return 0;
}
int monocoquesettingsfree(MonocoqueSettings* ms)
{
if(ms->tyre_diameter_config != NULL)
{
free(ms->tyre_diameter_config);
}
if(ms->config_str != NULL)
{
free(ms->config_str);
}
if(ms->log_filename_str != NULL)
{
free(ms->log_filename_str);
}
if(ms->log_dirname_str != NULL)
{
free(ms->log_dirname_str);
}
}

View File

@ -1,7 +1,6 @@
#ifndef _CONFIGHELPER_H #ifndef _CONFIGHELPER_H
#define _CONFIGHELPER_H #define _CONFIGHELPER_H
#include <pulse/channelmap.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
@ -9,8 +8,6 @@
#include "parameters.h" #include "parameters.h"
#include "../devices/sounddevice.h"
typedef enum typedef enum
{ {
SIMDEV_UNKNOWN = 0, SIMDEV_UNKNOWN = 0,
@ -24,32 +21,19 @@ typedef enum
{ {
SIMDEVTYPE_UNKNOWN = 0, SIMDEVTYPE_UNKNOWN = 0,
SIMDEVTYPE_TACHOMETER = 1, SIMDEVTYPE_TACHOMETER = 1,
SIMDEVTYPE_USBHAPTIC = 2, SIMDEVTYPE_SHAKER = 2,
SIMDEVTYPE_SHIFTLIGHTS = 3, SIMDEVTYPE_SHIFTLIGHTS = 3
SIMDEVTYPE_SIMWIND = 4,
SIMDEVTYPE_SERIALHAPTIC = 5,
SIMDEVTYPE_USBWHEEL = 6,
SIMDEVTYPE_SERIALWHEEL = 7,
SIMDEVTYPE_SIMLED = 8,
SIMDEVTYPE_ARDUINOCUSTOM = 9
} }
DeviceSubType; DeviceSubType;
typedef enum typedef enum
{ {
SIMDEVSUBTYPE_UNKNOWN = 0, SIMULATOR_MONOCOQUE_TEST = 0,
SIMDEVSUBTYPE_CAMMUSC5 = 1, SIMULATOR_ASSETTO_CORSA = 1,
SIMDEVSUBTYPE_CAMMUSC12 = 2, SIMULATOR_RFACTOR = 2,
SIMDEVSUBTYPE_MOZAR5 = 3, SIMULATOR_RFACTOR2 = 3
SIMDEVSUBTYPE_CSLELITEV3PEDALS = 4,
SIMDEVSUBTYPE_SIMAGICP1000PEDALS = 5,
SIMDEVSUBTYPE_SIMAGICGTNEO = 6,
SIMDEVSUBTYPE_MOZA_NEW = 7,
SIMDEVSUBTYPE_LOGITECH_G29 = 8,
SIMDEVSUBTYPE_MOZA_KS_PRO_WHEEL = 9
} }
DeviceSubSubType; Simulator;
typedef enum typedef enum
{ {
@ -62,52 +46,6 @@ typedef enum
} }
SimulatorUpdate; SimulatorUpdate;
typedef enum
{
EFFECT_ENGINERPM = 0,
EFFECT_GEARSHIFT = 1,
EFFECT_ABSBRAKES = 2,
EFFECT_TYRESLIP = 3,
EFFECT_TYRELOCK = 4,
EFFECT_SUSPENSION = 5
}
VibrationEffectType;
typedef enum
{
MOTOR_1 = 0,
MOTOR_2 = 1,
MOTOR_3 = 2,
MOTOR_4 = 3,
MOTOR_1_4 = 4,
MOTOR_2_4 = 5,
MOTOR_3_4 = 6,
MOTOR_1_2 = 7,
MOTOR_1_3 = 8,
MOTOR_2_3 = 9,
MOTOR_1_2_3_4 = 10,
MOTOR_1_2_3 = 11,
MOTOR_2_3_4 = 12,
MOTOR_1_2_4 = 13,
MOTOR_1_3_4 = 14
}
MotorPosition;
typedef enum
{
MONOCOQUE_GEAR_REVERSE = 0,
MONOCOQUE_GEAR_NEUTRAL = 1,
MONOCOQUE_GEAR_ONE = 2,
MONOCOQUE_GEAR_TWO = 3,
MONOCOQUE_GEAR_THREE = 4,
MONOCOQUE_GEAR_FOUR = 5,
MONOCOQUE_GEAR_FIVE = 6,
MONOCOQUE_GEAR_SIX = 7,
MONOCOQUE_GEAR_SEVEN = 8,
MONOCOQUE_GEAR_EIGHT = 9,
}
MonocoqueGear;
typedef enum typedef enum
{ {
MONOCOQUE_ERROR_NONE = 0, MONOCOQUE_ERROR_NONE = 0,
@ -115,37 +53,14 @@ typedef enum
MONOCOQUE_ERROR_INVALID_SIM = 2, MONOCOQUE_ERROR_INVALID_SIM = 2,
MONOCOQUE_ERROR_INVALID_DEV = 3, MONOCOQUE_ERROR_INVALID_DEV = 3,
MONOCOQUE_ERROR_NODATA = 4, MONOCOQUE_ERROR_NODATA = 4,
MONOCOQUE_ERROR_UNKNOWN_DEV = 5, MONOCOQUE_ERROR_UNKNOWN_DEV = 5
MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE = 6,
} }
MonocoqueError; MonocoqueError;
typedef enum
{
FRONTLEFT = 0,
FRONTRIGHT = 1,
REARLEFT = 2,
REARRIGHT = 3,
FRONTS = 4,
REARS = 5,
ALLFOUR = 6
}
MonocoqueTyreIdentifier;
typedef struct typedef struct
{ {
ProgramAction program_action; ProgramAction program_action;
SimulatorAPI sim_name; Simulator sim_name;
int configcheck;
int useconfig;
int verbosity_count;
int fps;
bool force_udp_mode;
bool disable_audio;
char* tyre_diameter_config;
char* config_str;
char* log_filename_str;
char* log_dirname_str;
} }
MonocoqueSettings; MonocoqueSettings;
@ -159,66 +74,12 @@ typedef struct
} }
TachometerSettings; TachometerSettings;
typedef struct
{
char* portdev;
MotorPosition motorsposition;
int numlights;
int numleds;
int startled;
int endled;
float ampfactor;
float fanpower;
int baud;
}
SerialDeviceSettings;
typedef struct
{
uint32_t frequency;
uint32_t volume;
int lowbound_frequency;
int upperbound_frequency;
uint32_t frequencyMax;
uint32_t amplitudeMax;
uint32_t amplitude;
int pan;
int channels;
double duration;
char* dev;
SoundEffectModulationType modulation;
int noise;
}
SoundDeviceSettings;
typedef struct
{
int value0;
int value1;
char* dev;
}
USBDeviceSettings;
typedef struct typedef struct
{ {
bool is_valid; bool is_valid;
int fps;
DeviceType dev_type; DeviceType dev_type;
DeviceSubType dev_subtype; DeviceSubType dev_subtype;
DeviceSubSubType dev_subsubtype;
// to get really fancy move the effect information to it's own structure that would be a member of
// any device settings member structure that can carry an effect
VibrationEffectType effect_type;
MonocoqueTyreIdentifier tyre;
double threshold;
bool has_config;
char* specific_config_file;
// union?
TachometerSettings tachsettings; TachometerSettings tachsettings;
SerialDeviceSettings serialdevsettings;
SoundDeviceSettings sounddevsettings;
USBDeviceSettings usbdevsettings;
} }
DeviceSettings; DeviceSettings;
@ -226,19 +87,4 @@ int strtogame(const char* game, MonocoqueSettings* ms);
int devsetup(const char* device_type, const char* device_subtype, const char* config_files, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings); int devsetup(const char* device_type, const char* device_subtype, const char* config_files, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings);
int settingsfree(DeviceSettings ds);
int monocoquesettingsfree(MonocoqueSettings* ms);
int strcicmp(char const *a, char const *b);
int getconfigtouse2(const char* config_file_str, char* car, int sim);
int getconfigtouse1(const char* config_file_str, char* car, int sim);
int getconfigtouse(const char* config_file_str, char* car, int sim);
int configcheck(const char* config_file_str, int confignum, int* devices);
int uiloadconfig(const char* config_file_str, int confignum, int configureddevices, MonocoqueSettings* ms, DeviceSettings* ds);
int getNumberOfConfigs(const char* config_file_str);
#endif #endif

View File

@ -13,47 +13,24 @@
#include <string.h> #include <string.h>
void create_dir(char* dir)
{
struct stat st = {0};
if (stat(dir, &st) == -1)
{
mkdir(dir, 0700);
}
}
void create_xdg_dir(const char* dir)
{
struct stat st = {0};
if (stat(dir, &st) == -1)
{
mkdir(dir, 0700);
}
}
char* create_user_dir(char* home_dir_str, const char* dirtype, const char* programname)
{
// +3 for slashes
size_t ss = (4 + strlen(home_dir_str) + strlen(dirtype) + strlen(programname));
char* config_dir_str = malloc(ss);
snprintf (config_dir_str, ss, "%s/%s/%s/", home_dir_str, dirtype, programname);
create_dir(config_dir_str);
return config_dir_str;
}
char* gethome() char* gethome()
{ {
char* homedir = getenv("HOME"); char* homedir = getenv("HOME");
return homedir; return homedir;
if (homedir != NULL)
{
printf("Home dir in enviroment");
printf("%s\n", homedir);
}
uid_t uid = getuid(); uid_t uid = getuid();
struct passwd* pw = getpwuid(uid); struct passwd* pw = getpwuid(uid);
if (pw == NULL) if (pw == NULL)
{ {
return NULL; printf("Failed\n");
exit(EXIT_FAILURE);
} }
return pw->pw_dir; return pw->pw_dir;
@ -198,94 +175,28 @@ void restrict_folders_to_cache(char* path, int cachesize)
} }
bool does_directory_exist(char* path) bool does_directory_exist(char* path, char* dirname)
{ {
DIR* dir = opendir(path); struct dirent* de;
if (dir) DIR* dr = opendir(path);
if (dr == NULL)
{ {
// Directory exists printf("Could not open current directory");
closedir(dir);
return true;
}
else
{
// Directory does not exist or cannot be opened
return false; return false;
} }
// Refer http://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
bool answer = false;
while ((de = readdir(dr)) != NULL)
{
if (strcmp(dirname,de->d_name) == 0)
{
answer = true;
}
}
closedir(dr);
return answer;
} }
bool does_file_exist(const char* file)
{
if (file == NULL)
{
return false;
}
#if defined(OS_WIN)
#if defined(WIN_API)
// if you want the WinAPI, versus CRT
if (strnlen(file, MAX_PATH+1) > MAX_PATH)
{
// ... throw error here or ...
return false;
}
DWORD res = GetFileAttributesA(file);
return (res != INVALID_FILE_ATTRIBUTES &&
!(res& FILE_ATTRIBUTE_DIRECTORY));
#else
// Use Win CRT
struct stat fi;
if (_stat(file, &fi) == 0)
{
#if defined(S_ISSOCK)
// sockets come back as a 'file' on some systems
// so make sure it's not a socket or directory
// (in other words, make sure it's an actual file)
return !(S_ISDIR(fi.st_mode)) &&
!(S_ISSOCK(fi.st_mode));
#else
return !(S_ISDIR(fi.st_mode));
#endif
}
return false;
#endif
#else
struct stat fi;
if (stat(file, &fi) == 0)
{
#if defined(S_ISSOCK)
return !(S_ISDIR(fi.st_mode)) &&
!(S_ISSOCK(fi.st_mode));
#else
return !(S_ISDIR(fi.st_mode));
#endif
}
return false;
#endif
}
char* expand_tilde(char* path) {
if (path[0] != '~') {
return path;
}
const char* home_dir = getenv("HOME");
if (!home_dir) {
return path;
}
size_t expanded_size = strlen(home_dir) + strlen(path);
char* expanded_path = (char*)malloc(expanded_size + 1);
if (!expanded_path) {
return path;
}
strcpy(expanded_path, home_dir);
strcat(expanded_path, path + 1);
if (path) {
free(path);
}
return expanded_path;
}

View File

@ -3,15 +3,10 @@
#include <stdbool.h> #include <stdbool.h>
void create_dir(char* dir);
void create_xdg_dir(const char* dir);
char* create_user_dir(char* home_dir_str, const char* dirtype, const char* programpath);
char* gethome(); char* gethome();
char* str2md5(const char* str, int length); char* str2md5(const char* str, int length);
bool does_directory_exist(char* path); bool does_directory_exist(char* path, char* dirname);
void restrict_folders_to_cache(char* path, int cachesize); void restrict_folders_to_cache(char* path, int cachesize);
void delete_dir(char* path); void delete_dir(char* path);
bool does_file_exist(const char* file);
char* expand_tilde(char* path);
#endif #endif

View File

@ -2,39 +2,12 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <libgen.h>
#include <libconfig.h> #include <libconfig.h>
#include <argtable2.h> #include <argtable2.h>
#include <regex.h> #include <regex.h>
int freeparams(Parameters* p)
{
if(p->config_dirpath != NULL)
{
free(p->config_dirpath);
}
if(p->config_filepath != NULL)
{
free(p->config_filepath);
}
if(p->log_filename_str != NULL)
{
free(p->log_filename_str);
}
if(p->log_fullfilename_str != NULL)
{
free(p->log_fullfilename_str);
}
if(p->log_dirname_str != NULL)
{
free(p->log_dirname_str);
}
return 0;
}
ConfigError getParameters(int argc, char** argv, Parameters* p) ConfigError getParameters(int argc, char** argv, Parameters* p)
{ {
@ -44,33 +17,19 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
p->program_action = 0; p->program_action = 0;
p->max_revs = 0; p->max_revs = 0;
p->verbosity_count = 0; p->verbosity_count = 0;
p->fps = 60;
p->disable_audio = false;
p->udp = false;
p->user_specified_config_file = false;
p->user_specified_log_file = false;
p->user_specified_config_dir = false;
// setup argument handling structures // setup argument handling structures
const char* progname = "monocoque"; const char* progname = "monocoque";
struct arg_lit* arg_verbosity1 = arg_litn("v","verbose", 0, 2, "increase logging verbosity"); struct arg_lit* arg_verbosity1 = arg_litn("v","verbose", 0, 2, "increase logging verbosity");
struct arg_lit* arg_verbosity2 = arg_litn("v","verbose", 0, 2, "increase logging verbosity"); struct arg_lit* arg_verbosity2 = arg_litn("v","verbose", 0, 2, "increase logging verbosity");
struct arg_lit* arg_verbosity3 = arg_litn("v","verbose", 0, 2, "increase logging verbosity");
struct arg_rex* cmd1 = arg_rex1(NULL, NULL, "play", NULL, REG_ICASE, NULL); struct arg_rex* cmd1 = arg_rex1(NULL, NULL, "play", NULL, REG_ICASE, NULL);
struct arg_lit* arg_udp = arg_lit0("d", "udp", "force udp mode for sims which support it"); struct arg_str* arg_sim = arg_strn("s", "sim", "<gamename>", 0, 1, NULL);
struct arg_lit* arg_audio1 = arg_lit0("a", "disable_audio", "force disable of audio devices"); struct arg_lit* help = arg_litn(NULL,"help", 0, 1, "print this help and exit");
struct arg_file* arg_conf = arg_file0("c", "uiconf", "<config_file>", NULL);
struct arg_file* arg_log = arg_filen("l", "log", "<log_file>", 0, 1, NULL);
struct arg_str* arg_confdir = arg_str1(NULL, NULL, "configdir", "<config_dir>");
struct arg_int* arg_fps = arg_int0("f", "fps", "fps", "main data refresh rate");
struct arg_lit* help1 = arg_litn(NULL,"help", 0, 1, "print this help and exit");
struct arg_lit* vers = arg_litn(NULL,"version", 0, 1, "print version information and exit"); struct arg_lit* vers = arg_litn(NULL,"version", 0, 1, "print version information and exit");
struct arg_end* end1 = arg_end(20); struct arg_end* end1 = arg_end(20);
void* argtable1[] = {cmd1,arg_log,arg_conf,arg_fps,arg_udp,arg_audio1,arg_verbosity1,help1,vers,end1}; void* argtable1[] = {cmd1,arg_sim,arg_verbosity1,help,vers,end1};
int nerrors1; int nerrors1;
struct arg_rex* cmd2a = arg_rex1(NULL, NULL, "config", NULL, REG_ICASE, NULL); struct arg_rex* cmd2a = arg_rex1(NULL, NULL, "config", NULL, REG_ICASE, NULL);
@ -84,14 +43,6 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
void* argtable2[] = {cmd2a,cmd2b,arg_max_revs,arg_granularity,arg_save,arg_verbosity2,help2,vers2,end2}; void* argtable2[] = {cmd2a,cmd2b,arg_max_revs,arg_granularity,arg_save,arg_verbosity2,help2,vers2,end2};
int nerrors2; int nerrors2;
struct arg_rex* cmd3 = arg_rex1(NULL, NULL, "test", NULL, REG_ICASE, NULL);
struct arg_lit* arg_audio2 = arg_lit0("a", "disable_audio", "force disable of audio devices");
struct arg_lit* help3 = arg_litn(NULL,"help", 0, 1, "print this help and exit");
struct arg_lit* vers3 = arg_litn(NULL,"version", 0, 1, "print version information and exit");
struct arg_end* end3 = arg_end(20);
void* argtable3[] = {cmd3,arg_verbosity3,arg_audio2,help3,vers3,end3};
int nerrors3;
struct arg_lit* help0 = arg_lit0(NULL,"help", "print this help and exit"); struct arg_lit* help0 = arg_lit0(NULL,"help", "print this help and exit");
struct arg_lit* version0 = arg_lit0(NULL,"version", "print version information and exit"); struct arg_lit* version0 = arg_lit0(NULL,"version", "print version information and exit");
struct arg_end* end0 = arg_end(20); struct arg_end* end0 = arg_end(20);
@ -114,64 +65,22 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
goto cleanup; goto cleanup;
} }
arg_granularity->ival[0] = 1;
nerrors0 = arg_parse(argc,argv,argtable0); nerrors0 = arg_parse(argc,argv,argtable0);
nerrors1 = arg_parse(argc,argv,argtable1); nerrors1 = arg_parse(argc,argv,argtable1);
nerrors2 = arg_parse(argc,argv,argtable2); nerrors2 = arg_parse(argc,argv,argtable2);
nerrors3 = arg_parse(argc,argv,argtable3);
if (nerrors1==0) if (nerrors1==0)
{ {
if (help1->count > 0)
{
arg_print_errors(stdout,end1,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable1,"\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
}
p->program_action = A_PLAY; p->program_action = A_PLAY;
p->sim_string = arg_sim->sval[0];
p->verbosity_count = arg_verbosity1->count; p->verbosity_count = arg_verbosity1->count;
if(arg_conf->count > 0)
{
p->config_filepath = strdup(arg_conf->filename[0]);
p->user_specified_config_file = true;
}
if (arg_udp->count > 0)
{
p->udp = true;
}
if (arg_audio1->count > 0)
{
p->disable_audio = true;
}
if(arg_fps->count > 0)
{
p->fps = arg_fps->ival[0];
}
if(arg_log->count > 0)
{
char* filename = strdup(arg_log->filename[0]);
p->log_fullfilename_str = strdup(arg_log->filename[0]);
p->log_filename_str = strdup(arg_log->basename[0]);
char* dname;
dname = dirname(filename);
p->log_dirname_str = strdup(dname);
p->user_specified_log_file = true;
free(filename);
}
exitcode = E_SUCCESS_AND_DO; exitcode = E_SUCCESS_AND_DO;
} }
else if (nerrors2==0) else
if (nerrors2==0)
{ {
if (help2->count > 0)
{
arg_print_errors(stdout,end2,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable2,"\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
}
p->program_action = A_CONFIG_TACH; p->program_action = A_CONFIG_TACH;
p->max_revs = arg_max_revs->ival[0]; p->max_revs = arg_max_revs->ival[0];
p->granularity = 1; p->granularity = 1;
@ -183,62 +92,45 @@ ConfigError getParameters(int argc, char** argv, Parameters* p)
p->verbosity_count = arg_verbosity2->count; p->verbosity_count = arg_verbosity2->count;
exitcode = E_SUCCESS_AND_DO; exitcode = E_SUCCESS_AND_DO;
} }
else if (nerrors3==0)
{
if (help3->count > 0)
{
arg_print_errors(stdout,end3,progname);
printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable3,"\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
}
p->program_action = A_TEST;
p->verbosity_count = arg_verbosity3->count;
if (arg_audio2->count > 0)
{
p->disable_audio = true;
}
exitcode = E_SUCCESS_AND_DO;
}
else else
{ {
// hit this if you typed wrong parameters
if (cmd1->count > 0) if (cmd1->count > 0)
{ {
arg_print_errors(stdout,end1,progname); arg_print_errors(stdout,end1,progname);
printf("Usage: %s ", progname); printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable1,"\n"); arg_print_syntax(stdout,argtable1,"\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
} }
else
if (cmd2a->count > 0) if (cmd2a->count > 0)
{ {
arg_print_errors(stdout,end2,progname); arg_print_errors(stdout,end2,progname);
printf("Usage: %s ", progname); printf("Usage: %s ", progname);
arg_print_syntax(stdout,argtable2,"\n"); arg_print_syntax(stdout,argtable2,"\n");
exitcode = E_SUCCESS_AND_EXIT;
goto cleanup;
} }
if (cmd3->count > 0) else
{ {
arg_print_errors(stdout,end3,progname); if (help->count==0 && vers->count==0)
printf("Usage: %s ", progname); {
arg_print_syntax(stdout,argtable3,"\n"); printf("%s: missing <play|config> command.\n",progname);
printf("Usage 1: %s ", progname);
arg_print_syntax(stdout,argtable1,"\n");
printf("Usage 2: %s ", progname);
arg_print_syntax(stdout,argtable2,"\n");
}
}
exitcode = E_SUCCESS_AND_EXIT; exitcode = E_SUCCESS_AND_EXIT;
goto cleanup; goto cleanup;
} }
}
if (help0->count > 0 || (cmd3->count == 0 && cmd1->count == 0 && cmd2a->count == 0)) // interpret some special cases before we go through trouble of reading the config file
if (help->count > 0)
{ {
printf("Usage: %s\n", progname); printf("Usage: %s\n", progname);
printf("Usage 1: %s ", progname); printf("Usage 1: %s ", progname);
arg_print_syntax(stdout,argtable1,"\n"); arg_print_syntax(stdout,argtable1,"\n");
printf("Usage 2: %s ", progname); printf("Usage 2: %s ", progname);
arg_print_syntax(stdout,argtable2,"\n"); arg_print_syntax(stdout,argtable2,"\n");
printf("Usage 3: %s ", progname);
arg_print_syntax(stdout,argtable3,"\n");
printf("\nReport bugs on the github github.com/spacefreak18/monocoque.\n"); printf("\nReport bugs on the github github.com/spacefreak18/monocoque.\n");
exitcode = E_SUCCESS_AND_EXIT; exitcode = E_SUCCESS_AND_EXIT;
goto cleanup; goto cleanup;
@ -256,6 +148,6 @@ cleanup:
arg_freetable(argtable0,sizeof(argtable0)/sizeof(argtable0[0])); arg_freetable(argtable0,sizeof(argtable0)/sizeof(argtable0[0]));
arg_freetable(argtable1,sizeof(argtable1)/sizeof(argtable1[0])); arg_freetable(argtable1,sizeof(argtable1)/sizeof(argtable1[0]));
arg_freetable(argtable2,sizeof(argtable2)/sizeof(argtable2[0])); arg_freetable(argtable2,sizeof(argtable2)/sizeof(argtable2[0]));
arg_freetable(argtable3,sizeof(argtable3)/sizeof(argtable3[0]));
return exitcode; return exitcode;
} }

View File

@ -1,42 +1,22 @@
#ifndef _PARAMETERS_H #ifndef _PARAMETERS_H
#define _PARAMETERS_H #define _PARAMETERS_H
#include <stdbool.h>
#include "../simulatorapi/simapi/simapi/simapi.h"
typedef struct typedef struct
{ {
int program_action; int program_action;
const char* sim_string; const char* sim_string;
const char* save_file; const char* save_file;
int fps;
int max_revs; int max_revs;
int granularity; int granularity;
int verbosity_count; int verbosity_count;
SimulatorAPI sim;
bool simon;
bool udp;
bool disable_audio;
char* config_filepath;
char* config_dirpath;
bool user_specified_config_file;
bool user_specified_config_dir;
bool user_specified_log_file;
char* log_filename_str;
char* log_fullfilename_str;
char* log_dirname_str;
} }
Parameters; Parameters;
typedef enum typedef enum
{ {
A_PLAY = 0, A_PLAY = 0,
A_TEST = 1, A_CONFIG_TACH = 1,
A_CONFIG_TACH = 2, A_CONFIG_SHAKER = 2
A_CONFIG_SHAKER = 3
} }
ProgramAction; ProgramAction;
@ -48,7 +28,6 @@ typedef enum
} }
ConfigError; ConfigError;
int freeparams(Parameters* p);
ConfigError getParameters(int argc, char** argv, Parameters* p); ConfigError getParameters(int argc, char** argv, Parameters* p);
struct _errordesc struct _errordesc

View File

@ -2,20 +2,37 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <string.h> #include <string.h>
#include <basedir_fs.h>
#include <libconfig.h>
#include <libconfig.h>
#include "gameloop/gameloop.h" #include "gameloop/gameloop.h"
#include "gameloop/tachconfig.h" #include "gameloop/tachconfig.h"
#include "devices/simdevice.h" #include "devices/simdevice.h"
#include "devices/sound.h"
#include "helper/parameters.h" #include "helper/parameters.h"
#include "helper/dirhelper.h" #include "helper/dirhelper.h"
#include "helper/confighelper.h" #include "helper/confighelper.h"
#include "simulatorapi/simapi/simapi/simdata.h" #include "simulatorapi/simdata.h"
#include "slog/slog.h" #include "slog/slog.h"
#define PROGRAM_NAME "monocoque" int create_dir(char* dir)
{
struct stat st = {0};
if (stat(dir, &st) == -1)
{
mkdir(dir, 0700);
}
}
char* create_user_dir(char* dirtype)
{
char* home_dir_str = gethome();
char* config_dir_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen(dirtype) + strlen("monocoque/"));
strcpy(config_dir_str, home_dir_str);
strcat(config_dir_str, dirtype);
strcat(config_dir_str, "monocoque");
create_dir(config_dir_str);
free(config_dir_str);
}
void display_banner() void display_banner()
{ {
@ -26,119 +43,37 @@ void display_banner()
printf("/_/ /_/ \\____/ /_/ |_/ \\____/ \\____/ \\____/ \\___\\_\\\\____/ /_____/ \n"); printf("/_/ /_/ \\____/ /_/ |_/ \\____/ \\____/ \\____/ \\___\\_\\\\____/ /_____/ \n");
} }
void SetSettingsFromParameters(Parameters* p, MonocoqueSettings* ms, char* configdir_str, char* cachedir_str)
{
if(p->user_specified_config_file == true && does_file_exist(p->config_filepath))
{
ms->config_str = strdup(p->config_filepath);
}
else
{
if(p->user_specified_config_dir == true && does_directory_exist(p->config_dirpath))
{
asprintf(&ms->config_str, "%s/%s", p->config_dirpath, "monocoque.config");
}
else
{
asprintf(&ms->config_str, "%s%s", configdir_str, "monocoque.config");
}
}
if(p->user_specified_log_file == true && does_file_exist(p->log_fullfilename_str))
{
ms->log_dirname_str = strdup(p->log_dirname_str);
ms->log_filename_str = strdup(p->log_filename_str);
}
else
{
ms->log_dirname_str = strdup(cachedir_str);
ms->log_filename_str = strdup("monocoque.log");
}
ms->fps = p->fps;
ms->verbosity_count = p->verbosity_count;
ms->program_action = A_TEST;
if (p->program_action == A_PLAY)
{
ms->program_action = A_PLAY;
}
if (p->program_action == A_CONFIG_TACH)
{
ms->program_action = A_CONFIG_TACH;
}
ms->force_udp_mode = false;
ms->disable_audio = p->disable_audio;
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
display_banner(); display_banner();
char* home_dir_str = gethome(); Parameters* p = malloc(sizeof(Parameters));
if(home_dir_str == NULL)
{
fprintf(stderr, "You need a home directory");
return 0;
}
Parameters* p = NULL;
p = malloc(sizeof(Parameters));
MonocoqueSettings* ms = malloc(sizeof(MonocoqueSettings));; MonocoqueSettings* ms = malloc(sizeof(MonocoqueSettings));;
ConfigError ppe = getParameters(argc, argv, p); ConfigError ppe = getParameters(argc, argv, p);
if (ppe == E_SUCCESS_AND_EXIT || ppe == E_SOMETHING_BAD) if (ppe == E_SUCCESS_AND_EXIT)
{ {
printf("invalid parameters\n");
goto cleanup_final; goto cleanup_final;
} }
ms->program_action = p->program_action;
xdgHandle xdg; char* home_dir_str = gethome();
if(!xdgInitHandle(&xdg)) create_user_dir("/.config/");
{ create_user_dir("/.cache/");
fprintf(stderr, "Function xdgInitHandle() failed, is $HOME unset?\n"); char* config_file_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen("/.config/") + strlen("monocoque/monocoque.config"));
goto cleanup_final; char* cache_dir_str = ( char* ) malloc(1 + strlen(home_dir_str) + strlen("/.cache/monocoque/"));
} strcpy(config_file_str, home_dir_str);
strcat(config_file_str, "/.config/");
strcpy(cache_dir_str, home_dir_str);
strcat(cache_dir_str, "/.cache/monocoque/");
strcat(config_file_str, "monocoque/monocoque.config");
const char* config_home_str = xdgConfigHome(&xdg);
const char* cache_home_str = xdgCacheHome(&xdg);
char* cachedir_str = NULL;
char* configdir_str = NULL;
if(p->user_specified_config_file == false && p->user_specified_config_dir == false)
{
create_xdg_dir(config_home_str);
configdir_str = create_user_dir(home_dir_str, ".config", PROGRAM_NAME);
}
if(p->user_specified_log_file == false)
{
create_xdg_dir(cache_home_str);
cachedir_str = create_user_dir(home_dir_str, ".cache", PROGRAM_NAME);
}
fprintf(stderr, "applying settings\n");
SetSettingsFromParameters(p, ms, configdir_str, cachedir_str);
if(cachedir_str != NULL)
{
free(cachedir_str);
}
if(configdir_str != NULL)
{
free(configdir_str);
}
fprintf(stderr, "settings applied\n");
slog_init("monocoque", SLOG_FLAGS_ALL, 1);
slog_config_t slgCfg; slog_config_t slgCfg;
slog_config_get(&slgCfg); slog_config_get(&slgCfg);
slgCfg.eColorFormat = SLOG_COLORING_TAG; slgCfg.eColorFormat = SLOG_COLORING_TAG;
slgCfg.eDateControl = SLOG_TIME_ONLY; slgCfg.eDateControl = SLOG_TIME_ONLY;
strcpy(slgCfg.sFileName, ms->log_filename_str); strcpy(slgCfg.sFileName, "monocoque.log");
strcpy(slgCfg.sFilePath, ms->log_dirname_str); strcpy(slgCfg.sFilePath, cache_dir_str);
slgCfg.nTraceTid = 0; slgCfg.nTraceTid = 0;
slgCfg.nToScreen = 1; slgCfg.nToScreen = 1;
slgCfg.nUseHeap = 0; slgCfg.nUseHeap = 0;
@ -146,117 +81,114 @@ int main(int argc, char** argv)
slgCfg.nFlush = 0; slgCfg.nFlush = 0;
slgCfg.nFlags = SLOG_FLAGS_ALL; slgCfg.nFlags = SLOG_FLAGS_ALL;
slog_config_set(&slgCfg); slog_config_set(&slgCfg);
if (ms->verbosity_count < 2) if (p->verbosity_count < 2)
{ {
slog_disable(SLOG_TRACE); slog_disable(SLOG_TRACE);
} }
if (ms->verbosity_count < 1) if (p->verbosity_count < 1)
{ {
slog_disable(SLOG_DEBUG); slog_disable(SLOG_DEBUG);
} }
xdgWipeHandle(&xdg);
slogi("checking for diameters config"); slogi("Loading configuration file: %s", config_file_str);
char* diameters_file_str;
asprintf(&diameters_file_str, "%s/.config/monocoque/diameters.config", home_dir_str);
ms->tyre_diameter_config = strdup(diameters_file_str);
free(diameters_file_str);
ms->useconfig = 0;
ms->configcheck = 0;
slogi("Testing monocoque config file: %s", ms->config_str);
slogd("using diameters file %s %i", ms->tyre_diameter_config, ms->configcheck);
config_t cfg; config_t cfg;
config_init(&cfg); config_init(&cfg);
config_setting_t* config_devices = NULL; config_setting_t* config_devices = NULL;
if (!config_read_file(&cfg, ms->config_str))
if (!config_read_file(&cfg, config_file_str))
{ {
sloge("Issue with monocoque config file: %s:%d - %s", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg)); fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
fprintf(stderr, "Issue with monocoque config file: %s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
slog_destroy();
goto cleanup_final;
} }
else else
{ {
slogi("Opened and validated monocoque configuration file"); slogi("Openend monocoque configuration file");
config_devices = config_lookup(&cfg, "devices");
} }
config_destroy(&cfg); free(config_file_str);
free(cache_dir_str);
if (p->program_action == A_CONFIG_TACH)
if (ms->program_action == A_CONFIG_TACH)
{ {
int error = 0; int error = 0;
SimDevice* tachdev = malloc(sizeof(SimDevice)); SimDevice* tachdev = malloc(sizeof(SimDevice));
tachdev->initialized = false;
SimData* sdata = malloc(sizeof(SimData)); SimData* sdata = malloc(sizeof(SimData));
SimInfo* siminfo = malloc(sizeof(SimInfo));
simapi_set_faux_siminfo(siminfo);
DeviceSettings* ds = malloc(sizeof(DeviceSettings)); DeviceSettings* ds = malloc(sizeof(DeviceSettings));
error = devsetup("USB", "Tachometer", "None", ms, ds, NULL); error = devsetup("USB", "Tachometer", "None", ms, ds, NULL);
error = devinit(tachdev, ds);
if (error != MONOCOQUE_ERROR_NONE)
{
sloge("Could not proceed with tachometer configuration due to error: %i", error);
}
else
{
int devices = 0;
devices = devinit(tachdev, siminfo, 1, ds, ms);
if(devices < 1)
{
error = MONOCOQUE_ERROR_INVALID_DEV;
}
}
if (error != MONOCOQUE_ERROR_NONE)
{
sloge("Could not proceed with tachometer configuration due to error: %i", error);
}
else
{
slogi("configuring tachometer with max revs: %i, granularity: %i, saving to %s", p->max_revs, p->granularity, p->save_file); slogi("configuring tachometer with max revs: %i, granularity: %i, saving to %s", p->max_revs, p->granularity, p->save_file);
if (error != MONOCOQUE_ERROR_NONE)
{
sloge("Could not proceed with tachometer configuration due to error: %i", error);
}
else
{
config_tachometer(p->max_revs, p->granularity, p->save_file, tachdev, sdata); config_tachometer(p->max_revs, p->granularity, p->save_file, tachdev, sdata);
} }
devfree(tachdev);
slogt("freeing devices if necessary"); free(tachdev);
if(tachdev->initialized == true)
{
slogt("freeing tachmoeter device");
tachdev->free(tachdev);
}
//free(tachdev);
free(sdata); free(sdata);
free(ds); free(ds);
} }
else else
{ {
int error = 0;
error = MONOCOQUE_ERROR_NONE;
//setupsound();
bool pulseaudio = false;
if (ms->program_action == A_PLAY)
{
ms->useconfig = 1;
slogi("running monocoque in gameloop mode.."); slogi("running monocoque in gameloop mode..");
//#ifdef USE_PULSEAUDIO int error = 0;
//pa_threaded_mainloop_unlock(mainloop);
pulseaudio = true;
//#endif
if(ms->disable_audio == false) error = strtogame(p->sim_string, ms);
if (error != MONOCOQUE_ERROR_NONE)
{ {
setupsound(); goto cleanup_final;
} }
//error = looper(devices, numdevices, p);
error = monocoque_mainloop(ms); int configureddevices = config_setting_length(config_devices);
int numdevices = 0;
DeviceSettings* ds[configureddevices];
slogi("found %i devices in configuration", configureddevices);
int i = 0;
while (i<configureddevices)
{
error = MONOCOQUE_ERROR_NONE;
DeviceSettings* settings = malloc(sizeof(DeviceSettings));
ds[i] = settings;
config_setting_t* config_device = config_setting_get_elem(config_devices, i);
const char* device_type;
const char* device_subtype;
const char* device_config_file;
config_setting_lookup_string(config_device, "device", &device_type);
config_setting_lookup_string(config_device, "type", &device_subtype);
config_setting_lookup_string(config_device, "config", &device_config_file);
if (error == MONOCOQUE_ERROR_NONE)
{
error = devsetup(device_type, device_subtype, device_config_file, ms, ds[i], config_device);
}
if (error == MONOCOQUE_ERROR_NONE)
{
numdevices++;
}
i++;
}
i = 0;
int j = 0;
error = MONOCOQUE_ERROR_NONE;
SimDevice* devices[numdevices];
while (i<configureddevices)
{
if (ds[i]->is_valid == true)
{
SimDevice* device = malloc(sizeof(SimDevice));
devinit(device, ds[i]);
devices[j] = device;
j++;
}
i++;
}
error = looper(devices, numdevices, ms->sim_name);
if (error == MONOCOQUE_ERROR_NONE) if (error == MONOCOQUE_ERROR_NONE)
{ {
slogi("Game loop exited succesfully with error code: %i", error); slogi("Game loop exited succesfully with error code: %i", error);
@ -265,69 +197,37 @@ int main(int argc, char** argv)
{ {
sloge("Game loop exited with error code: %i", error); sloge("Game loop exited with error code: %i", error);
} }
if(ms->disable_audio == false)
{
freesound();
}
}
else
{
slogi("running monocoque in test mode...");
if(ms->disable_audio == false) i = 0;
while (i<configureddevices)
{ {
setupsound(); if(ds[i]->dev_subtype == SIMDEV_USB)
{
free(ds[i]->tachsettings.pulses_array);
free(ds[i]->tachsettings.rpms_array);
}
free(ds[i]);
i++;
} }
ms->useconfig = 0;
int configs = getNumberOfConfigs(ms->config_str); i = 0;
int confignum = getconfigtouse(ms->config_str, "default", configs-1); while (i<numdevices)
int configureddevices;
configcheck(ms->config_str, confignum, &configureddevices);
DeviceSettings* ds = malloc(configureddevices * sizeof(DeviceSettings));
slogd("loading confignum %i, with %i devices.", confignum, configureddevices);
int numdevices = uiloadconfig(ms->config_str, confignum, configureddevices, ms, ds);
SimDevice* simdevices = malloc(numdevices * sizeof(SimDevice));
SimInfo* siminfo = malloc(sizeof(SimInfo));
simapi_set_faux_siminfo(siminfo);
int initdevices = devinit(simdevices, siminfo, configureddevices, ds, ms);
for( int i = 0; i < configureddevices; i++)
{ {
settingsfree(ds[i]); devfree(devices[i]);
free(devices[i]);
i++;
} }
free(ds);
error = tester(simdevices, numdevices);
if (error == MONOCOQUE_ERROR_NONE)
{
slogi("Test exited succesfully with error code: %i", error);
}
else
{
sloge("Test exited with error code: %i", error);
}
for (int x = 0; x < numdevices; x++)
{
if (simdevices[x].initialized == true)
{
simdevices[x].free(&simdevices[x]);
}
}
if(ms->disable_audio == false)
{
freesound();
}
}
slog_destroy();
} }
configcleanup:
config_destroy(&cfg);
cleanup_final: cleanup_final:
monocoquesettingsfree(ms);
free(ms); free(ms);
freeparams(p);
free(p); free(p);
exit(0); exit(0);
} }

View File

@ -1,45 +1,10 @@
set(simulatorapi_source_files set(simulatorapi_source_files
simapi/simapi/simmapper.c simmapper.c
simapi/simapi/simmapper.h simmapper.h
simapi/simapi/simdata.h simdata.h
simapi/simapi/test.h test.h
simapi/include/acdata.h simapi/acdata.h
simapi/include/rf2data.h ac.h
simapi/include/pcars2data.h
simapi/include/dirt2data.h
simapi/include/scs2data.h
simapi/include/outgauge.h
simapi/include/f12018.h
simapi/include/rbrdata.h
simapi/include/r3e.h
simapi/include/wreckfest2data.h
simapi/include/forza.h
simapi/simmap/basicmap.h
simapi/simmap/mapacdata.c
simapi/simmap/mapacdata.h
simapi/simapi/ac.h
simapi/simapi/rf2.h
simapi/simapi/dirt2.h
simapi/simapi/pcars2.h
simapi/simapi/scs2.h
simapi/simapi/outgauge.h
simapi/simapi/wreckfest2.h
simapi/simapi/f1.h
simapi/simapi/rbr.h
simapi/simapi/forzadef.h
simapi/simapi/r3edef.h
simapi/simapi/mapping/acmapper.c
simapi/simapi/mapping/rf2mapper.c
simapi/simapi/mapping/r3emapper.c
simapi/simapi/mapping/pcars2mapper.c
simapi/simapi/mapping/dirt2mapper.c
simapi/simapi/mapping/scs2mapper.c
simapi/simapi/mapping/outgaugemapper.c
simapi/simapi/mapping/f12018mapper.c
simapi/simapi/mapping/rbrmapper.c
simapi/simapi/mapping/wreckfest2mapper.c
simapi/simapi/mapping/forzamapper.c
simapi/simapi/getpid.c
) )
add_library(simulatorapi STATIC ${simulatorapi_source_files}) add_library(simulatorapi STATIC ${simulatorapi_source_files})

21
src/monocoque/simulatorapi/ac.h Executable file
View File

@ -0,0 +1,21 @@
#ifndef _AC_H
#define _AC_H
#include <stdbool.h>
#include "simapi/acdata.h"
#define AC_PHYSICS_FILE "acpmf_physics"
#define AC_STATIC_FILE "acpmf_static"
typedef struct
{
bool has_physics;
bool has_static;
void* physics_map_addr;
void* static_map_addr;
struct SPageFilePhysics ac_physics;
struct SPageFileStatic ac_static;
}
ACMap;
#endif

View File

@ -0,0 +1,21 @@
#ifndef _RF2_H
#define _RF2_H
#include <stdbool.h>
#include "simapi/rf2data.h"
#define RF2_TELEMETRY_FILE "rFactor2SMMP_Telemetry"
#define RF2_SCORING_FILE "rFactor2SMMP_Scoring"
typedef struct
{
bool has_telemetry;
bool has_scoring;
void* telemetry_map_addr;
void* scoring_map_addr;
struct rF2Telemetry rf2_telemetry;
//struct rF2Scoring rf2_scoring;
}
RF2Map;
#endif

@ -1 +1 @@
Subproject commit ea44d0e57f855a12242b35553623250e22aa0f3c Subproject commit 04fd2ca8ed590f562ad6423d9cf9f076daa28b95

Some files were not shown because too many files have changed in this diff Show More