Révéler les secrets de sa voiture avec le port OBD-II

Auteur :  x0r Publié le   Nombre de commentaires : 8
Mots-clefs : hack linux voiture obd

Lorsque j'ai découvert que ma voiture a un port de diagnostic qui permet d'obtenir en temps réel tous les paramètres instantanés comme la vitesse, le régime du moteur ou d'autres choses encore, je me suis immédiatement mis en tête de faire l'acquisition d'un module USB permettant de faire mumuse avec ce port.

Ce port, qu'on appelle OBD-II (pour On Board Diagnostics), serait obligatoire dans toutes les voitures construites à partir du début des années 2000. Il est donc fort possible que votre voiture en ait également un. En général, il est assez bien caché, parfois sous une trappe, mais le plus souvent sous le volant ou un autre endroit atteignable depuis le siège conducteur.

À l'origine, il s'agit d'un port de diagnostic qui permet aux constructeurs automobiles de surveiller l'état et les émissions de gaz ou particules des moteurs qu'ils mettent dans leurs voitures, afin d'être en règle avec les lois sur les émissions de gaz à effet de serre notamment. Bien entendu, nous allons nous servir de ce port pour faire des choses un peu plus ludiques.

En effet, les possibilités sont nombreuses. Certains pourraient utiliser ce port pour faire de la surveillance de parc automobile. D'autres pourraient s'en servir pour fabriquer un panneau d'instruments alternatifs affichant des paramètres du moteur (retards à l'allumage, forces...) que le tableau de bord n'indique pas. Personnellement, je rêve de fabriquer un affichage têtes hautes. Même si je suis assez dubitatif face à la réalisabilité du projet pour plusieurs raisons, je me suis néanmoins procuré les outils pour faire mumuse.

Pour communiquer avec la voiture, il existe plusieurs protocoles (de couches 1 et 2 grosso modo) standardisés, et savoir lequel utiliser relève généralement de la devinette, parce que comme tout protocole industriel, c'est toujours le bordel. Heureusement, des circuits intégrés l'ELM327 font cette autodétection pour nous, et il suffit de balancer les commandes OBD-II dessus (qui, eux, ne varient quasiment pas) pour interagir avec la voiture.

Le matériel

Il vous faut :

  • un PC, smartphone, PDA, console de jeux, Arduino, grille-pain ou je ne sais quoi d'improbable ;
  • un dongle ELM-327 USB (ou un montage comportant ce chip).

Photo d'un dongle ELM-327

Ce dernier est trouvable sur eBay pour une dizaine d'euros. Il permet de communiquer avec le port OBD-II au moyen de son propre protocole série (RS-232). La version USB intègre tout simplement un convertisseur PL2303 et il est possible de dialoguer avec le dongle via minicom ou un outil similaire.

Hackons un petit peu

D'un point de vue utilisateur, le protocole est de la forme « requête-réponse » pour les choses les plus simples. De la même manière qu'en HTTP, on envoie une requête pour obtenir une donnée particulière, et le dongle nous répond. Par exemple, pour demander la valeur actuelle du compte-tours, la session ressemble à ça :

> 01 0D
41 0C 23 6C

Dans la requête, l'octet 01 indique qu'on demande une valeur réelle instantanée, et l'octet 0D (appelé « PID ») indique qu'on souhaite la valeur du compte-tours. La liste des données qu'on peut obtenir est disponible un peu partout sur le Net. La réponse est 23 6C, ce qui, converti en base 10 et après division par 4, donne la valeur réelle, qui est 2 267 tours par minute.

Chaque valeur est renvoyée sous sa forme brute ; pour les exploiter, il faut généralement les traiter bit à bit pour en extraire un chiffre exploitable.

Bien entendu, seule une petite partie des PIDs listés dans la page sus-citée est généralement prise en charge par l'ordinateur de bord, sinon ce ne serait pas drôle. Heureusement, il y a une commande qui permet d'obtenir la liste des PIDs prise en charge :

> 01 00
41 00 BE 3E B8 11

La réponse est renvoyée sous la forme de 32 bits. Converti en base 2, ce nombre donne 1011 1110 0011 1110 1011 1000 0001 0001. En lisant de gauche à droite, on conclut donc que ma voiture prend en charge les PIDs 00, 02 à 06, 0A à 0E, 10, 12 à 14, 1B et 1F.

