The online racing simulator
InSim updates needed for "live delta" type of feature?
Hello programmers,

I've been responding to a request for a "live delta" feature, available in LFS Lazy and described here:
https://www.lfs.net/forum/post/2055739#post2055739 (watch Mandula's video)

It's not a simple feature, requiring storage of best laps in some kind of high resolution format, to provide accurate comparison while driving. I'm wondering if it's possible to do from an external program, without direct memory access, and if not, what needs to be added to InSim.

More detail here, about why I'm asking:
https://www.lfs.net/forum/post/2056069#post2056069

Thanks!
It whould nice to add "insim nodes" with ID's (1 - 1024 (or more)) as a option in the AutoX editor.

And send a InsimPacket to the insimapp when someone cross a node.

could look something like this (Probably totally wrong LOL)

struct IS_NOX // Insim Node xTime
{
byte Size; // 15
byte Type; // ISP_NOX
byte ReqI; // 0
byte PLID; // player's unique id

word Node; //Node ID
byte Sp1; //Spare 2
byte Sp2; //Spare 3
byte Sp3; //Spare 4

unsigned Time; // hundredths of a second since start (as in SMALL_RTP)
};

Edit: The AutoX object count need to be increased by uhm ALOT.
For example: i run a timeattack server which has 6 stages. If i want to add 1024 nodes on each stage which is in total 6 * 1024 = 6.144 objects.
Or you need to add a seperate SubObject counter like you have done with the restricted area circles.
#3 - Racon
I had a think about this a while back, but I'm not so good with trig and have a lot of other irons in the fire. Lots of other fires too, in fact XD

I was thinking that I would save a trail of coordinates, speeds and timestamps during a lap, saving it if it's a better time or the first lap.

At each MCI I would find the closest 2 points, work out where I am proportionally in regard to those 2 and use their data to calculate my target time/speed for that moment. (Edit: the bit I struggled with is here - I would need a line between these two points and a 90degree angle to the car, but i couldn't work out how to get around when the closest two points weren't the right 2 to get this part to work)

I can't see it would need anything more than is present in InSim already to achieve that for someone who knows the maths, if we know what layout is loaded. (IIRC there is no AXI fired when a layout is loaded by an admin from the editor - I'm guessing because the client does it rather than the server. I'm requesting an AXI every race start as a workaround).
A little bit of math. Say you have 2 points A(x0, y0) and B(x1, y1) and you want to get a line that is defined by them. The equation of the line is: y(x)=(y1-y0)/(x1-x0)*x+y0. If you need to find a direction that is perpendicular to this line, its coefficient of direction is kp=-(y1-y0)/(x1-x0). Using the same equation for a straight line you can get another line: yp(x)=kp*x.

In 3D space, you can get the distance d between 2 points A(x0, y0, z0) and B(x1, y1, z1) as:
d=sqrt((x1-x0)^2+(y1-y0)^2+(z1-z0)^2)

Hope this helps.
Quote from rane_nbg :Our Airio can also show you the time for each sector and

Its relying on the same node information which LFS stores in .pth files. EQ Worry combined certain corners for custom layout tracks to complete some missing data. It would be much better to create a whole set of nodes which do not rely on a (modified) .pth file.

Actually this is the missing piece of the puzzle to get racing interesting again. At the time I had the idea to recreate nodes based on some external polygon track overlay being triggered by MCI data but this is a lot of work and I guess it introduces some lag.

twelve year old topic covering the same problem; https://www.lfs.net/forum/thread/74329-How-to-create-PTH-files
#7 - Racon
Thanks Rane, I've added those to my notes for the next time Thumbs up
Hello Scawen.
It is possible to develop it in an external program.
I think its done with Nodes like Lazy storres an integer describing the time elapsed from the start of the lap. The limitation comes when the race starts there can not be any reliable data untill the player passes sector 1 and recieves a sector packet.

