Une chaîne de transformation CAKE© commence rarement par du calcul. Elle commence par une séparation : ce qui varie d'une mission à l'autre, et ce qui reste identique. CakeLand matérialise cette séparation en deux types de fichiers.
Le fichier .cpx porte les constantes de variation : masse de la planète, altitude de départ, vitesse, pas de temps. Le fichier .cps porte la mission : l'algorithme qui transforme ces constantes en trajectoire. L'éditeur les assemble dans cet ordre — constantes, ligne vide, mission — puis exécute le tout comme un seul programme. Changer d'orbite, c'est changer de .cpx ; le .cps, lui, a déjà été vérifié et n'a pas à être touché.
Voici la chaîne réduite à l'essentiel : trois constantes, puis une mission qui calcule la vitesse circulaire et la période d'une orbite basse à 200 km, et place douze points sur le cercle.
t09c03-a.cake?! --- constantes (.cpx) ---
=: mu=398600.4418%
=: re=6378.137%
=: alt=200.0%
?! --- mission (.cps) ---
=: r=re+alt%
=: v=Cake.sqrt(mu/r)%
=: periode=2.0*3.141592653589793*r/v%
|> "Vitesse circulaire (km/s) : " + Cake.toString(Cake.round(v*1000)/1000)%
|> "Periode (min) : " + Cake.toString(Cake.round(periode/60*10)/10)%
=: k=0%
$= (k<12) {
=: a=2.0*3.141592653589793*k/12%
Cake.emit(r*Cake.cos(a), r*Cake.sin(a), 0, k)%
:+ k=1%
}
Vitesse circulaire (km/s) : 7.784 Periode (min) : 88.5 [12 point(s) transmis au visualiseur par Cake.emit] → valeur finale : 12
<projet nom="t09c03-a" package="com.unibool.script" type="Library">
<classe nom="Root" visibilite="Public">
<methode nom="main" retour="void" visibilite="Public">
<bloc>
<declaration nom="mu" type="var" valeur="398600.4418"/>
<declaration nom="re" type="var" valeur="6378.137"/>
<declaration nom="alt" type="var" valeur="200.0"/>
<declaration nom="r" type="var" valeur="re + alt"/>
<declaration nom="v" type="var" valeur="Cake.sqrt(mu / r)"/>
<declaration nom="periode" type="var" valeur="2.0 * 3.141592653589793 * r / v"/>
<appel methode="println" args=""Vitesse circulaire (km/s) : " + Cake.toString(Cake.round(v * 1000) / 1000)"/>
<appel methode="println" args=""Periode (min) : " + Cake.toString(Cake.round(periode / 60 * 10) / 10)"/>
<declaration nom="k" type="var" valeur="0"/>
<tant-que condition="k<12">
<bloc>
<declaration nom="a" type="var" valeur="2.0 * 3.141592653589793 * k / 12"/>
<code-brut><![CDATA[Cake.emit(r*Cake.cos(a),r*Cake.sin(a),0,k)%]]></code-brut>
<affectation cible="k" valeur="k + 1"/>
</bloc>
</tant-que>
</bloc>
</methode>
</classe>
</projet=: déclarations · |> entrées/sorties · $= flux · :+ déclarations · bibliothèque : Cake.sqrt, Cake.toString, Cake.round, Cake.emit, Cake.cos, Cake.sinf07dd31c85028933 (reproductible : cake hash t09c03-a.cake)Les deux valeurs se vérifient à la main : la racine carrée de 398 600,4418 ÷ 6 578,137 vaut bien 7,784 km/s, et une orbite à cette altitude boucle en un peu moins d'une heure et demie. Une chaîne de transformation digne de ce nom se contrôle ainsi, par un calcul indépendant, avant d'être crue.
Le bouton « Objet en orbite » de l'éditeur charge la même séparation à l'échelle réelle : reentry_leo.cpx pour les constantes, reentry_rk4.cps pour une intégration de Runge-Kutta d'ordre 4 qui tient compte de la gravité et d'une traînée atmosphérique exponentielle. Avec la traînée à zéro, l'orbite reste fermée ; les autres fichiers .cpx de CakeLand font varier ce seul paramètre pour montrer la décroissance puis la rentrée.
Le source ci-dessous est exactement celui que l'éditeur assemble. Remarquez 0.0-mu : c'est la manière sûre d'écrire une valeur négative dans une expression (voir le chapitre 9).
t09c03-b.cake?# .cpx -- CONSTANTES DE VARIATION : LEO circulaire ~200 km, sans trainee
?# Meme mission, kDrag=0 -> orbite fermee stable (cercle/ellipse).
=: mu=398600.4418%
=: re=6378.137%
=: r0x=6578.137%
=: r0y=0.0%
=: r0z=0.0%
=: v0x=0.0%
=: v0y=7.784%
=: v0z=0.0%
=: kDrag=0.0%
=: scaleH=60.0%
=: dt=20.0%
=: steps=600%
=: rx=r0x%
=: ry=r0y%
=: rz=r0z%
=: vx=v0x%
=: vy=v0y%
=: vz=v0z%
=: hh=dt*0.5%
=: h6=dt/6.0%
=: i=0%
$= (i<steps) {
=: q0=Cake.sqrt(rx*rx+ry*ry+rz*rz)%
=: g0=0.0-mu/(q0*q0*q0)%
=: d0=0.0-kDrag*Cake.exp(0.0-(q0-re)/scaleH)*Cake.sqrt(vx*vx+vy*vy+vz*vz)%
=: a0x=g0*rx+d0*vx%
=: a0y=g0*ry+d0*vy%
=: a0z=g0*rz+d0*vz%
=: r1x=rx+hh*vx%
=: r1y=ry+hh*vy%
=: r1z=rz+hh*vz%
=: w1x=vx+hh*a0x%
=: w1y=vy+hh*a0y%
=: w1z=vz+hh*a0z%
=: q1=Cake.sqrt(r1x*r1x+r1y*r1y+r1z*r1z)%
=: g1=0.0-mu/(q1*q1*q1)%
=: d1=0.0-kDrag*Cake.exp(0.0-(q1-re)/scaleH)*Cake.sqrt(w1x*w1x+w1y*w1y+w1z*w1z)%
=: a1x=g1*r1x+d1*w1x%
=: a1y=g1*r1y+d1*w1y%
=: a1z=g1*r1z+d1*w1z%
=: r2x=rx+hh*w1x%
=: r2y=ry+hh*w1y%
=: r2z=rz+hh*w1z%
=: w2x=vx+hh*a1x%
=: w2y=vy+hh*a1y%
=: w2z=vz+hh*a1z%
=: q2=Cake.sqrt(r2x*r2x+r2y*r2y+r2z*r2z)%
=: g2=0.0-mu/(q2*q2*q2)%
=: d2=0.0-kDrag*Cake.exp(0.0-(q2-re)/scaleH)*Cake.sqrt(w2x*w2x+w2y*w2y+w2z*w2z)%
=: a2x=g2*r2x+d2*w2x%
=: a2y=g2*r2y+d2*w2y%
=: a2z=g2*r2z+d2*w2z%
=: r3x=rx+dt*w2x%
=: r3y=ry+dt*w2y%
=: r3z=rz+dt*w2z%
=: w3x=vx+dt*a2x%
=: w3y=vy+dt*a2y%
=: w3z=vz+dt*a2z%
=: q3=Cake.sqrt(r3x*r3x+r3y*r3y+r3z*r3z)%
=: g3=0.0-mu/(q3*q3*q3)%
=: d3=0.0-kDrag*Cake.exp(0.0-(q3-re)/scaleH)*Cake.sqrt(w3x*w3x+w3y*w3y+w3z*w3z)%
=: a3x=g3*r3x+d3*w3x%
=: a3y=g3*r3y+d3*w3y%
=: a3z=g3*r3z+d3*w3z%
=: nrx=rx+h6*(vx+2.0*w1x+2.0*w2x+w3x)%
=: nry=ry+h6*(vy+2.0*w1y+2.0*w2y+w3y)%
=: nrz=rz+h6*(vz+2.0*w1z+2.0*w2z+w3z)%
=: nvx=vx+h6*(a0x+2.0*a1x+2.0*a2x+a3x)%
=: nvy=vy+h6*(a0y+2.0*a1y+2.0*a2y+a3y)%
=: nvz=vz+h6*(a0z+2.0*a1z+2.0*a2z+a3z)%
:= rx=nrx%
:= ry=nry%
:= rz=nrz%
:= vx=nvx%
:= vy=nvy%
:= vz=nvz%
Cake.emit(rx,ry,rz,i)%
:+ i=1%
}
|> "Mission RK4 terminee"%
Mission RK4 terminee [600 point(s) transmis au visualiseur par Cake.emit]
=: déclarations · $= flux · := déclarations · :+ déclarations · |> entrées/sorties · bibliothèque : Cake.sqrt, Cake.exp, Cake.emitff65aeadd7b74dba (reproductible : cake hash t09c03-b.cake)Dans une station à des centaines de milliers de points de contrôle, on ne réécrit pas l'algorithme de chaque drone pour chaque vol. On vérifie une mission une fois, on la fige par son empreinte d'arbre, et l'on ne fait plus circuler que des constantes. La surface de ce qui peut changer — donc de ce qui doit être revérifié — se réduit à quelques lignes de .cpx.
Séparer les constantes de la mission, c'est séparer ce qui change de ce qui a été prouvé. Une chaîne de transformation fiable fait circuler des données, pas du code neuf.
Le chapitre suivant regarde de près ce que devient ce programme une fois analysé : l'arbre et sa forme CXML.