Average speed seems off

Posts: 27
I got my overlay working great, but when doing the broadcast, the math didn't seem correct for the average lap speed. I did the math, and it seems that it's showing faster on screen than it actually is, like a mile or two am hour off of what it should be.

Is that an iracing thing of how speed is calculated, or is there a setting to change within atvo? We're using it for qualifying for an indy style event, but may have to go the old fashioned way if it's not a fix.
Posts: 785
Are you referring to the "speedfast" or "speedprev" bindings? They are supposed to return the average speed of the fastest or previous lap respectively.

What they actually return is the track length divided by the laptime. Is that now how the average speed should be calculated? What are you comparing ATVO to, does iRacing give its own average speed (I am not aware of it) and is that more accurate than ours?
I can't change too much there, both the track length and laptime are given to us by the sim and they should be accurate enough (track length is accurate to meters, laptime accurate to at least 0.001 second) so I can't explain a 1-2 mph difference there.


Besides this we also calculate the speed of each car at all times, but this is not going to be very accurate because it is calculated based on track position. There is a limited sampling rate and we also don't have the exact position on track, we only have the approximate distance along the racing line (if a car takes a very different line its speed is going to be calculated wrong). So you can't use the average of all speeds to calculate an accurate average, it will be way off.
Edited (1 time)
Posts: 27
I must admit it's rather odd. For example, our quickest lap of the week of practice so far is a 46.275 lap time. On ATVO it is showing an average speed of 195.293 mph. But doing the calculation for a 2.5 mile track (on multiple calculations by different people), we all get 194.489 mph.

We have a work around on it, just glad we found out before qualifying day. I know on ovals in America, average speed of the lap is based on the lap time and the actual size of the track, not the actual line that they ran. The second part of your answer is what I was looking for, and had assumed was happening. Is there a data section that doesn't use the car position or racing line they used, and just goes off of the track data, for this instance would be calculating for speed for a 2.5 mile oval?
Posts: 785
My guess is that track is not exactly 2.5 miles but is actually 2.5103287708333... miles.

If you load in a session on that track and look at the Data Explorer you can probably find the track length somewhere.

For clarification, we do NOT use the actual car speed at any point to calculate average speed. We calculate average speed by taking track length and divide by laptime. This should be completely accurate. But a 2.5 mile oval is not exactly 2.5000000 miles so you have to use the proper track length to calculate it.
Edited (2 times)
Posts: 27
The track is measured at 2.5 miles. If you go through every piece of data that's ever been taken at the speedway, when they measure average speed, it is based off of 2.5 miles as the track was built and measured as so. But thanks for the info as to why the speed is off comparably.

I'm getting ahead of myself here, but is there a way to calculate for speed within a macro or something from the lap time to a certain distance so I can circumvent the issue with it being listed as the "wrong" size (for sake of comparison with the real indy 500)?
Posts: 785
Like I replied on the iRacing forums, the data we get from iRacing is that the length is 4.04 km, which is 2.51034 miles and not 2.5. We cannot fix that and I don't plan on overriding these kind of things just because they should be different. It's up to iRacing to fix this if it's a bug.

If you want to calculate an average speed with a different track length, you can very easily use a simple conversion script. Just bind to the fastest laptime (or previous laptime) binding instead of the "speed___" bindings, and then you can do the division yourself. For example:

using System;
using System.Linq;
using ATVO.ThemesSDK;
using ATVO.ThemeEditor.ThemeModels;
using ATVO.ThemeEditor.Scripting.DotNET;

namespace Scripts
{
public class conv : IScript
{
public object Execute(ThemeContentItem item, object value, string parameter, ISimulation sim)
{
var laptime = (float)value; // Laptime in seconds
var trackLength = 2.50f; // Hard-code 2.50 miles

var laptimeHours = laptime / 3600; // Laptime in hours
var speed = trackLength / laptimeHours; // Average speed in mph

return speed + " mph";
}
}
}


Perhaps the fastest laptime binding returns an already formatted laptime in which case the "value" you obtain is a string and not a floating point number. It is then maybe easier to bind to the "entitysessionresult_object" and obtain the FastestLapTime value from the result itself like so:

using System;
using System.Linq;
using ATVO.ThemesSDK;
using ATVO.ThemeEditor.ThemeModels;
using ATVO.ThemeEditor.Scripting.DotNET;
using ATVO.ThemesSDK.Data.Results;

namespace Scripts
{
public class conv : IScript
{
public object Execute(ThemeContentItem item, object value, string parameter, ISimulation sim)
{
var result = (IEntitySessionResult)value;
var laptime = result.FastestLapTime;

var trackLength = 2.50f; // Hard-code 2.50 miles

var laptimeHours = laptime / 3600; // Laptime in hours
var speed = trackLength / laptimeHours; // Average speed in mph

return speed + " mph";
}
}
}
Edited (1 time)
Posts: 27
I'll give this a try when I get home. Am I correct in assuming that I bind the previous lap, or fastest lap (whichever for the overlay), and then in the script section of that label, copy and past the code that you have input here in this thread, and it should in theory work for the display?
Posts: 785
The easiest way would be:

- Add a new script (.cs type)
- Copy the second script from my last post.
- Optionally replace "result.FastestLapTime" with "result.LastLapTime" if you want the previous lap instead of the fastest lap.
- Go to your label where you want it displayed
- Change the binding to "entitysessionresult_object"
- Under Convert script select the script you added in the first step.