Implementing it inside Live for Speed should be the best option.
Another thing that could be implemented from Lazy is the "Radar".
Would greatly improove safety/clean driving.
At this point, given the update progress with car mods, lighting, multithreading, and tire physics, I think it's best that Scawen only thinks about updating the inSim core in an optimal way that would allow us to create external apps.

Then, we could in a very similar way to creating mods, be able to create a plethora of different apps and offer features that Lazy had, or even more. For a long time now, I've been desperately expecting a real-time FFB graph monitor, but now I see that this can also be a feature of such apps. An example of how they made available those Python apps in Assetto Corsa, that would be awesome for LFS, a new playground for us Smile So my suggestion for Scawen is also to think along the lines of updating inSim with the simple 2D graphical capability of drawing points, lines, circles, squares, and similar on the screen.

As mentioned by Bass-Driver, we would need a new inSim packet that contains nodes, which is automatically sent whenever any car comes into contact with it (within some predefined sphere radius). Therefore, a node packet should also contain its spacial x,y,z coordinates as well as its radius, optionally maybe even a direction (heading) vector. Each node should be a physical sphere rather than a circle, this will make our life with math calculations easier.

Bear in mind that we should not distract Scawen way too much at this moment, there are many more important things he's working on, that are highly expected from all of us.
FWIW, I have a real-time delta feature mostly implemented (but not enabled) in the Sim Broadcasts InSim. It can be quite accurate (tested down to at least 1ms from a replay) with some limitations and caveats (see below).

It uses a combination of the LFS .pth files (or compatible custom ones that I make as needed for custom configs and entirely new layouts) and car position & speed from MCI packets.

A note for those who are not aware, LFS "nodes" actually refer to lines which are described in the pth files, drawn to the left and right of direction vectors with coordinates (nodes) which make up an approximate racing line.


I haven't fully finished the feature yet, as we intend to use it for more accurate reporting of the race order, and there are a few problems/limitations that raise some tricky edge-cases when needing to implement a seperate timing system. We need this for a couple of reasons:
  1. LFS has a "bug" where if two cars are close enough to eachother to be within the same node, LFS might show the cars in the wrong order (I believe it sorts them by PLID or something) causing the positions to flip around a lot when they physically aren't overtaking eachother.
  2. In custom configs, we only get position changes from LFS at sector lines, which can get confusing for commentary.
So, to the limitations (may only be a significant problem for Sim Broadcasts'/T&S use, but they're why I haven't yet finished the feature):
  1. The car positions from MSI packets are not always accurate. I need to do some proper testing, but I believe it may be significantly worse when running on a server (though it may be just as bad on the client, TBC). This comes from the fact that MCI packets aren't sent in-line with position packets sent client <-> server within LFS. MCI packets are sent at regular intervals, whereas the internal packets are variable rate. This means that the MCI positions are relying on the LFS prediction, which obviously can't always be correct. Under most condititions, this doesn't cause a major issue, but in cases of severe lag (especially combined with fast control inputs) or heavy collisions into walls etc, the positions can end up being quite wrong for one or more packets.
  2. In default configs (and most custom pth files), the Start/Finish line (and maybe sector lines?) don't neccesarily match a node. It is often halfway between two nodes, which can be several metres away. There is also no way for InSim to find out exactly where the S/F or sector lines are with default configs, though they can be read from the layout for open config. This may not be a problem when calculating deltas to another car (the purpose of this thread I guess), but does throw a spanner in the works for a custom timing & scoring system (which is Sim Broadcasts' main use) or for calculating more accurate split times than 100th of a second.
  3. InSim does not know when the actual start of the race happens, or (mostly for custom layouts) the correct order of cars at the moment the race starts. The initial REO isn't neccesarily the final starting order because of cars joining late/spectating. It's possible the REO gets updated later, but I've not yet tested that. This means that you can't reliably start calculating deltas until a car has crossed a split (or potentially some other line, but that would need to be configured per layout).
