Minisky 101: Basics¶
Minisky is a discrete-time simulator. At each timestep \(i\), it takes the current state \(x_i\) and computes the next state \(x_{i+1}\) with some timestep \(\Delta t\): $$ x_{i+1} = f(x_i, \Delta t). $$ This page focuses on how the state \(x_i\) is represented internally and how the timestep \(\Delta t\) controls simulation time.
State¶
minisky stores most core aircraft state in what we call "traffic arrays", inside minisky.Traffic.
Unlike typical game engines that model a list of objects, minisky uses the struct-of-arrays (SoA) architecture, where the attributes of an aircraft (e.g. its position, speed, altitude) are the "columns" of a table.
Take a simple example of creating an aircraft:
from minisky import MiniSky
with MiniSky() as runtime:
runtime.traffic.cre(...)
Here, the attributes of the aircraft (runtime.traffic.{callsign, lat, lon...}) are stored as separate numpy arrays. An aircraft is identified by its row index in these arrays. Whenever minisky creates, reads, updates or deletes aircraft, these arrays are kept aligned at all times.
This SoA architecture is also used in many minisky subsystems, including autopilot, performance modelling and conflict detection.
Stepping¶
When you execute MiniSky.run(), the runner repeatedly calls Simulation.step(), which updates the state and advances the simulation time by the timestep simdt \(\Delta t\). Conceptually:
runtime.simulation.simdt = 0.5 # (1)!
for _ in range(4):
runtime.simulation.step() # (2)!
wait()
print(runtime.simulation.simt)
# 2.0
- Each step represents half a simulation second.
- Four steps advance two simulation seconds.
Here, the time that wait() depends on the playback speed defined by the runner.
runtime.runner.speed = 10
await runtime.run()
With these settings, the runner targets 10 simulation seconds per real second, i.e. one step every 0.05 real seconds.