Repository navigation
Releases
1.0.0
The first desktop release. GT7 Datalogger now comes as one download for macOS (Apple
silicon), Windows and Linux, with no Docker, Python or terminal: it runs the same
server and shows the same dashboard in your browser
(Desktop app). Docker, Raspberry Pi and source
installs get the same server changes. If you reach the logger through a reverse proxy
by a DNS name, set GT7_TRUSTED_HOSTS before upgrading (see Security).
The macOS app is signed and notarized. The Windows files are not code-signed yet, so
SmartScreen warns before the installer runs (More info, then Run anyway), and
the Linux files have no .asc signatures yet; check either against SHA256SUMS.txt.
Added
-
The app can tell you a newer release is out (#196). With Check for updates
on, the server asks GitHub Releases once a day which release is the latest and, if
it is above the running version, reports it (version and link) in
/api/admin/statsunderupdate;POST /api/admin/update-checkasks straight
away. Notify only: nothing is downloaded. Versions are compared by full release
precedence, so0.9.0-rc.1<0.9.0-rc.2<0.9.0and a lower or equal version is
never offered;update_channelstableasks for the latest release only, and
prereleasetakes the highest of the last twenty, release candidates included.
The check is off by default (the desktop launcher turns it on), applies the moment
it is switched, remembers its answer and its ETag across restarts so a restart does
not ask again, is silent when it fails, and waits for GitHub's rate limit to reset
when it is told to. The configuration docs now list everything the app fetches on
its own: the car list, the first-start track pull and this. -
A first-run wizard explains the network prompts before they appear (#191).
Opening the app with setup not yet completed (the desktop app, or any install
that has recorded nothing) lands on#/welcome; a bookmark or a deep link is
never taken over, and an existing install with sessions keeps its start page. In
the desktop app it first says how the app works: it records in the background,
with its icon in the menu bar or the notification area, and this page is its
dashboard in the browser, which can be closed at any time. It asks PlayStation
or Try the demo: the demo stays on the simulator and offers
its scenarios. The PlayStation path says what this computer will ask first (the
Windows Firewall prompt, the macOS Local Network alert, the firewalld/ufw hint on
Linux), then switches to the PlayStation source, which is what raises the prompt,
searches for the console and saves the address, with manual entry and the PS5
menu path when the search finds nothing. Listener and heartbeat errors become
specific help (another program on UDP 33740, Local Network denied, a firewall, GT7
not running). A live check waits for telemetry and offers packet format A when
packets arrive but do not decode. The desktop app then asks whether phones and
tablets may connect; finishing turns LAN access on or leaves it off, shows the
admin token it created, a QR code of the dashboard's LAN address and this
computer's own address to bookmark. Finishing,
and Skip setup, complete setup, which starts recording. -
The UI knows how it is installed (#190). Advice that assumed Docker now
follows the install kind from/api/health: the unreachable page, the mDNS and
127.0.0.1 help under Settings › Phone app, the Race Engineer note, and the
Advanced line for an environment-only setting (the desktop app restarts its
server by itself). A new Settings › About shows the version, how it runs, the
data folder and log file, the update status, and that the app is unofficial and
not affiliated with Sony Interactive Entertainment or Polyphony Digital. With LAN
access off, Settings › Phone app and the layout builder say so, with a Let my
phone/tablet connect switch, instead of showing an address nothing can reach.
Copy buttons work on a plain-HTTP LAN address, where the clipboard API does not
exist: they fall back to the old copy command, and failing that select the text.
A listener that could not start, a failing heartbeat and laps that could not be
saved show in a strip under the status bar on every view, and a banner says when
a newer version is out, once the server reports one. A request the server's guard
refuses (LAN access off, an untrusted Host, a cross-site request) shows the
server's reason instead of "admin token rejected", and on the desktop app
Settings › Access says the computer's own browser never needs the token. -
One data folder for everything the app writes (
GT7_DATA_DIR). The database,
track bundles, survey logs, sync state, backups and the installation's identity all
sit in one folder; unset, that is the database's own folder, exactly as before. With
it set the database defaults togt7.dbinside it, so a launcher can place an
install with one variable instead of a working directory (#178). -
One version, read the same way everywhere. The version the server reports over
mDNS, sends the sync service and stamps into compiled track documents now comes from
a committedbackend/app/_version.py; the three readers it replaces disagreed in a
dev venv.python scripts/version.py bump X.Y.Zrewrites the five places a release
touches andcheckfails when they drift apart (#179). -
A log file, when you ask for one (
GT7_LOG_FILE). Lines go to a file as well,
rotated at 5 MB with four old files kept, uvicorn's own lines — a port already in
use, a failed start — included. Docker users can set
GT7_LOG_FILE=/data/logs/gt7.log(#180). -
/api/healthsays what is answering. It returned only{"status": "ok"}, so a
launcher, a phone or a second copy could not tell this server from anything else on
port 8000. It now addsapp,version,install_kind(desktop,dockeror
source) and the database's schema revision beside the newest this build knows (#184). -
A first-run setup marker.
GET /api/admin/setupreports where setup stands and
POST /api/admin/setup/completestores the marker and starts recording. With
GT7_FORCE_SIM=firstrun(set by the desktop app) the server starts on the simulator
with mDNS and recording off until then, so no network prompt appears before the
first-run wizard explains it (#184). -
LAN access is one live switch (
GT7_LAN_ACCESS). Off, only the computer
running the server can use it (other devices get 403lan_off) and mDNS stops
announcing it; on — the default, as before — phones and tablets connect. It is
checked on every request, so changing it restarts nothing and drops no lap. Finishing
first-run setup with LAN access on creates an admin token when there is none, which
other devices then need for changes; reads and the live stream stay open. In the
desktop app the computer's own browser needs no token (#195). -
The database is copied before a new version upgrades it. When a start finds the
schema behind this build's, it first copies the database, with SQLite's backup API,
tobackups/pre-upgrade-<time>-<revision>/in the data folder, beside a
manifest.jsonsaying which version made the copy, the revisions it goes from and
to, how many sessions, laps, tracks, settings and layouts it holds, and its
quick_checkverdict. The copy is written as.partialand takes its name only once
the manifest is in it, so a start killed half way never leaves something that looks
like a good backup. One copy is kept, three on the desktop app. Without 2.2 times the
database's size free, the start logs how much is needed and stops rather than
upgrade with no way back (#182). -
Setup asks about units and background requests. The first-run wizard has a
Preferences step: Race Engineer units, chosen from the browser's region (miles in the
US, UK, Liberia and Myanmar), and switches for the daily update check and the car-list
refresh. The car-list refresh had no off switch at all;GT7_CAR_REFRESH=false(Settings
› Advanced) now keeps the bundled inventory until you press Update car database (#191). -
The desktop app's launcher and build (
desktop/). One executable in three
modes: a supervisor with a menu-bar item (macOS) or tray icon (Windows) that holds
the port and runs the server as a child of itself, restarting it if it crashes or
stops answering;--headlessfor systemd; and--server, the child. One app per data
folder, a saved port with 8001–8009 as fallback, "already running" when Docker holds
the port, Start at login, Open data folder and logs, a phone/tablet toggle, and
Windows firewall repair. The app, its shortcuts, the macOS menu bar and the Windows
tray carry the GT7 Datalogger mark, made fromdesktop/icons/source/by
desktop/icons/make_icons.py.desktop/scripts/build.pyfreezes it with PyInstaller and
desktop/ci/smoke.pychecks a build end to end; nothing of it reaches the Docker
image (#192, #193, #194). -
Windows and Linux packaging for the desktop app. Windows gets an Inno Setup
installer,GT7-Datalogger-Windows-x64-Setup.exe, which installs for everyone by
default (or "for me only") into the same folder every version, so the firewall
rules it adds for the app (UDP and TCP, local subnet, private and public networks)
survive updates. It closes a running copy before updating, after saying a session
being recorded will be closed; offers Start at login and Check for updates daily;
and leaves the data folder alone on uninstall. A portable zip ships beside it, and
desktop/windows/package.ps1 -Signsigns every .exe, .dll and .pyd and then the
installer and uninstaller, through Azure Artifact Signing, SignPath or a .pfx, with
a Defender scan that fails the build on any detection. Linux gets an AppIm...
0.9.0
Added
-
A synced session gets its circuit even when its first lap was not a whole
one. The session is announced to the sync service with its first lap, and the
circuit is identified from a whole lap; a race's grid start, a pit-out lap or
logging that began mid-lap is not one, so the summary went without the circuit
and the session stayed unnamed on the service even after the next lap named it.
The circuit is now owed once it is known: it goes on its own straight away to a
service that takes that (track_on_patchamong itssessionshints, from
gt7-sync-service #37), and always with the drive's totals, which an older
service ignores. The service fills a session's circuit only while it has none,
and so does the logger, so a session keeps the circuit it opened with. A server
that refuses the circuit costs the session nothing: the laps and totals still go. -
Sportsland SUGO is recognised. GT7 added SUGO International Racing Course in
its October update, after the shipped catalog was generated, so sessions there stayed
unnamed. The catalog now carries it (42 tracks, 122 configurations) and the shipped
signatures include it, built from the lap zetetos/gt-telemetry published; the
re-vendored file also brings the surveys gt7-datalogger-track-data has merged since
the last copy: 26 configurations now take their geometry from a surveyed road
instead of one captured lap, and 80 are recognised in all (#170). -
Spanish, Brazilian Portuguese and Japanese. A language menu in the status bar
(and on/engineer) sets this device's language; Auto follows the browser. The
Race Engineer speaks and captions its callouts in it, with that language's own
wording rather than a translated English sentence ("Tiempo de vuelta, un minuto
32,5", "ラップタイム、1分32秒5"), and its Voice panel can pick a different
Callout language from the interface's. Voices are listed for the callout
language; a device with none for it speaks the English and keeps the captions
translated. The whole interface is translated — every view, the dash and the OBS
overlays — except the setting names in Settings › Advanced, which the server
sends in English; dates follow the language too. A copied overlay or dash URL
carries the language (&lang=es), since an OBS source has no menu. Callout
message_argsnow describe the English sentence exactly —nullfor a clause it
leaves out, plus the sector-voice precision, corner names and spoken units — which is
what the browser words a translation from; the Analysis coaching notes carry the same
message_key/message_args, and a Tracks layout suggestion its reasoning as
match(#85). -
Backup: one file for a whole install. Settings › Data › Back up makes
gt7-backup-<date>.zipfrom the parts you pick: Data (every session and lap, as
a copy of the database), Surveys (track bundles, corrections, survey logs, car
widths and the install's source id), Config (saved settings, without
credentials), Dash/OBS (overlay and dash layouts) and Tokens (the webhook URL
and sync token, off by default). Amanifest.jsonrecords the app version, the
database's schema revision and what each part holds. The database is copied a few
megabytes at a time, so the recorder keeps saving laps while a backup is made, and
compacted, so deleted laps do not travel with it. The archive is laid out like the
data folder, so it can also go back by hand. The lap exports that used to sit under
Back up are now Export laps (#161). -
Restore a backup. Settings › Data › Restore uploads a backup, lists the
parts it holds with what each would do here, and applies only the ones ticked.
Data replaces the recordings: the backup's database is unpacked, checked,
upgraded if an older version made it, and swapped in with the recorder paused only
for the swap; the replaced database is kept asgt7.db.before-restore-<time>, and
this install's saved settings and layouts stay. A backup from a newer version is
refused for Data, with the version it came from. Surveys merge as an import
does, adding corrections, survey logs and car widths where this install has none;
Install identity (source-id.json) is its own switch, on by default only for
an install with no recordings yet. Config merges through the same checks as
Settings › Advanced, with the per-machine settings (source, console IP, ports,
simulator scenario, LAN announcement) listed but off by default. Dash/OBS
merges by name, and Tokens go intosecrets.jsononly when ticked. Laps are
then re-judged against the surveys here in the background (#161). -
Sector times from the Race Engineer. With Sector announcements on
(Settings › Race Engineer, off by default), the engineer calls each sector as
you complete it, against the coaching reference: the delta, the sector time
or both, sector by sector or cumulative. It needs the circuit's sector
boundaries, authored in Tracks, where the checklist now has a Sectors
row. The same switch sets how many decimals (1 to 3) and whether rounded or
truncated, for sector and lap time announcements alike. Contributed by
@Marstiphal (#165). -
Sector times on the Analysis lap strip. Each sector on the lap strip shows its
time, for the compared lap or the reference when none is compared, with the delta
to the reference beside it; zoomed to a sector, the map's callout shows the same.
The times run the lap's own clock from the line to the line, so they add up to the
lap time. A lap whose clock did not run the whole lap (a recording joined part way
round, a stop the logger skipped) shows none, and a circuit without authored sectors
is timed in thirds of the lap, marked~. Contributed by @Marstiphal (#166). -
Sector comparison and theoretical best. A new Analysis panel beside Sector times
sets the reference lap's sectors against the session's best of each, with the time
the reference lost to the best in every sector, and adds the best sectors up into a
theoretical best lap and how far it is under the reference. Bests come only from laps
that count; a reference from another session has no comparison, since the bests are
this session's. Contributed by @Marstiphal (#167). -
Replay Recording for leaderboard replays. Some GT7 leaderboard replays stream
without the on-track flag, so the recorder skipped every packet and the lap never
reached replay salvage. A Replay Recording switch on the Live dashboard (off by
default, and off again after a restart) records them anyway: replay packets are
sampled while the car is moving, a stop in the replay keeps its time, and paused,
loading and idle-menu packets are still skipped. While it is on, every lap completes
only through replay salvage, so GT7's own lap time has to vouch for it, and the
sessions it opens are excluded from bests. Turn it on before the replay starts and
off before driving; it applies to new sessions, not the one already open
(POST /api/control/replay-recording, admin). The switch is translated, and a toggle
the server refuses (a protected server without the admin token) shows why in a toast.
Contributed by @Marstiphal (#171, #87).
Fixed
-
Sector times put time the recording missed into the last sector. Analysis ›
Sector times shares GT7's lap time out by the lap's own clock, and the last sector
takes the remainder. A lap the logger joined part way round, or whose clock froze
through a stop or an outage it skipped, has a clock that falls short of the lap time,
so the sectors where it lost time read short and the last sector long by everything
missed — up to six minutes on recorded laps. A lap whose clock does not agree with its lap
time, within the window a salvaged lap has to meet (500 ms, or 0.5 % of the lap),
now shows no sector times. Partial laps timed line to line keep theirs (#168). -
The Race Engineer said "ten tenths" for a gap of 950–999 ms. A lap delta,
personal-best gain, pace drop or corner loss chose tenths before rounding, so just
under a second came out as "ten tenths slower". It now rounds first and says "one
second", like a lap time carries 59.96 s to the next minute.
0.8.1
Added
- Front wheel angle and wheelbase on every lap. Laps recorded on packet C
carry a new optional sample column,wheel_angle: the mean of the two front
road wheels' steering angles in radians, as GT7 broadcasts them — what the
tyres did, besidesteer, what the steering wheel did. Each lap also stores
the car'swheelbase_m. Together they give the yaw rate the car would have
with no slip (speed × wheel_angle ÷ wheelbase), the reference for measuring
understeer and oversteer, and show where the front wheels stop turning while
the steering input keeps rising. Both travel in lap export and import, the
session archive and (wheel_angle, as "Front Wheel Angle") the CSV export;
the lap file stays at version 2. Laps recorded without packet C, and every
lap stored before, have neither. The simulator sends both. Adding them
migrates the database (0013), so an older version cannot open it afterwards
(#154). - Understeer and oversteer against the front wheels. Analysis has three new
channels for packet-C recordings: Front wheel angle, beside Steering, where the
input rising while the wheels go flat is the steering lock; Kinematic yaw, the
rotation the front wheels asked for (speed × wheel angle ÷ wheelbase); and Yaw
balance, the measured yaw rate minus it — below zero understeer, above zero
oversteer. The two are worked out on the server from the full 60 Hz ticks, not the
charts' resampling, and are absent from laps without packet C. The corner report
card gains Yaw ÷ wheels (under 1 understeer, over 1 oversteer) and, where a
corner ran out of lock, Lock m; the lap analysis document carries both under
each corner'sbalanceand the car'swheelbase_m. No learned baseline is needed,
so they are there from the first lap (#155). - Engine torque. Laps recorded on the
~packet or wider carry a new optional
column,engine_torque: the engine torque being delivered, normalized, as GT7
broadcasts it — negative on the overrun, 0 with the clutch in. It is in the
Analysis channel picker (Engine) and the CSV export. The live packet also decodes
the powertrain byte (powertrain_flags) and the energy-recovery flag
(energy_recovery_available). All three meanings come from the community's
decoding of the packet, not from Polyphony (#150). - Chase a lap. Race Engineer coaching can compare against a stored lap of
the circuit you are driving instead of your session best: Coaching
reference on/engineer, then Chase a lap. A quicker lap of your own
takes over, falling back to the chosen lap if it stops counting. The choice
survives race restarts and ends on a new car or circuit. New WebSocket
messageset_engineer_reference;race_engineer_statusgains
reference_mode,reference_lap_idandreference_time_ms. Contributed by
@Marstiphal (#152). - Split view in Analysis. Split, beside Follow on the race line map,
puts four charts of your choosing next to the map on a wide screen, sharing
its cursor and zoom; the full chart stack stays below. Remembered on this
device. Contributed by @Marstiphal (#156). - Every lap's values on the race line map. Pointing at the reference line
shows a readout there: the time diff and each chart panel, one column per
lap the map shows, as the chart tooltip lists them. It works in the
full-screen map too, where the charts are out of sight, and Hover
readout in the Layers menu switches it off. Contributed by @Marstiphal
(#159).
Fixed
- A pit stop before the timing line split a race into several sessions. Where
the pit box comes before the line (Montreal), the car rejoins on the lap it stopped
on. The stop was salvaged as a finished lap whenever the in-lap's length matched a
time GT7 still reported, which left a phantom lap carrying an earlier lap's time,
and the session then ended because the lap counter had not moved on: one 12-lap
race with two stops became three sessions. A lap salvaged during a stop is now held
until driving resumes. The same car on the same lap was a stop mid-lap, and the lap
goes on to complete with GT7's own time; the next lap continues the session as
before; anything else, or a stop longer than 90 s, emits the salvage as it used
to be at once. A held salvage is saved when the server shuts down (#153). - mDNS pointed phones at a port they could not reach under
./dev.sh. The dev
script ran uvicorn without--host, so the API listened on 127.0.0.1 while the
advertisement and the Phone app QR code gave the LAN address. The dev script now
binds every interface, as Docker andpython -m app.mainalready did; a server
bound to loopback only is not advertised, and Settings › Phone app says why
instead of showing a code that would not connect (#151). - Brake applied is labelled as what it is: the brake actually applied after ABS,
which GT7 sends in live driving only (#150). - Follow zoomed the race line in on a hover. The Follow camera tracked any
cursor, so with Follow on, moving the mouse across a chart dropped the map
into a close-up — constantly in split view, where the charts sit beside it.
It now tracks the playhead only, as its tooltip always said (#157).
Security
- The webhook URL and sync token are no longer stored in the database. Both are
credentials, and the database is the file people copy when asked for their data.
Settings now saves them insecrets.jsonbeside the database, created readable by
its owner only; every other setting stays in the database. On the first start of
this version, values an older version saved are moved there and wiped from the
database file, including the bytes a plain delete would have left behind. Nothing
changes for sync or notifications, andGT7_SYNC_TOKENandGT7_WEBHOOK_URLin the
environment work as before. Settings › Data links to a new
Sharing your data section on what to
send instead of the database (#94).
0.8.0
Added
-
Sector times. (#22) A new Analysis panel splits every lap of the
session at the circuit's sectors, the best of each sector and the best lap
in purple. Sector boundaries are placed on the Tracks page beside the
corners — each one is where a sector ends — by clicking the map, or from
the sector times a race screen showed for one of your laps, which say
where that lap was when each sector ended. They are stored in the track
bundle as an optionalsectorskey, so they travel with export, import
and sync. A circuit without them is split into thirds of the lap, the
boundaries taken from the session's quickest lap as places on the road
and found on every other lap, so a wider line does not move a split.
GET /api/analysis/sectorsserves the figures; the comparison reply
carries the sectors on the reference lap's axis. Track sync sends the
boundaries with the bundle; a server that does not keep them yet drops
the key, and once it advertisesmax_sectorsthe next Sync now
re-sends every bundle that has some, so none placed earlier is lost. -
Every setting in Settings. (#140) A new Advanced section lists every
setting the server has, each with its environment variable, default, where
its value comes from (set here, the environment, or the default) and
whether a change applies at once or at the next start, plus a reset
that hands it back to the environment. The stream rate, CORS origins,
UDP ports, simulator scenario, sync live rate, car and track files and
the admin token can now be changed without editing the environment;
only the bind host, port and database path stay environment-only.
GET/PUT /api/admin/configserve it, and the configuration
reference's tables are generated from the same list, so the documented
defaults are the shipped ones. -
Live client protocol. (#141) A client of
/ws/livecan open withhello
and choose its own telemetry rate (up to the full 60 Hz), afullframe
carrying every packet field, and which events it receives. The server
answers withwelcome(protocol version, server version, capabilities).
This is what the phone app will use. Clients that say nothing,
the browser UI included, get exactly the stream they always did. The
protocol is published as JSON Schema under
docs/reference/schemas/ws/along with the REST API'sopenapi.json,
both generated from the server's models. -
Phone discovery and pull. (#142) The server announces itself on
the LAN over mDNS as_gt7dl._tcp, carrying the protocol version
(GT7_MDNS, on by default, switchable live). The new Phone app
section in Settings shows whether it is being announced, and the server's
address as a QR code for networks where multicast does not reach.
GET /api/sessions/changes?since=returns the sessions changed since a
revision and the ids of every session that still exists, so a client
keeping a copy pulls only what changed. Every write to a session or its
laps bumps the session's newrevision. -
Two receivers at once. (#144) Documented: the console streams to
every client sending heartbeats, so two dataloggers, or a datalogger and
the phone, can capture the same drive. -
Gearing sawtooth. (#107) Under the Analysis Gearing panel's ratio
table, a chart of speed against rpm: a line per gear from standstill to
the redline, and the lap's own samples over them coloured by gear, so
where each gear was shifted, how far the revs drop across each ratio gap
and whether top gear is ever reached all read off one chart. The panel
follows the corner report card's focus lap, and draws the reference
behind in grey when its gearbox differs. GT7 sends no final drive, so
the lines are fitted to the lap's own samples; GT7's top speed stands in
when a lap gives no consistent fit. -
Suggested gear, recorded and judged. (#108) The gear GT7 suggests on
its HUD, the → 3 the Live view's gear widget already showed, is now
kept on every lap as thesuggested_gearsample column. It holds 15
where GT7 suggested nothing, and a lap on which it suggested nothing at
all keeps no column. The Analysis Gear panel draws it dashed beside
each lap's gear and shades the corners taken a gear higher, and the
playback strip shows it beside the gear. A corner is judged at its
slowest point against the last gear GT7 suggested on the way in. The
Tuning panel counts the reference lap's corners a gear high, and
the Sessions lap table gains a Gear high column for every lap,
measured against the session's quickest counting lap.
corner_reportrows and the lap analysis document'sspeedblock carry
suggested_gearandgear_high,GET /api/analysis/gear-choiceserves
the per-lap counts, and the CSV export has a Suggested Gear channel. The
lap file's version is unchanged. The simulator now suggests gears, and on
about half its laps holds a gear too many through one corner. When GT7
fills the suggestion has not been checked against a console yet.
Changed
- CORS origins apply live.
GT7_CORS_ORIGINSis read per request, so
changing it in Settings takes effect without a restart. The API reference
no longer claims CORS is open to every origin; it never was by default. - The Analysis race line map. (#102) The reference line is coloured by
a lens — Inputs, Speed, Gain / loss against the compared lap, Tire temp
(the hottest tire, on the Live tiles' 55 / 95 °C bands) or F−R temp
(front minus rear, even within ±3 °C, full colour from 8) — and drawn as
one line rather than a trail of dots. The lens is remembered on the
device and carried in the Analysis link aslens=; the tire lenses are
greyed out on laps recorded before per-corner channels. The header's row of
toggles is a Layers menu with a switch for everything the map draws
besides the line: the surveyed road, corner numbers, the other laps with
their apex speeds and brake points, and the markers. Corner numbers are
bare labels beside the apex. Zooming: drag along the line, ⌘/Ctrl +
wheel about the pointer,[and]through the corners, Esc or a
double-click out; the window animates, a locator inset shows where on
the lap it is, apex badges give every lap's minimum speed through a
corner in view, and a callout says what the compared lap did there. A
corner's window now starts where the reference began braking for it. - The lap strip. A bar under the map — the lap unrolled, coloured like
the line — carries the track's sectors as S1…Sn buttons (replacing the
thirds buttons) and the corner numbers, and the zoom window to drag,
resize and slide. A compact copy is pinned over the charts, so the zoom
stays in reach with every channel on. The map's borders are drawn as
simplified polylines instead of a segment per metre. - The corner report card reads in track order by default, with a sort
switch for biggest loss first.
Fixed
- Speed at redline on laps recorded before 0.7.0. The Gearing panel
read under 6 km/h for them: it took the stored top speed as top gear's
speed at the redline, and those laps hold a ratio there (#128). The
speeds now come from the same fit as the chart (#107), and the top speed
is hidden where it is not one. - Auto-discovery finds the console again on a new address. With no console
IP set, the heartbeat followed the first console that answered and kept going
to that address for good, so a PlayStation that came back from rest mode, a
router reboot or a lease change on another DHCP address never streamed again
until Find console was pressed. Once the stream has been quiet for 5 s
the heartbeat is broadcast again (and still sent to the last known address),
and whichever address answers takes the stream over. With a console IP set
the heartbeat still goes only to it, and Find console still keeps a second
console it wakes out of a live stream. - Race opening laps no longer inflate consistency. Confirmed lap 1s from
standing and rolling starts are excluded from bests with the reason race start,
so the existing consistency calculation leaves them out too. Full-lap geometry
and telemetry are preserved, and the exclusion can be overridden. Existing
sessions with a recorded race result are updated once, preserving manual rulings. - A pit stop no longer splits a race in two. (#136) Going through the
pits could close a completed lap and end its session at once, so one
continuous race became two sessions. The decision now waits until driving
resumes: the same car carrying on into the next lap keeps the session,
anything else starts a new one. Replays still split, and a race finish
seen while the car is inactive keeps its result in the original session.
0.7.0
Changed
- The session being recorded can be deleted once recording is paused.
(#130, contributed by @NikiforovG) Until now it could not be deleted until
the next session started, which is never, if you stop driving and switch
the console off. Pause recording with ● REC and it can go. Its
unfinished lap goes with it, and recording again starts a fresh session.
While recording is on it stays protected, through pauses, pit stops and
connection drops alike. Deleting now shares one lock with packet
processing, lap saves, best-lap rulings, the recording switch and Log
lap now, so a delete waits for a lap still being saved rather than
racing it. - Web UI usability revamp. Admin is renamed Settings. It now has a
section rail, a health strip, and a single Apply bar that sends every
buffered edit in one request, replacing seven separate Apply buttons. The
overlay builder moves to its own Overlays tab. Sessions becomes a
master–detail view with a lap-time chart, a bulk-action bar and
auto-hidden constant columns. Tracks gains a "Needs you" list and a
readiness checklist. Live is denser: shift lights, a Δ card and a pinned
in-progress lap./engineerand/dashare rebuilt for touch. New
route-level pages cover 404, a locked server, server errors, an
unreachable server and a silent console. The status bar shows telemetry
as a labelled dot. The fastest lap is purple everywhere.#/adminlinks
still work. - The simulated car has a heading. The simulator sent every packet with
a velocity of(speed, 0, 0)and no orientation, whichever way the car was
going. It now sends the velocity along the path it is driving and the
car's orientation as GT7 does, turned from the path by a body slip that
points the nose out of a slow corner and into a fast one, so the body slip
channel can be seen in sim mode.build_packettakes anorientation. - The Race Engineer's coaching compares laps by place, and knows which
corner a braking zone belongs to. (#110) Two things were wrong with
"you braked twelve metres earlier into turn five". The laps were compared
at equal distance, and a lap's own distance drifts from another's by a
median 2.9 m and up to 64 m — the Analysis view has lined laps up by
position since 0.6, the engineer had not. And the braking point was "the
first brake application in the 250 m before the corner's entry", which
through a sequence is often the corner before's: on the best laps of nine
stored sessions it disagreed with where the corner's braking zone began at
41 of 105 corners. Coaching now puts every lap on the reference lap's
axis before comparing (off the event loop, at the lap boundary), and takes
what a lap did at a corner fromprocessing/corner_metrics— each brake
application belongs to one corner, the first apex its midpoint has not
reached, and a corner's braking zone is the one that took the most speed
off. The lap analysis document uses the same definitions, so the two
cannot disagree. Replayed over 21 stored sessions the number of coaching
notes is unchanged (120) and 59 are word for word the same; gone are the
likes of "you are braking early into turn ten, about two hundred thirteen
meters". Thresholds for what is worth saying are unchanged. - SQLAlchemy 2.1 or newer is required. The repository layer's
Select
andRowannotations follow 2.1's variadic generics (a five-column
projection isRow[int, int, int, bool, bool]; a row of any shape is
Row[*tuple[Any, ...]]). No query changed. - Compiled borders are smoothed. A border record sits on a 1 m grid, and
anedgerecord and astraddlerecord of the same kerb disagree by up to
a metre about where it is, so an ordered border stepped sideways wherever
the kind changed — nicks in the drawn line, and a road whose edge moved a
metre in a metre. The compiler now smooths each surveyed run with Taubin's
λ|μ pass, which does not pull curves towards their inside the way an
average does: against corners whose true border is known it stays within
0.35 m on everything from a 3 m kerb to an 80 m sweeper, where a ±8 m
moving average cuts 2 m off the kerb. Ends of a run stay where the survey
stopped, nothing is smoothed across a gap, no vertex moves more than
0.75 m from its record (undertrack_limits' edge margin), and the bundle
itself is never rewritten. Over the 24 circuits in the shared map: 3 103
kinks to 19, every gap span unchanged, coverage within 0.3 of a point.
compile_bundle(doc, smooth=False)compiles the evidence exactly as
recorded, and the document's newsmoothingkey says which it got.
Compiled format 3, so every stored compile is rebuilt once.
Fixed
- A lap's top speed is a speed. Its
gearing.top_speedwas written
fromtransmission_top_speed, which GT7 sends as a ratio (2.3 for a tuned
AE86), so the gearing panel's "est. @ redline" column read 1–2 km/h for
every real recording. It now comes from GT7's own estimate,
calculated_max_speed, on both the live and salvaged lap paths (#128).
The simulator had hidden this by sending 290 in the ratio field. Laps
recorded before keep the ratio in their stored gearing. - "Delete all recorded data" while recording no longer strands the next
laps. It cleared the tables but left the recorder holding the wiped
session, so the laps driven next were saved against a session that no
longer existed. It now takes the same lock and resets the recorder the way
deleting the current session does (#130), and recording carries on into a
fresh session. - The fastest lap is purple everywhere. The overlay and dash times
widget, the Sessions list and the Bests board still drew it in the accent
blue. On the Bests board a slower time's gap is neutral grey rather than
red. Afastestcolour joins the theme, with the same value as the
charts'FASTEST_COLOR. - The position widget no longer reads "P-1/0" outside a race. GT7 sends
-1 and a field of 0 in practice, time trial and menus. The widget now shows
a dash until there is a real position. - Pitting on a lap's last stretch no longer splits the race. GT7
reports the line crossing into the next lap while the car is in the pit
lane, where it is not "on track". The lap was salvaged from its GT7 time,
which is right, but every salvaged lap also ended the session. That rule is
for a replay whose stream breaks off, and here it opened a new session for
the rest of the race. The next lap number in the same car now keeps the
session going. A replay, where the counter jumps elsewhere or the car
changes, still gets its own session. - A deleted session's id is never given to a new one. SQLite numbered a
new session one past the largest id left, so after "Delete all recorded
data", or deleting the newest session, the next drive reused an old id.
The sync client still held a record under that id, so a race's laps were
sent into the deleted qualifier's server session and replaced its laps.
Session ids now use AUTOINCREMENT (migration 0011, a rebuild of the small
sessionstable). Deleting a session, or everything, now also clears the
sync client's record of it, and a session that opens always starts a
fresh sync record. - Analysis no longer sticks on a session that has been deleted. The tab
remembers the last session it showed. If that session had since been
deleted, a newer one never appeared until it was opened from Sessions. It
now falls back to the newest session with laps. - Menus, pit stops and pauses no longer merge or split sessions. (#120,
contributed by @NikiforovG) GT7 keeps streaming while the car is not being
driven, and those packets carry lap counters of their own: 0 or -1 in a
menu, 0 in the pits. The lap processor read them as it read driving. A
race that ended at lap 12 and went through a menu at -1 left the counter
parked there, so the next race's lap 1 was not seen as a reset and the two
races were recorded as one session; a pit interruption that read
6 → 0 → 6was seen as a reset and split one race in two. A car change or
a lap reset is now confirmed only by a packet that is on track, unpaused
and has a lap number of 0 or more. Inactive packets keep the last driving
lap and its buffer, and never open a session by themselves. Two ways of
finishing a lap still accept them: the time-validated replay salvage, and
the final lap's+1step from the same car, which records the race result
even when the finishing packet has already cleared the on-track flag. The
packet counter is followed through loading screens as well, so their
frames add nothing to the lap clock. A new session logs why it began. - The session being recorded cannot be deleted from under the recorder.
(#121, contributed by @NikiforovG) Deleting the current session removed its
row while the recorder kept its id: laps driven afterwards were saved
against a session that no longer existed, and a race's finishing result
was lost without a word.DELETE /api/sessions/{id}now answers 409
for the recorder's current session, paused or not, and the Sessions view
says why. The check is serialized with session creation, so a session
cannot be deleted in the moment between its row being committed and the
recorder adopting its id. Every other session deletes as before. The
current session becomes deletable once the next one has started, or after
a restart. - Analysis no longer shows the previous session under a new session's
name. Picking a session with no completed lap yet from the Analysis
dropdown — which is every session until its first lap is finished, so
exactly the one you have just started driving — changed the title and
nothing else: the lap chips, the map, the charts and the address bar all
stayed on the session that had been open, its laps re...
0.6.2
Added
-
Events and driver aids on the race-line map. (#103, #104) The map in
Analysis gains two layers, each with a toggle in its header that only
appears when the selected laps can fill it, remembered on the device.
Events marks where each detected lockup ◆, wheelspin ●, bottoming ✚
and kerb strike ✖ began, filled with the lap's colour so the same braking
zone going wrong on three laps is visible as three marks in one place.
Hovering a marker names the lap, the wheels and the severity in words
("slowest wheel at 62% of road speed"); clicking one zooms every panel to
the event with 60 m either side. Suspension events are detected per wheel,
so same-type events beginning within 6 m of each other draw as one marker
that names all the wheels. TCS rings every sample where traction
control was cutting power, in the lap's colour and hollow so the input
zone underneath stays readable; ASM does the same with squares and
starts off. Both follow the zoom, draw in the full-screen map, and the
key under the map lists only what is drawn in the current window. -
Session consistency, as a figure and a chart. (#112) Each session row
carries its consistency under the lap-time sparkline —±0.42 s · 0.4%,
the standard deviation of the lap times and that as a share of the median
lap, green under 0.5 % and amber from 1.5 %. Analysis gains a Lap times
— this session panel: every lap as a point against lap number, a line
through the laps that count, the median dashed and a one-sigma band
around it, with the spread, median and best beneath. Both are taken over
the laps that count toward bests and no others, the same verdict the bests
board acts on, and need three of them: a pit out-lap, a race's lap 1 and
laps excluded by hand are drawn hollow and left out of the figure, and a
lap too slow for the scale is pinned to the top edge so one out-lap cannot
flatten the rest. Points in the current comparison take their chart
colour, and clicking a point adds the lap to the comparison or removes it. -
Sessions and live sync. (#79) The sync client now sends the other two
data types the service takes, each behind its own toggle in Admin → Sync,
off by default and offered only when the server lists it. Sessions
sends your own laps as you drive — each lap as thegt7-datalogger-lap
document Export writes, against a session the service opens for the
drive — and the totals when the drive ends. The session is announced with
its first lap, not at its start, so a stint that never completes a lap
never reaches the server and the summary carries the circuit and its
layout id, which are only known one lap in. Laps queue in
data/sync-sessions.jsonwhile the service is away and flush in order
when it answers or at the next start; a lap ruled in or out of the bests
by hand is sent again with its new verdict; a session the service no
longer has (deleted in its portal, or expired) is closed here and said
so. Live holds one WebSocket to the service while the car is on track
and sends where it is about four times a second (GT7_SYNC_LIVE_HZ, no
faster than the server's ceiling): position, speed, gear, lap and lap
time, downsampled from the 60 Hz feed and never queued. The socket opens
on the first on-track packet and closes after five minutes without one;
the Sync panel shows the spectate URL and how many are watching. A
stream an administrator closes, or another logger on the account
replaces, is held for a quarter of an hour rather than fought over. The
Admin push now takes?type=and, blank, flushes every active type.
GT7_SYNC_SESSIONS,GT7_SYNC_LIVE,GT7_SYNC_LIVE_HZ. -
Discard a lap or the run while surveying. (#98) A spin that laid
border points across the gravel, or a boundary marked on the wrong side
for half a lap, used to be in the bundle for good — the merge only adds,
and the only way back was to out-vote the bad metres by re-driving them
twice. The Survey view has Discard lap, which throws away what this
run has surveyed in the current lap so far and carries on, and Discard
run…, which throws away everything the run has gathered; neither stops
the survey. Only this run's votes go: a metre another run or another
installation had mapped keeps that evidence and loses this run's vote on
it. Evidence the autosave already wrote is backed out of the bundle, the
circuit's laps are re-judged as after any bundle write, and the run's
JSONL records the discard so a log assigned later does not bring the lap
back. New endpointPOST /api/survey/discard; the survey status carries
lap,lap_votes,run_votesanddiscards.
Fixed
-
ASM coming on no longer reads as traction control. The
aidscolumn
is a bitmask and was resampled onto the comparison's 5 m grid by linear
interpolation like every other channel, so ASM switching on (0 → 2)
passed through1, the TCS bit, and the Throttle panel shaded a sliver of
TCS at every ASM onset; TCS together with the rev limiter (9) falling
back to nothing could pass through the handbrake bit the same way.aids
now takes the nearest sample's value, assurfacealready did. Stored
laps are unaffected — the per-lap TCS and ASM percentages were always
computed from the raw ticks. -
Where a circuit crosses over itself, both levels are surveyed and
judged. (#96) A border record was identified by its plan cell alone, so
Suzuka's bridge and the road beneath it were one metre of road: whichever
level was surveyed first kept the cell, the other's geometry was thrown
away, its votes were counted against the survivor, the compiled borders
zigzagged between the two, and a lap over the bridge was judged against
whichever level had won — flagged beyond the surveyed edge while on the
road. Bundle format v5: one record per metre per side per road
level, levels told apart by an elevation difference over 3 m. The figure
comes from the collected bundles — 0.72 m is the widest elevation spread
between same-side neighbours, 0.18 m between raw survey marks in one
cell; banking is 5.5 m across the road at Daytona but is never compared
within a cell. A record without elevation merges as it always did. The
border ordering refuses a next cell the road could not have climbed to,
the across-the-road pairing prefers its own level, each road quad carries
its elevation envelope (compiled geometry v2) and the survey judge places
a sample by its elevation: on the deck is on the deck, and a car on an
unsurveyed road under a surveyed bridge reads unknown, never off. Laps now
storepos_ybesidepos_x/pos_z. Laps recorded before this release
carry no elevation and are judged on plan alone, which at a crossover
means against both levels at once — they get the benefit of the doubt
there and cannot be re-judged any tighter. A v4 bundle upgrades untouched;
a crossover it merged before v5 has already lost its second level, which
only re-driving or a replace import (#93) brings back.stacked_cellsin
the bundle stats counts the metres where both levels are present. The
data repo's copy of the format moves to v5 alongside.
Changed
0.6.1
Fixed
- Laps are re-judged when a track's survey changes. (#91) A lap's
verdict against the surveyed edges —off_survey_count, and the
clean_lapit feeds — was decided once, when the lap was saved, against
the bundle as it was then. Fixing a bad survey afterwards changed nothing:
the only re-judge covered the live session, and only at the moment its
circuit was identified. Every bundle write now queues a pass over every
lap ever driven on the circuit, across sessions and across spellings of
its name: a survey run's saves (two minutes after the last one, so a
running survey's autosave never triggers a pass over the whole history),
a log assigned, a shared bundle pulled, an import, a rename (the label
left behind too), a delete — after which the verdicts go back to unknown
rather than keeping a judgement from geometry that no longer exists.
Identifying old sessions judges their laps too. The pass runs in the
background; Re-check laps on a Tracks row forces it and says how many
verdicts changed. New endpointPOST /api/track-bundles/{slug}/rejudge. - A survey-caused "not clean" flag can be cleared again. (#92)
clean_lapwas folded one-way: an excursion past the surveyed edge set it
false, and a re-judge that found none carried the stored value forward, so
a lap a bad survey spoiled stayed spoiled whatever was corrected later. It
is now derived from both counts every time — clean when GT7's surface
flags saw nothing and nothing ran past the surveyed edge — by one function
the live lap and the re-judge share. A lap the old flag left behind (count
zero, clean false) is repaired by the next pass.
0.6.0
Added
-
A sync client for contributing surveys. (#79) Pulling shared bundles
has worked since #47, but sending a survey back was a clone-the-repo-and-
open-a-PR job. Admin → Sync now takes a server address and a token
(the address field also accepts thegt7sync://…?token=…connection
string the service issues, split into the two), keeps the token on its
own, masked in the UI, sent only as aBearerheader and never logged,
asks the server what data types it accepts, and shows a toggle per type
under a master switch, all off by default. The one type this release sends
is tracks: every survey bundle whose official layout has been
confirmed — corner labels included — uploaded once it has been left alone
for ten minutes after a change. A running survey's once-a-minute autosave
keeps resetting that clock, so a run goes up once, after it stops and the
labelling that follows it; Sync now skips the wait. Nothing is re-sent
when the evidence has not changed since the server last accepted it. Uploads run in the background with retry and exponential
backoff; a refused document is remembered with its reason and not retried
until the bundle changes; a server that stops accepting tracks flips the
toggle off and says so. Each row of the Tracks view carries a chip — not
synced — confirm layout, queued, syncing, synced with the upload id,
rejected, error. The client is one transport plus one adapter per data
type, sosessionsandlivereuse the plumbing when their server halves
land. New settingsGT7_SYNC_URL,GT7_SYNC_TOKEN,GT7_SYNC_ENABLED,
GT7_SYNC_TRACKS; new endpointsGET /api/admin/sync,POST /api/admin/sync/test,POST /api/admin/sync/push;/api/track-overview
rows gain asyncfield. -
An in-app guide to the Analysis view. The view had grown to thirty chart
channels and a dozen features, and none of them said what they were: a panel
called "Tire spd / car spd" or a switch called "sync" was left to be
guessed at. A ? button in the toolbar now opens a short guide — a
Features tab and a Channels tab, a sentence or two each on what it shows and
how to read it, what a recording needs to have a channel, which channels are
on the chart now, a search across both, and a link from every entry to its
section of the documentation, which stays the long form. The channel picker
shows the same description on hover and links to the list. The descriptions
live beside each channel's definition and are required there, and a test
fails if any documentation link stops landing on a real heading, so the guide
can't quietly fall behind the app. -
Laps can be excluded from bests by hand. (#74) Only the partial-lap guard
could keep a lap off the bests, and it only ever asks one question — did the
lap cover the whole track? An off-track lap, a lap with contact, or an out-lap
the guard accepted all went on owning the session best and the Bests board.
The Sessions lap table now has a Counts checkbox per lap, with a reason
picker (off-track, contact, restart, dirty, pit-out) beside an excluded one,
andPATCH /api/laps/{id}behind it. The ruling works both ways — a lap the
guard wrongly called partial can be counted — and it sits beside the guard's
verdict rather than over it: the guard keeps re-judging the session as laps
arrive and can never overwrite a ruling, and clearing one hands the lap back
to whatever the guard said last. Every consumer of "does this lap count"
already filtered on one flag, which is now computed from both in SQL, so the
session best, the Bests board, the class benchmark, the session-summary
webhook and the Race Engineer's pace and coaching all follow a ruling without
a change of their own. On the session being driven, the live session best, the
Δ-best reference and the engineer's coaching reference move on the spot. The
Bests board says why a quicker time is missing (⊘ 1:28.422 off-track).
Rulings travel with exported lap files, and so does the guard's verdict: an
imported pit out-lap stays partial instead of becoming a best on the machine
it lands on. -
Time-synced ghosts on the race line. (#75) Every dot on the Analysis map
sat at the reference car's distance — right for reading the chart delta under
the cursor, but it never showed where the other car physically was at the same
moment, which is what makes a gap legible while a lap plays back. The map now
syncs on time by default: the reference dot stays at the playhead and
every other lap's dot is drawn where that lap had got to after the same
elapsed time, interpolated between samples so it glides rather than hops. A
slower lap trails, a quicker one leads, and the distance between them is the
gap. sync: Time | Position in the map header switches back to dots level
with the reference — which, now that compared laps are lined up by place (see
Fixed), sit on top of it apart from the line each lap took. The choice is
remembered per device, like Follow; the charts and the other cursor-synced
panels stay on distance.
Checked against the raw 60 Hz positions of 1,215 recorded laps (27.7 h), a
time-synced dot sits a median 7 mm and a p99 25 cm from where the car
actually was. Where GT7 reset a car mid-lap — a jump of up to 4 km in one
tick, which the 5 m distance grid cannot represent — the dot holds and then
jumps, at a moment estimated from the step's speeds, instead of sliding
across the infield. Every map dot is now placed at the exact cursor rather
than the nearest 5 m step (that was up to 2.5 m of false gap between the
reference and a time-synced dot), which also stops the Follow camera
hopping 5 m at a time during playback. -
Export a whole session as a ZIP. (#76) Backing up or handing over a
session meant clicking json on every lap. Export session (and
GET /api/sessions/{id}/export.zip) now packs every lap's export file and a
session.json— the session row as the sessions list gives it: car, circuit,
tags, note, bests exclusion and race result — into one archive. The lap files
are the single-lap exports unchanged, so each one imports today. Laps are
read, compressed and written one at a time, off the event loop, and the
archive spools to disk past 16 MB, so a long endurance stint doesn't have to
fit in a Raspberry Pi's memory. Importing the archive back as a session is
the follow-up. -
Cars describe themselves, and keep themselves current. (#57) The bundled
car data was 575 rows ofid,name— the app could print a name and answer
nothing else — and it only ever changed when somebody found the admin button,
which downloaded a third-party mirror of the same list. Cars are now a real
inventory, generated from Polyphony's own car list and shipped inside the
package: manufacturer, model year, Gr. category, drivetrain, aspiration,
displacement, power, torque, weight and dimensions, for 584 cars. It resolves
from the package rather than the working directory, so a fresh install names
every car it ships knowing about on its first packet, with no network and no
setup step. On top of that the app refreshes itself — on first run and weekly
after that, in the background, never blocking or failing a start — so cars
added by a GT7 content update arrive on their own. Being offline is not an
error: the bundled inventory is the floor, and a failed check simply keeps
it. A refresh only ever adds and updates, so the ten cars GT7 no longer
publishes keep their names for the sessions that reference them. Sessions
carry the manufacturer, year, drivetrain and aspiration on the row beside the
car name — the full published name, model year, category, drivetrain,
aspiration, displacement, power, torque, weight, dimensions and PP — and
sessions recorded before this, or before a car was known, are filled in as
soon as the inventory can answer for them. Laps deliberately store no copy:
every lap query already joins its session, so lap listings, the Bests board
and lap detail read the car across a join they were making anyway, and
GET /api/lapsgainedmanufacturer=anddrivetrain=filters on the back
of it. -
Circuits name themselves on a fresh install. (#58) Track identification
needed data only you could produce — a signature exists once somebody names a
circuit, a survey bundle once somebody maps one — so a new install recognised
nothing, and every session stayed unnamed with no hint that naming one circuit
would light up the track badge, the outline, category bests and corner labels.
The app now ships signatures for 78 GT7 configurations — nine from circuits we
have surveyed ourselves, the rest from published circuit captures — generated
offline and vendored so the first packet resolves with no network. They live in the sametrackstable as your own and are marked as
shipped, which is what keeps the two apart: a circuit you named always wins,
and a re-sync replaces every shipped row and none of yours. The Tracks view
labels them, and keeps undriven ones out of the table so 78 rows restating the
catalog cannot bury the rows that mean something. -
Reverse layouts are told apart from their forward twin. (#58) A reverse
layout has exactly the same bounding box and length as the layout it reverses,
so a shipped signature would have named reverse laps after the forward
configuration — and because bests key on the circuit name, forward and reverse
times would have pooled and competed for the same personal best. Each shipped
signature now carries the racing line in driving order, and a lap is walked
against it to see which way round it went. Not a clockwise test: signed area
does not survive a crossover, and Suzuka is a figure-eight. Measured over 896
recorded laps, this moved 10 laps of Deep Forest Reverse off Deep Forest
Race...
0.5.0
Added
-
The survey compiles into an actual track. (#38, #40) The bundle store
keeps border evidence as an unordered cloud of voted metre-cells; everything
downstream wanted them in order. A new compile pass walks each side's
cells into ordered polylines — 96–99 % of cells chain on the surveyed
circuits, generally into one closed loop per side — and derives the
centerline with measured road width and elevation, the road surface as a
contiguous quad strip, and the finish line. The Analysis map now draws that
road: on Deep Forest the fill went from 9 paired spans to ~940 quads,
because the compile pairs a border against the opposite border's curve
rather than hunting for a cell directly across (which almost never exists —
the two sides are surveyed on different laps). Unsurveyed stretches are
never invented: they are flagged as gaps, drawn dashed, and coverage is
now measured against the boundary itself — surveyed metres over total
boundary metres, gaps and loop-closure in the denominator — shown per
bundle in the Tracks view. The compiled document is derived data
(data/track-bundles/compiled/, rebuilt automatically whenever the bundle
changes), with JSON Schemas published for it and for bundle v4 in the docs.
Raw survey JSONL logs can now also be downloaded and uploaded, so a run
recorded on one machine can be replayed into another's bundles. -
Laps are judged against the surveyed edges, not just the surface
flags. (#41) The surface characters are blind to paved run-off — running
wide over asphalt reads as tarmac and stays "clean". When a circuit's
survey resolves at least half its road, each lap's positions are now also
classified against the compiled road surface: sustained excursions beyond
the surveyed border count separately (off_survey_count), appear as a
second figure in the Sessions table, and spoilclean_lap. Unsurveyed
ground never counts against a lap, and laps recorded before the session
was identified are re-judged the moment identification names it. -
The integrated clock now checks itself against GT7's. (#20) Elapsed
time is integrated from packet ids; packet C also broadcasts the game's own
live lap clock, decoded and until now unused. The processor tracks how far
the two drift apart within each lap — surfaced inGET /statusnext to
frames_dropped, with a log warning when a lap drifts past 100 ms.
Diagnostic only: it validates the time axis every chart is drawn from. -
The race-line map is something you can actually look at. It was a
360-pixel thumbnail that only the charts could drive, which is not enough
map for a 5 km circuit. Now: ⤢ opens it full screen, with scroll-to-zoom
and drag-to-pan (kept out of the rail, where a wheel that sometimes scrolls
the page and sometimes zooms is worse than one that always scrolls); a
corner strip under the map takes you to any corner in one click — as does
clicking the numbered circle on the map — and drives the shared zoom, so
the charts follow the map into the corner instead of the two disagreeing;
and the reference lap is drawn as a continuous zone-coloured line rather
than a dotted trail, because samples land every 5 m and that reads as a line
across a whole circuit but as scattered dots exactly where you have zoomed in
to look closely.The map is also no longer stretched. A metre across is now a metre
down — the axis ranges follow the plotting area's pixel aspect, so the shape
holds in the rail and at full screen alike. Letting each axis fill the box
independently distorted it by the circuit's aspect ratio: 8 % at Lago
Maggiore Centre and nearly 3x at Deep Forest. -
A surveyed circuit now recognises itself. Auto-identification only ever
matched against signatures written by naming a track by hand, which left a
hole big enough to make surveying feel broken: survey three circuits, never
use name track…, and the app has a metre-accurate map of each while still
failing to recognise the next session driven there — so the track badge, the
outline under the race line, category bests and corner labels all stay empty
on a circuit it has mapped in detail. Having surveyed a track and having
named it were two separate facts and nothing joined them.A lap with no matching signature is now compared against the survey
bundles, which are a strictly better fingerprint than a bounding box — they
are the road, not a rectangle around it. Matching asks the only question that
matters, did this lap drive on this surveyed tarmac?, and needs both a
coverage floor and a clear margin over the runner-up: two configurations of
one venue share tarmac, and a thin margin means the evidence does not
actually distinguish them, so the session stays unnamed rather than being
given a wrong name silently. Thresholds calibrated against 321 real sessions,
where the scores turn out sharply bimodal with an empty band to put the cut
in. A signature someone typed still wins outright. (#41) -
Sessions recorded before a circuit was surveyed can be named in bulk.
New sessions identify themselves, but history already on disk never got the
chance — which, for anyone who surveyed a circuit before this shipped, is all
of it. Tracks → Identify sessions re-runs the match over every unlabelled
session, reading each one's shortest usable lap rather than a whole
session's telemetry. (#41) -
The surveyed road now sits under the race line in Analysis. A racing line
only means something against the road it was driven on — whether the apex was
clipped, how much kerb was used, whether there was tarmac left on the exit —
and until now the map drew the lap floating in empty space. When the
session's circuit is named and surveyed, the map draws the track beneath
every lap: road surface, both borders, hand-marked walls in their own colour,
and the start/finish line. The geometry is compiled server-side
(/api/track-outline) and cached per bundle revision, because a bundle is up
to 50,000 border records and the browser has no business downloading a
circuit's whole survey history to draw a map. Circuits with no bundle answer
with an empty outline — never having been surveyed is the common case, not an
error — and the map falls back to exactly what it drew before. (#51, carved
out of #41) -
Traction circle (g-g diagram) in the Analysis side rail, from the
accelerometer GT7 has been broadcasting and this app has been throwing away.
Every moment of the lap plotted as lateral against longitudinal g, coloured by
input zone: how much of the ring gets used is the reading, and an empty
middle-left/middle-right is a car that never brakes and turns at the same
time. Compared laps overlay as faint dots, the cursor is synced with the
charts and the map, and the peak g in each direction is called out.The scale is checked, not assumed. GT7 documents neither a unit nor a
sign convention forsway/heave/surge, and a simulator cannot prove
what a real console sends — which is why #16 said to validate before
building any UI. So the app validates, per lap, against physics the same
lap already recorded: lateral againstv × ω(with the signed yaw rate
taken from the driven path, since the stored yaw column is absolute),
longitudinal againstdv/dt. A least-squares slope through the origin
recovers the unit and the sign at once, so the diagram comes out upright
whichever way the console counts. When a lap gave too little steady
cornering or braking to check, the panel says scale unverified rather
than drawing a confident-looking circle on an unproven scale. (#16) -
Steering-angle channel.
wheel_rotationhas been decoded since packet B
support landed and dropped on the floor ever since. It is now a stored column
and a chart panel: next to the yaw-rate trace it is what makes understeer
legible — more lock, no more rotation — along with corrections and
catch-and-release oversteer. (#15) -
ABS and TCS intervention, measured instead of inferred. The
~packet
format carries the pedal positions after the aids acted on them; plotted
against the raw pedal the two lines separate exactly where an aid stepped in.
New channels: Throttle applied, Brake applied, and the two gaps on
their own — TCS cut and ABS release. The aids bitmask only ever said
whether an aid was active; this says how much it took. (#18) -
Car category as a real dimension (#19). The class was already stored and
already filtered the Sessions list; it is now a server-side filter
(/api/sessions?category=Gr.3), it survives a session whose first packet
arrived in a narrower format (the session inherits its laps' class rather than
sitting outside its own filter), and Analysis shows the class benchmark —
the fastest full lap ever recorded at this circuit in the same category, the
gap to the reference lap, and a link to open it (/api/laps/best). Scoped by
class deliberately: a Gr.3 time and an N100 time around the same corners are
not the same achievement. -
Tracks view (new tab): the three sources of track knowledge, joined —
the DB's named tracks (which is what makes auto-identification work), the
survey bundles, and the bundled official GT7 catalog. Having one is not
having the others, and until now nothing said so: that gap is how a survey
ran for ~55 minutes attached to no circuit at all. Each row shows what is
present and what is missing (auto-ID, survey, official layout, metres
mapped, runs and contributing sources, elevation completeness — which only
fills in by re-driving — finish line located, corners labelled against the
official turn count) with the action for each gap beside it: assign an
orphaned run, rename (which merges when the new name is an exi...
0.4.1
Fixed
- Race Engineer voice now works from other devices on the LAN. Opening the
dashboard from a plain-HTTP address (http://<pi-ip>:8000— the normal way to
reach a Raspberry Pi) broke voice output: the browser only provides
crypto.randomUUIDon HTTPS or localhost, so registering the device and claiming
Use this device for voice output failed silently. The client id now falls back
tocrypto.getRandomValues, which works everywhere. - Phone navigation. The header nav drops to its own row on narrow screens
instead of clipping tab names — Sessions and Admin are reachable on a phone again. - Lap colours no longer collide in Analysis. Colours are keyed to the lap id
six-wise, so laps 6 apart — routinely the "latest vs best" pair — rendered
identically in the charts, map and chips. Laps compared together now always get
distinct colours; a lap only changes colour when it collides, and keeps its
canonical colour everywhere else. /dashand/engineerhave a way back to the main app (a small home link);
the OBS overlay stays chrome-less.#/overlaynow routes to the overlay like its#/dashand#/engineer
siblings, alongside the existing/overlayand#overlayforms.- Cross-view links into Analysis keep the chart channel selection (
ch=was
declared but never written), and the S1/S2/S3 zoom buttons' hover style renders
(its colour token was never defined).