Now, the above may not be a problem if all you need is a mostly correct delta, don't mind not having data till crossing a sector line and can work around some other edge cases. It makes my uses quite a bit more complicated though Wink
Coming back to this, I know there is no "race started" packet (no, I'm not talking about IS_RST, but actual start on green light) because it could be used to cheat starts, but how about adding such a packet, only sending it 1-5 seconds after the start, with the time info? This way InSim applications could adjust and still have timing data.

Alternatively, I guess we can use IS_CSC packets to trigger the race start manually when multiple cars start moving. For hotlaps it's a bit different, we would need to check the PTH nodes and start timing when the car reaches the finish line node - won't be accurate, but I guess we can do that until the first split.
So, I implemented something like this for SimFIA around 15 years ago now. It only used the Split packets because it was the only thing that was reliable. We would have the Timing and Scoring interface that would show deltas to car in front, car behind for our league races, but it would only show for 5 seconds when the player passed a sector. The FiA and FOM get their timing information on the F1 cars from the timing lines as well, but not just each sector time, but also the mini-sectors between the marshal posts (around 200 meters apart on average) each have a timing loop on them. The FOM TV feed actually updates the deltas between the cars on each timing loop by calculating the delta between Car 1 and Car 2's time between the same timing loop. That effectively works out to an array of timestamps. It's also why you don't see a sector time display, until all cars have passed the first sector timing line.

The array would be something like ...


struct Cars {
UCID u8;
PLID u8;
Info u8; // Is Player; Is IA;
Sp0 u8; // Padding. Always 0;

Lap u16; // Current Lap; Used to index into Laps array.
Node u16; // Current Node; Used to index into Best and Laps array.
Last u32; // Current Timestamp; Used to show last update from car.

// Array of Nodes
Best[Node] = CarInfo;
Laps[Lap][Node] = CarInfo;
}

struct CarInfo {
Lap = u16; // 24 Hour Races could have more than 255 laps.
Node = u16; // Node ID
Time = u32; // Timestamp (Milliseconds, So / 1000 for seconds.)

// Location in map in LFS Meter Units.
LocX = u32;
LocY = u32;
LocZ = u32;

// Vector of their velocity in LFS Meters Units.
// 9.80665 m/s² = 952_863 = 1G; Z should equal this when sitting still on level ground.
// -49.03325 m/s² = -4_764_315 = -5G; X Formula Car Hard Breaking.
VelX = i32;
VelY = i32;
VelZ = i32;

// Heading Degrees from Center +90.00 = 9000; -180.00 = -18000
HeadX = i16; // -Left / +Right
HeadY = i16; // +Up / -Down

// Rotating Degrees from Center +90.00 = 9000; -180.00 = -18000
RotX = i16; // Roll: between -180 and 180 deg;
RotY = i16; // Pitch: between -90 and 90 deg;
RotZ = i16; // Yaw: between -180 and 180 deg;
}

If you are going for iRacing type delta for players to compare against themselves, a much higher resolution timing loop is required. I'd want a timestamp for every time they enter each track node. Yeah, that's going to be like 350+ data points for each lap, and sometimes much more. I'd additionally, want their X, Y, Z world cords, as well as their heading vector, and speed (in the LFS meters per second, so the u32 where the upper u16 is the actual meters, and the lower u16 is the 65536th of a meter.)

IS_GPS {
Size = 12; // Bytes * 4;
Type = ISP_GPS;
SubT = u8;
ReqI = u8;

UCID = u8;
PLID = u8;
Info = u8; // Is AI, is Player;
Sp0 = u8; // Padding

Lap = u16; // 24 Hour Races could have more than 255 laps.
Node = u16; // Node ID
Time = u32; // Timestamp (Milliseconds, So / 1000 for seconds.)
Speed = u32; // Speed in m/s using LFS Meter Units.

// Location in map in LFS Meter Units.
LocX = u32;
LocY = u32;
LocZ = u32;

// Vector of their velocity in LFS Meters Units.
// 9.80665 m/s² = 952_863 = 1G; Z should equal this when sitting still on level ground.
// -49.03325 m/s² = -4_764_315 = -5G; X Formula Car Hard Breaking.
VelX = i32;
VelY = i32;
VelZ = i32;

// Heading Degrees from Center +90.00 = 9000; -180.00 = -18000
HeadX = i16; // -Left / +Right
HeadY = i16; // +Up / -Down

// Rotating Degrees from Center +90.00 = 9000; -180.00 = -18000
RotX = i16; // Roll: between -180 and 180 deg;
RotY = i16; // Pitch: between -90 and 90 deg;
RotZ = i16; // Yaw: between -180 and 180 deg;
}



That should get us pretty close. The
CarInfo->Best[CarInfo->Node]->Time - CarInfo->Best[0]->Time

gives you the delta of time into the lap. If you're faster into that node the time delta will be negative.
CarInfo->Best[CarInfo->Node]->Speed - CarInfo->Speed

if positive, you are faster than into that node speed wise than before, negative you are slower speed wise into that node.
I like the general idea of what you're proposing here, but I think there are some bits that can be removed for optimization:

The Cars struct doesn't really need UCID or Info (as long as that Info byte only contains the AI flag, at least), since those can easily be retrieved from the PLID.

Similarly in IS_GPS, Info, PLID, and SubT (what is it supposed to represent?) can be removed, which saves us 4 bytes with the spare Sp0. Although to be honest, I think this could also be achieved with an upgrade IS_MCI (or rather CompCar, which should include pitch and yaw in addition to heading).

Overall, I think your proposed IS_GPS, CarInfo, and Cars structs overlap a bit too much, and similar info could be retrieved by adding pitch and yaw to CompCar, as well as timestamps to IS_MCI. The tracking of timestamps at each node could probably be left to InSim developers (I did that myself, I know KingOfIce did too - not sure how exactly, but likely the same principle), but we would really need to be able to load custom PTH files via InSim for this to work in custom layouts.

And another hurdle is the lack of a signal for race start, which, while it helps prevent perfect starts via InSim, also prevents proper time tracking (even in hotlap mode, where we don't know when the finish line is crossed for the first time). Having a packet sent a few seconds after race start, and including the timestamp of the start, could be pretty nice for this.
I had forgotten about this thread... if not the maths in it. (Ty again rane, most helpful! Ended up with using dot and cross products to make nodelines and calculate the ratio between them)

I have gotten a prototype up and running since my last post here (link), but it is still funky in that the timings, while self-consistent, are adrift from LFS timings by a variable amount. I'm using CompCar data from MCI and I think that's where the problem lies - timing info is not available there so I'm using the time of packet receipt. Obviously there's going to be a little lag like that, but I didn't think it would amount to this much. Oh well.

I plan to switch it to using OutSim data instead and I think that'll tighten it up.

Another problem is the layout names not being present in AXI: There is no way for a client side InSim to know the name of a layout, and this makes any client system dependent on user-input for changing to anything other than an 'official' track. It's blocking this and other projects, so I've made it an Improvement Suggestion.
Quote from Bokujishin :I like the general idea of what you're proposing here, but I think there are some bits that can be removed for optimization:

Yeah, it was just the rough draft. How would you pair it down, showing the struct definition? My goal was mostly to give the most amount of detail possible for not only telemetry data for overlays, but also as a packet you could send to a Web Socket, so that the Web Client would be able to predict, by Dead Reckoning, the vehicle on a map interface.

The trick that Scawen would need to pull, is to send one of these packets every single time each car enters a new node. With the engine running at 1000Hz now, it should be decently accurate. I also wanted to say that I'm 100% OK with getting 15000+ packets per lap for a full server of just these types of packet data. It's over half a MB per lap, but again, I'm 100% cool with this. If this is something that Scawen is willing to add to the InSim interface, it was make timing, telemetry, and location tracking extremely easy. Kind of like an Ultra High Resolution Timer interface. It removes the jitter problem entirely, because the engine would be the one providing the time stamp information directly. It doesn't matter how long it takes to get to the InSim server at that point.

FGED GREDG RDFGDR GSFDG