That should be all.
Posts: 27
That time of year when trying to make this work, I did the steps you said, but on the script i get an error that says at the bottom, "Script must have a namespace called 'Scripts' with a class called 'Laptimetospeed' or 'Script'.

I'm not sure what i'm doing wrong or what other information you would need.
Posts: 27
https://i.ibb.co/mTB5c3L/1.png
Posts: 785
The name of the script file / item in ATVO must match the class name of the code in line 10. Your script file is called "lapspeed" but your class is called "conv". They must match otherwise ATVO doesn't know what part of the script to run.

By default also script names have the file extension in the name, so it should be "lapspeed.cs", and the class should be called "lapspeed".

Perhaps the easiest way to fix it is to add a new script and replace (copy/paste) only the contents of the "Execute" function. The class name will match in the default template.
Posts: 27
using System;
using ATVO.ThemesSDK;
using ATVO.ThemeEditor.ThemeModels;
using ATVO.ThemeEditor.Scripting.DotNET;

namespace lapspeed.cs
{
public class lapspeed : IScript
{
public object Execute(ThemeContentItem item, object value, string parameter, ISimulation sim)
{
var result = (IEntitySessionResult)value;
var laptime = result.FastestLapTime;

var trackLength = 2.50f; // Hard-code 2.50 miles

var laptimeHours = laptime / 3600; // Laptime in hours
var speed = trackLength / laptimeHours; // Average speed in mph

return speed + " mph";
}
}
}

that's the code i have in now, which is giving me this error: (Line 12) The type or namespace name 'IEntitySessionResult' could not be found (are you missing a using directive or an assembly reference?)

which in this case is the var result line.
Posts: 785
Double click the error will give you a piece of code you need to put at the top. Read this thread too: https://atvo.appgineering.com/Forum/Thread/94
Posts: 27
I double click on the error and it gives me an ATVO error and closes the software. Unable to load one or more of the reqeusted types. Retreieve the LoaderExceptions property for more information. This is the error log I get.

Error: 12/24/2020 7:13:57 PM (UTC)
ATVO version: 1.33.0.0
Themes SDK version: 1.33.0.0
Theme version: none

Unhandled exception
System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
at System.Reflection.Assembly.GetTypes()
at ATVO.ThemeEditor.Utils.ReflectionCache.GetTypes(Assembly assembly) in D:\Users\Nick\Documents\Programming\ATVO\Appgineer.in-TV-Overlay-for-iRacing\ATVO.ThemeEditor\Utils\ReflectionCache.cs:line 38
at ATVO.ThemeEditor.Scripting.DotNET.TypeSearchAssemblyProvider.Load() in D:\Users\Nick\Documents\Programming\ATVO\Appgineer.in-TV-Overlay-for-iRacing\ATVO.ThemeEditor\Scripting\DotNET\TypeSearchAssemblyProvider.cs:line 48
at ATVO.ThemeEditor.Scripting.DotNET.TypeSearchAssemblyProvider.<>c.<.cctor>b__8_0() in D:\Users\Nick\Documents\Programming\ATVO\Appgineer.in-TV-Overlay-for-iRacing\ATVO.ThemeEditor\Scripting\DotNET\TypeSearchAssemblyProvider.cs:line 24
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at ATVO.ThemeEditor.Views.Dialogs.FindUsingDirectivesDialog.Search() in D:\Users\Nick\Documents\Programming\ATVO\Appgineer.in-TV-Overlay-for-iRacing\ATVO.ThemeEditor\Views\Dialogs\FindUsingDirectivesDialog.xaml.cs:line 49
at ATVO.ThemeEditor.ViewModels.ErrorListViewModel.OnErrorClicked(ErrorViewModel error) in D:\Users\Nick\Documents\Programming\ATVO\Appgineer.in-TV-Overlay-for-iRacing\ATVO.ThemeEditor\ViewModels\ErrorListViewModel.cs:line 129
at ATVO.ThemeEditor.Views.ErrorListView.RowDoubleClick(Object sender, MouseButtonEventArgs e) in D:\Users\Nick\Documents\Programming\ATVO\Appgineer.in-TV-Overlay-for-iRacing\ATVO.ThemeEditor\Views\ErrorListView.xaml.cs:line 30
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
at System.Windows.Controls.Control.HandleDoubleClick(Object sender, MouseButtonEventArgs e)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
at System.Windows.UIElement.OnMouseDownThunk(Object sender, MouseButtonEventArgs e)
at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
at System.Windows.Input.InputManager.ProcessStagingArea()
at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
Posts: 81
Nick Thissen wrote:It is then maybe easier to bind to the "entitysessionresult_object" and obtain the FastestLapTime value from the result itself like so:

using System;
using System.Linq;
using ATVO.ThemesSDK;
using ATVO.ThemeEditor.ThemeModels;
using ATVO.ThemeEditor.Scripting.DotNET;
using ATVO.ThemesSDK.Data.Results;

namespace Scripts
{
public class conv : IScript
{
public object Execute(ThemeContentItem item, object value, string parameter, ISimulation sim)
{
var result = (IEntitySessionResult)value;
var laptime = result.FastestLapTime;

var trackLength = 2.50f; // Hard-code 2.50 miles

var laptimeHours = laptime / 3600; // Laptime in hours
var speed = trackLength / laptimeHours; // Average speed in mph

return speed + " mph";
}
}
}

How would you call tracklength to use the actual in-sim values? I wanted to add an average speed to a widget with a laptimes data set and there's no default binding for speeds there, just times.
Posts: 785
You can use the following value:
var trackLength = sim.Session.Track.Length;

This is the length in meters.