Skip to content

Commit

Permalink
Merge pull request #5 from boschglobal/erik_f1
Browse files Browse the repository at this point in the history
Migration of fone2val
  • Loading branch information
erikbosch authored Mar 21, 2024
2 parents 66b5316 + ef15982 commit da73863
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Component | Content | Comment/Status
[HVAC Service](hvac_service) | Python service example
[Seat Service](seat_service) | C++ service example
[eCAL Provider](ecal2val) | Python provider for [eCAL](https://projects.eclipse.org/projects/automotive.ecal)
[PS4/PS5 - 2021 Formula Provider](./fone2val) | F1 Telemetrydata source for [KUKSA Databroker](https://github.com/eclipse/kuksa.val/tree/master/kuksa_databroker)

## Contribution

Expand Down
97 changes: 97 additions & 0 deletions fone2val/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# PS4/PS5 - 2021 Formula 1 Feeder
### This demonstrator serves as a non-commercial research project, highlighting its focus on academic exploration rather than commercial endeavors.
The feeder consumes [F1 Telemetrydata](https://www.ea.com/able/resources/f1-2021/ps4/telemetry) as datasource and pushes data to the kuksa.val Databroker.
### Video Demo (Click to Watch!)
[![Demo Video](https://img.youtube.com/vi/7C_yHItbJNU/0.jpg)](https://www.youtube.com/watch?v=7C_yHItbJNU "Demo Video - Click to Watch!")

For the remaining parts of the demonstrator, except for the feeder contained in this repository, see
https://github.com/fraunhofer-iem/f1-telemetry-dashboard
### Feeder
The custom [VSS File](./VSS/vss.json) contains specification points for further Application use.\
The [`carTelemetry_feeder.ini`](./config/carTelemetry_feeder.ini) contains `kuksa.val`, `listenerIPAddr` and `PS5_UDPPort` configuration.

Before starting the [F1 feeder](./carTelemetry_feeder.py), you need to start the `kuksa.val databroker` docker container by running the following command in the main project folder:
```
docker run -it -v ./VSS:/VSS --rm --net=host -p 127.0.0.1:8090:8090 -e LOG_LEVEL=ALL ghcr.io/eclipse/kuksa.val/databroker:master --insecure --vss /VSS/vss.json
```
This VSS folder, contains a custom vss.json file for this particular game.
## Install dependencies and execution

General Information: This Project was run on an Ubuntu VM and created in cooperation with [`Fraunhofer IEM`](https://www.iem.fraunhofer.de/) as a non-commercial research project.

#### carTelemetry_feeder.ini
```
a. The F1 telemetry port/IP number for communication has to be updated in the ./config/carTelemetry_feeder.ini file.
> IP address of the Host/VM for example 192.168.178.154
> Same with the Port: fore example 20778
b. The listenerIPAddr of the host/VM a also needs to be updated in the ./config/carTelemetry_feeder.ini file.
> It has to match with the given IP in step a.
c. The PS5_UDPPort of the host/VM a also needs to be updated in the ./config/carTelemetry_feeder.ini file.
> It has to match with the given Port in step a.
d. kuksa.val IP for the VSSClient has to be updated in the ./config/carTelemetry_feeder.ini file.
> Normally set to 127.0.0.1.
e. kuksa.val port for the VSSClient has to be updated in the ./config/carTelemetry_feeder.ini file.
> Normaly set to 55555.
```

#### Dependencies:
```
We need to install the python F1 Module
> $pip install Telemetry-F1-2021
We also need to install kuksa-client (if not already done)
> $pip install kuksa-client
```

#### PS5 Settings
```
PS5 Telemetry Settings:
1. We have to enable the telemetry feature in the Game Settings.
2. We can make note of the UDP port number (20778)
3. We need to update the laptop/PC IP address for UDP communication.
```
#### Running the feeder

Now to run the feeder, execute the following command in your favorite Command Line Interface (Terminal):
```
python3 carTelemetry_feeder.py
```
#### What Data is sent?

Currently we are sending the following data:
```
Vehicle speed in kmh,
Vehicle engine rpm,
Vehicle fuel level in percent,
Wear level of each tire in percent,
Left and right wing damage in percent,
and last, Vehicle last Lap Time
```

## Authorization

[F1 feeder](./carTelemetry_feeder.py) will try to authenticate itself towards the KUKSA.val Server/Databroker if a token is given.
Note that the KUKSA.val Databroker by default does not require authentication.


### Troubleshouting
if the python feeder command fails:
```
(Errno 99 Cannot assign requested Address)
1. use the Linux command 'ifconfig' in your terminal
2. find the following line: enp0s3: inet 192.168.178.***
--> and copy the IP into the ./config/carTelemetry_feeder.ini file
```
1 change: 1 addition & 0 deletions fone2val/VSS/vss.json

Large diffs are not rendered by default.

171 changes: 171 additions & 0 deletions fone2val/carTelemetry_feeder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#################################################################################
# Copyright (c) 2023 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License 2.0 which is available at
# http://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
#################################################################################

import os
import sys
import signal
import threading
import configparser
from kuksa_client.grpc import VSSClient
from kuksa_client.grpc import Datapoint
from telemetry_f1_2021.listener import TelemetryListener

scriptDir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(scriptDir, "../../"))


class Kuksa_Client():
# Constructor
def __init__(self, config):
print("Init kuksa client...")
if "kuksa_val" not in config:
print("kuksa_val section missing from configuration, exiting")
sys.exit(-1)

def shutdown(self):
self.client.stop()

# Christophers approach on sending Data to Kuksa Server
def setTelemetryData(self, teleData):
dataDictionary = {}

kuksaConfig = config['kuksa_val']
with VSSClient(kuksaConfig.get('host'), kuksaConfig.getint('port')) as client:
for x, y in teleData.items():
dataDictionary.update({
str(x): Datapoint(y)
})
client.set_current_values(dataDictionary)


class carTelemetry_Client():

def __init__(self, config, consumer):
print("Init carTelemetry client...")
if "listenerIPAddr" not in config:
print("listenerIPAddr section missing from configuration, exiting")
sys.exit(-1)
if "PS5_UDPPort" not in config:
print("PS5_UDPPort section missing from configuration, exiting")
sys.exit(-1)
# extract carTelemetry Data
print("Connecting to extract CarTelemetry Data")

self.carTelemetry = {}
self.running = True

self.thread = threading.Thread(target=self.loop, args=())
self.thread.start()

def loop(self):
print("Car Telemetry data loop started")

config_ipAddr = config['listenerIPAddr']
config_UDPport = config['PS5_UDPPort']

listener_ip = config_ipAddr['host']
udp_port = config_UDPport['port']

print(f"listener_ip:{listener_ip}")
print(f"udp_port:{udp_port}")

listener = TelemetryListener(port=int(udp_port), host=listener_ip)

while self.running:
try:
# listen to the data via UDP channel
packet = listener.get()

# Update packet ID
packetID = packet.m_header.m_packet_id
# player carIndex
carIndex = packet.m_header.m_player_car_index
# Check for telemetry data - packet ID 6.
if (packetID == 6):

EngineRPM = packet.m_car_telemetry_data[carIndex].m_engine_rpm
Speed = packet.m_car_telemetry_data[carIndex].m_speed

self.carTelemetry['Vehicle.Speed'] = Speed
self.carTelemetry['Vehicle.RPM'] = EngineRPM

# Set the data to the KUKSA_VAL
self.consumer.setTelemetryData(self.carTelemetry)

if (packetID == 7): # car status data packet
fuelInTank = packet.m_car_status_data[carIndex].m_fuel_in_tank
fuelCapacity = packet.m_car_status_data[carIndex].m_fuel_capacity
fuelInPercent = fuelInTank/fuelCapacity

self.carTelemetry['Vehicle.FuelLevel'] = int(fuelInPercent*100)
self.consumer.setTelemetryData(self.carTelemetry)

if (packetID == 10): # car dmg packet

leftWingDamage = packet.m_car_damage_data[carIndex].m_front_left_wing_damage
rightWingDamage = packet.m_car_damage_data[carIndex].m_front_right_wing_damage

tyreWear_1 = packet.m_car_damage_data[carIndex].m_tyres_wear[0]
tyreWear_2 = packet.m_car_damage_data[carIndex].m_tyres_wear[1]
tyreWear_3 = packet.m_car_damage_data[carIndex].m_tyres_wear[2]
tyreWear_4 = packet.m_car_damage_data[carIndex].m_tyres_wear[3]

self.carTelemetry['Vehicle.FrontLeftWingDamage'] = leftWingDamage
self.carTelemetry['Vehicle.FrontRightWingDamage'] = rightWingDamage
self.carTelemetry['Vehicle.Tire.RearLeftWear'] = tyreWear_1
self.carTelemetry['Vehicle.Tire.RearRightWear'] = tyreWear_2
self.carTelemetry['Vehicle.Tire.FrontLeftWear'] = tyreWear_3
self.carTelemetry['Vehicle.Tire.FrontRightWear'] = tyreWear_4

self.consumer.setTelemetryData(self.carTelemetry)
if (packetID == 2):
lastLapTime = packet.m_lap_data[carIndex].m_last_lap_time_in_ms

self.carTelemetry['Vehicle.LastLapTime'] = lastLapTime/1000

self.consumer.setTelemetryData(self.carTelemetry)
except Exception:
continue

def shutdown(self):
self.running = False
self.consumer.shutdown()
self.carTelemetry.close()
self.thread.join()


if __name__ == "__main__":
print("<kuksa.val> Car Telemetry example feeder")
config_candidates = ['/config/carTelemetry_feeder.ini',
'/etc/carTelemetry_feeder.ini',
os.path.join(scriptDir, 'config/carTelemetry_feeder.ini')]
for candidate in config_candidates:
if os.path.isfile(candidate):
configfile = candidate
break
if configfile is None:
print("No configuration file found. Exiting")
sys.exit(-1)
config = configparser.ConfigParser()
config.read(configfile)

client = carTelemetry_Client(config, Kuksa_Client(config))

def terminationSignalreceived(signalNumber, frame):
print("Received termination signal. Shutting down")
client.shutdown()
signal.signal(signal.SIGINT, terminationSignalreceived)
signal.signal(signal.SIGQUIT, terminationSignalreceived)
signal.signal(signal.SIGTERM, terminationSignalreceived)

# end of file #
10 changes: 10 additions & 0 deletions fone2val/config/carTelemetry_feeder.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#Databroker ip and port
[kuksa_val]
host =
port =

#PS5 relevant
[listenerIPAddr]
host =
[PS5_UDPPort]
port =

0 comments on commit da73863

Please sign in to comment.