Exemple d'application : une simple boîte noire

Comme je voulais tester l'acquisition en temps réel de la vitesse v de la voiture et du régime moteur N, j'ai écrit un petit programme qui interroge un dongle OBD environ 16 fois par seconde et formate, pour une date t donnée, un couple (v, N).

Comme je conduis généralement seul, et que je me suis dit que conduire avec un laptop sur les genoux est une très mauvaise idée, j'ai eu l'idée de logguer ces informations pour ensuite les rejouer après coup, comme une sorte de boîte noire.

Des tests stationnaires (point mort, frein à main et quelques petits coups sur l'accélérateur) m'ont permis de voir que 16 échantillonnages par seconde suffisaient largement pour avoir quelque chose de fluide et qui semblait refléter parfaitement ce qu'indiquaient les instruments au tableau de bord, sans pour autant trop surcharger le dongle OBD.

Screenshot de données logguées à l'aide du dongle OBD

Conclusion

Que dire de l'intérêt d'exploiter les possibilités du port OBD-II de sa voiture, à part celui de mieux comprendre comment fonctionne le moteur, faire du diagnostic et du dépannage soi-même, ou tout simplement pour la simple curiosité intellectuelle ? Nous avons vu comment récupérer des paramètres « live » du moteur et les logguer.

Ma prochaine étape sera d'utiliser un Raspberry Pi ou un Arduino pour tenter d'exploiter le plus de données possibles. J'ai d'ores et déjà commandé un écran 7 pouces HDMI sur eBay, dans l'optique de faire quelque chose en ce sens avec mon Raspberry Pi.

Le futur appartient à l'auto-hébergement

Auteur :  x0r Publié le   Nombre de commentaires : 0
Mots-clefs : autohebergement linux sysadmin cloud vpn securite prism nsa

Cela fait maintenant au moins un an que j'auto-héberge ce site Web (et beaucoup plus de choses) derrière ma modeste connexion Internet, alors que de plus en plus de décideurs dans des grandes entreprises ont l'air d'avoir la tête dans les nuages en parlant de cloud ceci, cloud cela, à tout va. À croire qu'on pourra un jour faire le café dans le cloud.

Personnellement, je ne fais pas confiance au cloud. Les multiples déboires des hébergeurs de cloud, voire des services en ligne de manière générale (webmails, plateformes de blogs, etc.) font que je ne souhaite plus confier mes données à une entreprise tierce dans la mesure du possible. Sans parler de choses incroyablement complexes comme toute l'affaire autour de PRISM.

Bien entendu, une solution pour réduire à coup sûr les chances qu'une organisation gouvernementale américaine lise mes mails aurait été de se payer un serveur dédié et de faire tourner son service mail depuis cette machine. Mais même si j'avais les accès root sur ce serveur dédié, je ne suis probablement pas le seul ; le fournisseur du service a parfois besoin de mettre la main dessus pour dépanner la machine, par exemple. Et une bonne bécane coûte aussi cher qu'une connexion Internet.

D'où la solution que j'adopte maintenant depuis plus d'un an, qui est d'auto-héberger mes services. Ceci procure d'ailleurs énormément de plaisir et de nuits blanches à la fois : j'apprends en même temps que j'ajoute de nouveaux services. Un serveur Web par ci, un DNS par là, un Postfix ensuite avec un Dovecot et un RoundCube fraîchement installés, voire un serveur DHCP/DHCPv6 pendant un moment.

Auto-héberger ses services apporte de nombreux petits avantages, à savoir :

  • la maîtrise totale de son environnement informatique ; je sais que je suis le seul avec les accès root, je suis donc libre dans mes choix techniques et je peux donc installer tous les applicatifs que je veux sur mon serveur, ne serait-ce que pour les essayer ;

  • l'assurance que les données que l'on gère et qui ne sont pas expressément publiées ne quitteront pas le serveur. Bien entendu, cela sous réserve d'aucune compromission de mots de passe. Enfin, pour mitiger cette menace, le minimum consiste à stocker des mots de passe hashés et salés (donc pas comme LinkedIn) et de mettre en œuvre des mécanismes type fail2ban ;  

  • la possiblité d'apprendre comment fonctionne chaque service, et comment les paramétrer pour qu'ils fassent ce qu'ils veulent ; par exemple, comment configurer son DNS et ses entrées MX et SPF pour pouvoir envoyer et recevoir mes mails, ou apprendre comment est réellement acheminé un e-mail à travers le réseau ;

  • être libre de toute restriction arbitraire sur les contenus que l'on publie, dans les limites de la loi française bien entendu. Je pourrais publier des contenus « NSFW » si ça me chante ;

  • en somme, ce que j'aime par-dessus tout dans la vie : le fait que cela soit un défi intellectuel permanent.

Bien entendu, même si j'ai appris la grande majorité des connaissances nécessaires sur le tas, en fouillant dans les résultats de mon moteur de recherche préféré ou dans le cadre de mon implication dans l'association ARISE, ma modestie me conduira sûrement à ne jamais me considérer comme un administrateur système suffisamment chevronné. Je n'ai par exemple jamais essayé de mettre en place des systèmes redondants (avec « failover »), ni touché à du BGP, entre autres. Mais je pense que les connaissances que j'ai suffisent largement pour continuer à faire vivre mes services autohébergés.

Cependant, on pourrait croire que l'auto-hébergement reste quelque chose d'inaccessible au commun des mortels, mais je crois que cela pourrait changer. Auto-héberger implique en effet de garder un ordinateur allumé 24 heures sur 24, 7 jours sur 7. Or, un périphérique assimilable à un ordinateur et qui est le plus souvent allumé en permanence est présent chez la plupart des internautes en France : la « box » fournie par leur FAI. Or, on voit de plus en plus de FAI proposer de plus en plus de services dans leurs box.

De plus, il existe maintenant des distributions Linux comme YunoHost, qui fournissent des solutions clé en main pour auto-héberger un maximum de services tout en passant par une interface Web pour les configurer. Je serais tenté de regarder juste pour voir ce que ça vaut.

En somme, je pense qu'il suffit qu'un FAI donne un petit coup de pouce en fournissant des services auto-hébergés dans leurs box. Free est déjà sur la bonne voie en proposant, en plus, des noms DNS de la forme machin.hd.free.fr, ce qui encourage déjà beaucoup cette pratique.

La seule chose qui manque pour que l'auto-hébergement soit réellement pratique et viable, c'est le débit en upload. Et pendant ce temps, l'actualité fait que certaines entreprises migrent leurs services "cloud" en Suisse plutôt qu'aux États-Unis...

Exécuter du Lua dans du PHP à l'aide d'une sandbox

Auteur :  x0r Publié le   Nombre de commentaires : 0
Mots-clefs : securite lua php web sandbox

Un projet (personnel) sur lequel je travaille en ce moment consiste à réaliser un jeu de rôle en ligne (autrement dit, un « webgame »). Devant le nombre d'objets, d'armes, de quêtes ou d'autres choses différentes, il est alors évident que le moteur du jeu se doit d'être le plus flexible possible. En effet, certains objets modifient les règles du jeu, de la même manière que les actions des cartes L5R modifient la résolution des batailles.

Cependant, implémenter ces modificateurs de règle de manière statique, c'est-à-dire en PHP, obligerait en réalité de mélanger les règles générales et les règles particulières de chaque objet, ce qui ne serait pas souhaitable.

La solution consiste alors en l'exécution de petits bouts de code particuliers dans un langage de script facile à manier. Un très bon candidat pour cela est le Lua.

L'avantage du Lua réside dans sa simplicité et la présence de « first-class variables » : c'est-à-dire que n'importe quel objet, y compris une fonction, peut être affectée à une variable. Sa simplicité permet également, avec peut-être une page pour résumer l'API, à des gens qui ne sont pas forcément les développeurs d'exercer leur créativité.

Compiler l'extension PECL sur Gentoo

Tout d'abord, avant de pouvoir s'amuser avec ce langage, il nous faut compiler l'extension PECL lua.

Il s'avère que même s'il est disponible tout prêt sur PHP.net, qu'il n'a pas encore été packagé pour Gentoo.

Heureusement, l'ebuild n'est pas très compliqué, et l'installer ne l'est pas non plus :

  • Téléchargez-le ici : pecl-lua-1.0.0.ebuild
  • Placez l'ebuild dans /usr/local/portage/dev-php/pecl-lua ;
  • Tapez les commandes suivantes en tant que root :
# cd /usr/local/portage/dev-php/pecl-lua
# ebuild pecl-lua-1.0.0.ebuild manifest
  • Vérifiez que vous avez un PORTDIR_OVERLAY="/usr/local/portage" dans votre /etc/portage/make.conf ;
  • Enfin, mergez le paquet à l'aide d'un emerge pecl-lua.

Oui, je sais, je devrais publier cet ebuild sur l'overlay Sunrise, mais j'ai pas le temps là.

Il se pourrait que vous devrez ajouter votre architecture aux KEYWORDS avant de pouvoir merger le paquet. N'hésitez pas à me signaler si cet ebuild fonctionne sur d'autres architectures ; je vous en serais reconnaissant !

Le bac à sable

Maintenant que nous avons installé l'environnement, nous pourrons exécuter du code Lua à l'intérieur de l'interpréteur PHP.

Cependant, face à la possibilité d'exécuter du code arbitraire, et vu les risques que cela implique, nous allons exécuter ce code Lua dans un environnement contrôlé, ou « sandbox » (bac à sable) ; en particulier, nous désactiverons les fonctions suivantes :

  • os.setlocale, afin d'empêcher les bugs ou failles exploitées à l'aide d'un changement de locale ;
  • os.execute, afin d'empêcher l'exécution de commandes arbitraires ;
  • os.getenv, afin d'empêcher la fuite d'informations sensibles sur le serveur ;
  • enfin, toutes les fonctions de la famille debug.*, afin d'interdire les bidouilles avec l'interpréteur Lua.

Preuve de concept

En guise de preuve de concept et afin de tester l'intégration de Lua dans PHP, j'ai codé le script PHP suivant. Il affiche les caractéristiques d'un personnage fictif, et permet ensuite de les modifier en exécutant le code Lua adéquat.

<?php

$perso 
= array(
    
'nom'     => 'Herp Derp',
    
'sexe'    => 'M',
    
'race'    => 'Humain',
    
'origine' => 'Détessay',
    
'bru'     => 10,
    
'fin'     => 12,
    
'dis'     => 17,
    
'con'     => 13,
);

$perso_b $perso;

$lua_disabled_funcs = array(
    
'os.setlocale',    /* This potentially breaks things */
    
'os.execute',      /* Potentially very dangerous */
    
'os.getenv',       /* Might leak too much info about server */
    
'debug',           /* Disable debugging stuff */
);


function 
dump_perso($perso) {
    echo 
"<pre>\n";

    echo 
htmlspecialchars($perso['nom']) . "\n\n";
    echo 
htmlspecialchars($perso['race']) . ", ";

    switch(
$perso['sexe']) {
        case 
'M': echo "homme"; break;
        case 
'F': echo "femme"; break;
        default: echo 
"sexe inconnu"; break;
    }
    echo 
", " $perso['origine'] . "\n\n";

    echo 
"Brutalité :    " htmlspecialchars($perso['bru']) . "\n";
    echo 
"Finesse :      " htmlspecialchars($perso['fin']) . "\n";
    echo 
"Discipline :   " htmlspecialchars($perso['dis']) . "\n";
    echo 
"Constitution : " htmlspecialchars($perso['con']) . "\n";

    echo 
"</pre>\n";
}

?>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>test lua</title>
<style type="text/css">
.float {
    float: left;
    width: 40%;
}
</style>
</head>
<body>


<form method="POST" action="<?php print $_SERVER['SCRIPT_NAME'?>">
    <textarea id="lua-code" name="lua-code" style="min-width: 30em; min-height: 20em;">
        <?php if (isset($_POST['lua-code'])) { print $_POST['lua-code']; } ?>
    
</textarea>
    <br />
    <input type="submit" value="Exécuter" />
</form> 

<div class="float">
    <h1>Avant</h1>
    <pre><?php dump_perso($perso_b); ?></pre>
</div>

<?php
if (isset($_POST['lua-code'])) {
    
/* Set up Lua environment */
    
$l = new lua();

    
/* Set some variables from outside */
    
$l->assign("perso"$perso);

    
/* Deactivate dangerous functions */
    
foreach ($lua_disabled_funcs as $function) {
        try {
            
$l->eval("${function} = nil");
        } catch(
LuaException $e) {
            echo 
"$function: " $e->getMessage();
        }
    }

    
/* Run user code */
    
try {
        
$l->eval($_POST['lua-code']);
    } catch (
LuaException $e) {
        echo 
"user code: " $e->getMessage();
    }
?>

<div class="float">
    <h1>Après</h1>
    <pre><?php dump_perso($l->perso); ?></pre>
</div>
<?php
}
?>


</body>
</html>

Le code le plus intéressant se trouve plutôt vers la fin du script, et illustre comment on peut faire passer des données de l'environnement PHP vers l'environnement Lua, et vice-versa.

Et quand on exécute la page, ça marche :

Démo Lua en PHP

En conclusion, ce système est bien sympathique, et je suis content d'avoir pu expérimenter avec ça. Cela dit, pendant que je rédigais l'article, je suis passé à du Perl/Catalyst pour le projet en question (la même techno que ce qui fait tourner ce site), parce que Zend me donnait des boutons.

LinkFluence, votre crawler est bugué

Auteur :  x0r Publié le   Nombre de commentaires : 0
Mots-clefs : bot web linkfluence crawler fail coupdegueule

Je surveille mes logs Nginx avec ferveur et j'ai un tail -f /var/log/nginx/access_log en permanence dans un de mes screens.

J'ai soudainement remarqué qu'une IP fait de nombreuses requêtes sur l'URL servant à poster un commentaire, tout en se faisant jeter. En effet, le bot essayait de poster des commentaires sur de très nombreux articles de mon blog, mais sans renseigner d'auteur, ni de texte, ni les quelques champs invisibles servant de protection contre la plupart des spambots. Et en se gourant dans la méthode HTTP (GET au lieu de POST) à utiliser.

Je vous présente donc le bot de LinkFluence, dont l'user-agent est Mozilla/5.0 (compatible; Crawler/0.9; http://linkfluence.net/), et qui est yet another analyseur de contenus sociaux parmi les trouzmille qui s'amusent parfois à crawler mon site et que je laisse généralement faire (enfin... en général).

Je me suis d'ailleurs toujours demandé pourquoi, par « réseaux sociaux », les gens incluent généralement les blogs. Surtout que le mien, en étant exempt de bouton "J'aime", "Repioupiouter" ou d'autres délires socioréticulaires, ne fait pas vraiment dans le social.

Bref, les logs (que j'abrège par concision) sont ici :

37.59.42.102 - - [06/May/2013:10:33:16 +0200] "GET /blog/16 HTTP/1.1" 200 
37.59.42.102 - - [06/May/2013:10:33:16 +0200] "GET /blog/15 HTTP/1.1" 200
37.59.42.102 - - [06/May/2013:10:33:16 +0200] "GET /blog/18 HTTP/1.1" 200
37.59.42.102 - - [06/May/2013:10:33:17 +0200] "GET /blog/21 HTTP/1.1" 200
37.59.42.102 - - [06/May/2013:10:33:17 +0200] "GET /blog HTTP/1.1" 200
37.59.42.102 - - [06/May/2013:10:33:21 +0200] "GET /blog/23 HTTP/1.1" 200
37.59.42.102 - - [06/May/2013:10:33:21 +0200] "GET /blog/14 HTTP/1.1" 200
37.59.42.102 - - [06/May/2013:10:33:22 +0200] "GET /blog/17 HTTP/1.1" 200
37.59.42.102 - - [06/May/2013:10:33:22 +0200] "GET /blog/22 HTTP/1.1" 200
37.59.42.102 - - [06/May/2013:10:33:27 +0200] "GET /blog/20 HTTP/1.1" 200
37.59.42.102 - - [06/May/2013:10:33:27 +0200] "GET /blog/19 HTTP/1.1" 200

Puis quelques secondes plus tard, surprise, des requêtes GET sur une ressource sur laquelle on n'est censé que faire des POST :

37.59.42.102 - - [06/May/2013:10:33:31 +0200] "GET /blog/16/post_comment HTTP/1.1" 403
37.59.42.102 - - [06/May/2013:10:33:31 +0200] "GET /blog/15/post_comment HTTP/1.1" 403
37.59.42.102 - - [06/May/2013:10:33:31 +0200] "GET /blog/18/post_comment HTTP/1.1" 403
37.59.42.102 - - [06/May/2013:10:33:36 +0200] "GET /blog/21/post_comment HTTP/1.1" 403
37.59.42.102 - - [06/May/2013:10:34:05 +0200] "GET /blog/23/post_comment HTTP/1.1" 403
37.59.42.102 - - [06/May/2013:10:34:08 +0200] "GET /blog/17/post_comment HTTP/1.1" 403
37.59.42.102 - - [06/May/2013:10:34:08 +0200] "GET /blog/22/post_comment HTTP/1.1" 403
37.59.42.102 - - [06/May/2013:10:34:35 +0200] "GET /blog/20/post_comment HTTP/1.1" 403
37.59.42.102 - - [06/May/2013:10:34:35 +0200] "GET /blog/19/post_comment HTTP/1.1" 403
37.59.42.102 - - [06/May/2013:10:35:14 +0200] "GET /blog/12/post_comment HTTP/1.1" 403

Alors, suivre les liens <a>, c'est bien ; éviter de le faire avec les URLs d'action pour des formulaires, c'est mieux, et, ça aurait pu éviter à ce pauvre bot de se manger un ban permanent.

Et décidément, ils n'en sont pas à leur premier fail.

Un écran Infogare sur son PC (ou comment exploiter l'Open Data SNCF)

Auteur :  x0r Publié le   Nombre de commentaires : 22
Mots-clefs : transilien sncf gtfs opendata trains rer

EDIT : Je n'utilise plus cette méthode pour récupérer les horaires en live pour monrer.fr, mais une autre API une API officielle fournie par la SNCF.

Depuis quelques temps, la SNCF publie certaines données sur ses trains et ses services dans une politique d'Open Data.

Certains d'entre vous ont peut-être déjà vu mon dernier mini-site qui utilise ces données pour afficher une information de ponctualité pour la grande majorité des trains. Ainsi, un train prévu pour 11 h 07 et dont l'heure de passage est de 11 h 09 est affiché avec la mention « +2 min » :

Ces trains passeront avec deux minutes de retard.

Les horaires de passage réels des trains sont récupérables par plusieurs moyens, mais les comparer avec l'horaire théorique stocké dans les exports GTFS de la SNCF nécessite de faire un certain nombre de traitements sur ces données pour les confronter correctement.

Tout d'abord, quelques définitions

Pour ne pas vous perdre tout de suite, quelques définitions s'imposent. Au risque de réutiliser des termes du jargon ferroviaire en m'éloignant de leur sens d'origine, les voici :

Sur une ligne de RER, on assure des missions : ces missions sont tout simplement des listes de gares desservies dans un certain ordre. Certaines missions sont omnibus (i.e. s'arrêtent à toutes les gares d'une ligne), d'autres sont semi-directes et sautent parfois certaines gares. À chaque mission, on donne un code à quatre lettres, appelé code mission, unique à chaque mission, et qui permet aux régulateurs de connaître exactement la desserte d'un train donné, afin de l'aiguiller correctement. On montre également ces codes mission aux voyageurs.

Toutes les missions sont assurées à intervalles réguliers par des trains qui ont pour ordre de s'arrêter à chaque gare de leur mission en respectant un horaire théorique. Les aléas de la circulation amène souvent ces trains à accuser un retard, et on indique alors sur les écrans d'information des gares Transilien une estimation de l'horaire réel de passage d'un train dans une gare donnée.

Chaque train est repéré, en zone SNCF (lignes RER C, D, E et Transilien) par leurs numéros de train (par exemple, 123477), et en zone RATP (lignes RER A et B) par leur code mission à quatre lettres suivi de deux chiffres (par exemple, ZEUS67).

L'ensemble des trains circulant un jour donné constitue un service journalier. Souvent, on assure des services journaliers différents en semaine et en week-end ; les horaires théoriques du week-end pouvant être moins contraignants qu'en semaine, où l'on fait rouler un nombre plus conséquent de trains.

Enfin, afin de rester cohérent avec les termes utilisés dans l'export GTFS, j'appelle un itinéraire un train qui, un jour donné, assure une mission donnée. Le train 123477 un jour de semaine n'est pas forcément le même que le 123477 un samedi ou dimanche ; cependant, ces deux itinéraires sont repérés par des identifiants différents, par exemple DUASN123477R010392834 et DUASN123477R040392235.

Les sources de données de mon mini-site

J'utilise deux sources de données pour afficher les informations visibles sur le mini-site.

La première source donne tous les horaires théoriques de tous les trains que la SNCF fait rouler sur son réseau Transilien. Les gares sont repérées par leur code UIC ; la correspondance entre code UIC et nom de gare est fournie dans les données exportées. Ces données sont disponibles sur la page Transilien du site OpenData SNCF sous forme d'une archive zip qui, une fois extraite, donne ces informations dans ce fameux format GTFS dont je parle depuis le début de ce post.

La seconde source, en revanche, me donne les horaires réels des prochains trains d'une gare repérée à l'aide d'un trigramme appelé « TR3 » (exemple : SKX pour Sceaux, PAA pour Paris Gare de Lyon (RER D), COE pour Corbeil-Essonnes...). La liste officielle de ces trigrammes et la correspondance avec le nom de la gare est a priori un document interne à la SNCF, mais comme pour les codes UIC, on trouve assez facilement des listes (incomplètes) compilées sur Internet. J'ai dû passer pas mal de temps pour compléter cette liste, donc je vous propose d'aller regarder le code source et d'ouvrir le script db.sql.

Cette source n'est a priori pas publiée sous licence Open Data, mais l'URL a l'air de se balader un peu partout dans des forums. L'URL est de la forme :

http://sncf.mobi/infotrafic/iphoneapp/transilien/?gare=[trigramme gare]

et les données obtenues sont en JSON, exploitable quasi-directement dans un script, pourvu que l'on ait quelque part une base de données de gares indexées selon leur code TR3.

Utiliser les deux sources ensemble

Les problèmes principaux que j'ai eus en voulant utiliser les deux sources ensemble étaient les suivants :

  • L'export GTFS mentionne des identifiants d'itinéraire du style DUASN123477R010392834, alors que les horaires en temps réel ne donnaient que le numéro du train, du style 123477. On peut dériver le numéro du train en fonction de l'identifiant d'itinéraire, mais dans l'autre sens, comme dans mon cas, un train peut correspondre à plusieurs itinéraires ; on est donc obligé d'utiliser une clause WHERE un peu compliquée pour éliminer les itinéraires qui ne correspondent pas au jour actuel, entre autres.

  • Dans certains cas, les numéros de trains dérivés des identifiants d'itinéraire n'existent pas. En particulier avec certaines mission ROPO du RER D, ou certaines mission du RER C, où il faut aller chercher le numéro de train « à côté » pour avoir les données que l'on cherche.

  • Les horaires fournis dans l'export GTFS dépassent parfois 24 h. Un train qui part de sa gare d'origine à 23 h 50 et qui arrive à son terminus à 1 h 50 est noté comme arrivant à 25 h 50 dans les données GTFS. En revanche, le train assurant la même mission et qui part une demi-heure plus tard (à 0 h 20, donc) est noté comme arrivant à 2 h 20 au lieu de 26 h 20. Je comprends le raisonnement derrière, mais ça reste fastidieux à gérer.

  • L'export GTFS utilise les codes UIC des gares desservies par chaque train, alors que les données « temps réel » n'utilisent que les codes TR3. Heureusement, faire une table qui indexe les gares à la fois par leur code UIC et leur code TR3 n'est pas non plus la chose la plus compliquée du monde.

Conclusion

En soi, la seule chose que je regrette avec les données GTFS de la SNCF, c'est qu'il manque les numéros de trains, et que je suis obligé de les dériver d'un identifiant à l'aide d'un hack que je considère comme infâme. À part ça, cela me fait extrêmement plaisir que la SNCF ait décidé de faire un pas vers la libération de ses données, et même si la RATP a également emboîté le pas, ils ne publient pas les mêmes informations.

Néanmoins, j'espère avoir réussi à démystifier un peu cette application Web. Je tire néanmoins une grande fierté de pouvoir afficher sur ce mini-site des informations que même les écrans Infogare n'affichent pas ; on constate parfois à quel point certains trains accusent du retard en heure de pointe, même si ce n'est plus aussi visible qu'avant.

En confrontant ainsi données théoriques et informations de circulation en temps réel, ce serait là l'occasion pour des chercheurs ou des groupes de travail divers et variés de collecter des données de régularité et ainsi, peut-être, en tirer des conclusions qu'il serait impossible de tirer autrement ?

Et en bonus

Un petit truc marrant qu'on peut s'amuser à faire avec ces données GTFS : tracer le nombre de trains en service en fonction de l'heure de la journée sur chacune des lignes RER/Transilien SNCF (cliquez pour voir en grand et en meilleure qualité) :

Trains en fonction de l'heure

Suite : monrer.fr : autocomplétion et reverse-engineering