Le gestionnaire présenté dans les articles précédents est pratiquement complet, on va lui ajouter ici quelques améliorations : le suivi des trains, le suivi des commandes, le cab-signal, … et discuter de l’arrêt des trains aux signaux.
Le suivi des trains et le cab-signal seront ajoutés à l’exemple complet du Locodrome.
Jusqu’à présent le gestionnaire de réseau est complètement indépendant du matériel, progressivement dans cet article les contraintes et diversités du matériel vont apparaître. Ce qui va impliquer que la partie programme va s’amenuiser petit à petit, les classes vont se faire de plus en plus incomplètes avec des méthodes suggérées, puis faire la place à des descriptions, voire des idées.
Un gestionnaire en C++ pour votre réseau
Un gestionnaire en C++ pour votre réseau (4)
Trains et commandes de trains
.
Par :
DIFFICULTÉ :★★★
Parlons de trains et de commandes de trains. Dans la réalité il y a d’une part des "trains" et d’autre part des "commandes" de trains
- un "train" c’est normalement une locomotive avec ou sans une rame de voitures ou de wagons, ou une rame automotrice (on appellera "train" tout ce qui roule sur un réseau)
- une "commande" de train c’est normalement un homme : le "conducteur". Le conducteur fait rouler le train en observant les signaux et les consignes de vitesse. Une commande peut être aussi automatique (comme sur certains métros). Un train sans commande est normalement à l’arrêt (quoique ?).
Les deux choses sont à priori disjointes, il peut y avoir des trains sans commande (sans conducteur) et des commandes sans train (des conducteurs sans train).
Sur nos réseaux on a aussi des trains, et aussi des conducteurs. Les conducteurs peuvent être des hommes (nous) conduisant les trains avec des "manettes" (ou "souris", transfo, smartphone, tablette, …) ou un automatisme électronique ou informatique. Ces manettes ou automatismes sont les commandes de trains que l’on appellera "commandes".
Trains et commandes de trains
Pour prendre en compte les trains et les commandes dans notre gestionnaire de réseau il va falloir des objets " train" et des objets "commande".
Il est pratique de regrouper toutes les caractéristiques des locomotives ou des automotrices dans un objet supplémentaire "engin-moteur" (mais ce n’est pas une obligation), ces caractéristiques ne changeant quasiment pas cela peut faciliter la gestion mémoire.
Trains
Commençons par les trains, par ce qui caractérise un train, essentiellement son sens (pair, impair voire indéterminé) et sa "vitesse". La "vitesse" est contrôlée surtout par les manettes, mais l’information fournie par les manettes n’a souvent pas grand chose a voir avec une "vitesse" (exprimée en kilomètres par heure) et les consignes envoyées aux alimentations analogiques ou numérique n’ont pas non plus grand chose à voir avec des "vitesses", par exemple en numérique les "vitesses" vont de 0 à 127 au maximum. On va appeler "cran" (un peu par analogie avec les machines électriques) les pseudo vitesses reçues des manettes ou envoyées aux alimentations.
La vitesse réelle d’un train dépend des caractéristiques de l’engin moteur, du cran, de la charge, du pourcentage d’une rampe, du réglage de CVs en numérique, … Si l’engin moteur est étalonné on peut à partir du cran obtenir la vitesse réelle (s’il y a un dispositif de compensation de charge).
Le sens et le cran sont les informations de base, mais on peut ajouter plein d’autres choses optionnelles : l’engin-moteur, le nom du train ("flèche d’or", "train bleu", … pour affichage sur le TCO par exemple), la zone ou les zones où est le train, un mode (ligne, manoeuvre, …), …
Concernant l’engin-moteur on peut avoir optionnellement : le nom (230D13, BB16007,… pour affichage sur le TCO par exemple), un type (vapeur, diesel, autorail, analogique, numérique, …), une adresse DCC, une icône (si on a de la mémoire !), une table de correspondance cran/vitesse, …
Voici les ébauches des classes engin-moteur et train :
enum {PAIR,IMPAIR /*,INDETERMINE*/}; // les sens des trains
class EnginMoteur {
char* nom; // OPTION
byte type; // OPTION
int adresse; // OPTION
… // icone // OPTION
EnginMoteur(…) {…} // constructeur
};
class Zones { … } // petite classe pour gerer les zones où se trouve le train
class Train {
byte sens /*=INDETERMINE*/; // le sens du train
byte cran = 0; // 0 a 127 le cran du train
EnginMoteur* em = NULL; // l'engin moteur
char* nom; // le nom du train OPTION
Zone* zone = NULL; // la zone ou est la tete de train
//Zones* zones = NULL; // les zones ou est le train // OPTION
byte mode; // informations sur le train OPTION
Commande* commande = NULL; // la commande associee
// byte max = 127; // le cran maximum autorise OPTION
Train(…) {…} // constructeur
byte getCran() {
return cran;
}
void setCran(byte c) {
cran = c;
}
byte getSens() {
return sens;
}
void setSens(byte s) {
sens = s;
}
void invSens() { // inversion du sens
sens = !sens;
}
setCranEtSens(byte c,byte s) {
cran = c;
sens = s;
}
// … getAdresse() { … } // adresse DCC
// byte getVitesse() { … } // vitesse reelle ( Km/h )
boolean sensable() { // test si changement de sens possible
return cran == 0 // la vitesse est nulle
//&& zone.type … si le type de la zone le permet
//&& le train est entierement sur une seule zone
;
}
};
Le sens du train peut être un booléen si on n’utilise pas le sens indéterminé, la méthode sensable()
permet de contrôler les changements de sens du train.
Commandes de trains
Les objets "commande" de train servent d’interface entre les manettes et le gestionnaire. On aura un objet commande par manette ou par commande automatique. Une commande échange des informations avec sa manette et des objets du gestionnaire.
L’objet commande reçoit de la manette des demandes :
- d’incrémentation/décrémentation de cran
- de changement de sens
- de fonctions en numérique
- d’arrêt d’urgence
- …
Ces demandes sont contrôlées avant d’être transmises au gestionnaire (pas de vitesses négatives, pas de dépassement de vitesse, pas de changement de sens intempestifs, …). Les demandes sont alors envoyées à l’alimentation associée en analogique ou à la centrale DCC en numérique, ainsi qu’au train pour l’information du gestionnaire et éventuellement à la manette elle même pour la mise à jour de ses affichages.
L’objet commande envoie donc des informations ou des demandes :
- à l’alimentation associée (analogique)
- à la centrale DCC (numérique)
- au train associé s’il existe (sens, cran)
- à la manette associée (sens, cran, vitesse, icône, feux du cabsignal, nom du train ,icone du train, …) pour affichage sur la manette
- …
Voici une ébauche de la classe commande :
class Commande {
byte no; // numero de commande et d'alimentation en analogique
… // lien avec la manette pour actions sur la manette (sens, cran, icone, cabsignal, …)
… // lien avec l'alimentation
… // lien avec la centrale DCC
Train* train = NULL; // le train commande
Commande(…) { … } // constructeur
void incCran(byte d) {
byte c; // demande d'incrementation (en + ou -) du cran du train
if (train == NULL) {
return; // precaution
}
c = train.cran; // le cran ancien
if (d > 0) {
c += d;
if (c > 127) { // if (c>max) c=max; cran maximum autorise
c = 127;
}
} else {
c -= d;
if (c < 0) {
c = 0;
}
}
train.setCran(c); // le nouveau cran
// envoi du cran (+ sens) a l'alimentation ou a la centrale (+ manette)
}
void setSens(byte s) { // demande de changement de sens du train
if (train == NULL) {
return; // precaution
}
if (!train.sensable()) {
return; // controles du changement de sens
}
train.setSens(s);
// envoi du sens (+ cran) a l'alimentation ou a la centrale (+ manette)
}
};
Ici aussi le sens du train peut être booléen, mais il est pratique d’avoir un sens indéterminé, donc trois états (indéterminé, pair ou impair), notamment pour les manettes avec interrupteur à trois positions (dont deux instables) ou avec deux boutons.
On pourrait avoir une méthode setCran()
, pour gérer des manettes à potentiomètre, mais pour le gestionnaire de réseau il est beaucoup plus pratique d’avoir une incrémentation/décrémentation du cran, donc des manettes à encodeur en quadrature ou smartphones ou tablettes. Par exemple lors de l’arrêt automatique devant un signal fermé le cran va devenir nul et il faudra forcer d’une manière ou d’une autre la remise à zéro du potentiomètre avant de pouvoir repartir. On a les mêmes problèmes avec les limitations de vitesses automatiques.
Allocation mémoire
Regardons un peu les problèmes d’allocation mémoire. On aura un objet "engin-moteur" par engin-moteur pouvant être mis sur le réseau. On aura un objet "train" par train sur le réseau (commandé ou pas). On aura un objet "commande" par manette, plus un par commande automatique.
Les objets "engin-moteur" peuvent être très gourmands en mémoire, on a souvent beaucoup de locomotives ou d’autorails et si on veut des icônes cela explose, ce n’est plus possible avec un Arduino seul, il faut passer par des fichiers soit sur une carte SD soit sur un ordinateur. Une solution pour les icônes est de les mettre dans les manettes, dans une carte SD par exemple.
Les objets "train" sont moins nombreux on peut les borner en fonction de la taille du réseau (nombre de voies disponibles). Les objets "commande" dépendent du nombre de manettes sur le réseau, on peut aussi les borner.
Ce qui faut éviter c’est de créer des objets (avec des new
) et les abandonner par la suite pour un créer d’autres, cela transforme la mémoire en gruyère, avec des trous partout, ce qui est très difficile à gérer pour l’Arduino. Par contre tant qu’il y a de la mémoire disponible on peut ajouter des objets en fonction des besoins.
Pour suivre et contrôler la disponibilité de la mémoire, il faut se servir de la fonction freeRam qui a été ajoutée, avec une détection du processeur (ARM pour l’Arduino Due et AVR pour les Uno, Nano, Mega, Mini...) :
#if defined(__arm__)
#include <malloc.h> // pour la mesure de mémoire sur Due
extern "C" char *sbrk(int i);
int freeRam() {
struct mallinfo mi=mallinfo();
char *heapend=sbrk(0);
register char * stack_ptr asm("sp");
return (int) (stack_ptr - heapend + mi.fordblks);
}
#elif defined(__AVR__)
int freeRam () {
int v; // calcul de la memoire libre
extern int __heap_start, *__brkval;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
#else
#error Processeur non supporté.
#endif
On peut aussi se passer des objets "engin-moteur" et fusionner les objets "train" et les objets "commande". Sur le Locodrome, pour économiser la mémoire, il n’y a pas d’objet "engin-moteur", seulement deux objets "train" et pas d’objet "commande". Les objets "train" et "commande" sont fusionnés, on a toujours deux trains et deux commandes.
Suivi des trains
Muni des objets trains on peut assez facilement faire un suivi des trains. On a un suivi des trains quand le gestionnaire sait a tout moment où sont les trains, plus précisément dans quelle(s) zone(s) ils sont.
Le suivi des train sert :
- à l’affichage des trains sur tco
- de base pour le cab-control
- …
L’idée du fonctionnement est le suivant, chaque zone peut mémoriser un train. Quand un train occupe une zone on copie le train de la zone "précédente" dite zone de provenance. Quand un train libère une zone on efface le train.
Le plus délicat est de trouver la zone de provenance, sauf pour les zones en cul de sac ou parcourues toujours dans le même sens, il y a deux provenances possibles et il faut choisir la bonne.
Depuis le premier article il y a dans la classe zone une méthode provenance()
(non documentée) prévue à cet effet :
Zone* provenance() {
Zone* p;
Zone* i;
// if (type==SENS_IMPAIR_SEUL) return suivantePaire(); // optimisation (si type)
// if (type==SENS_PAIR_SEUL) return suivanteImpaire(); // optimisation (si type)
p=suivantePaire();
if (p != NULL && p->libre()) {
p = NULL; // candidate si non nulle et occupee
}
i = suivanteImpaire();
if (i != NULL && i->libre()) {
i = NULL; // candidate si non nulle et occupee
}
if (p != NULL) {
if (i == NULL) {
return p;
} else {
erreur(201); // deux candidates (ambiguite)
}
} else {
if (i != NULL) {
return i;
} else {
erreur(202); // aucune candidate (erreur)
}
}
return NULL;
};
Si on a des types de zone, une optimisation peut être faite en testant si la zone ne peut être parcourue que dans un sens (pair ou impair). Dans le cas général on regarde les zones adjacentes et si une seule est occupée on a trouvé la provenance sinon il y a ambiguïté et c’est une erreur.
Comme il a été précisé dans l’article précédent (avec l’exemple du Locodrome) il faut apporter un grand soin à l’écriture des méthodes suivantePaire()
et suivanteImpaire()
, une zone a une suivante seulement si les aiguilles sont bien positionnées pour qu’un train virtuel puisse passer de la zone à la suivante ou vice versa sans dérailler, sinon elle n’a pas de suivante (résultat NULL). Cet aspect est essentiel pour le bon fonctionnement de la méthode provenance()
.
Malgré cela dans quelques cas l’ambiguïté reste, c’est malheureusement (ou heureusement) le cas du Locodrome à cause du BAL de voie unique. Par exemple, quand un train impair passe de la zone z4 à la zone z3, lors de l’occupation de z3 on recherche la provenance, z4 est occupée (il y a toujours deux zones occupées lors d’un changement de zone) et z2 peut être occupée par un autre train (de sens impair aussi à cause du BAL), l’ambiguïté reste, il faut la lever.
Tous les moyens sont bons pour lever l’ambiguïté : sens de trains, sens d’itinéraires, sens d’autorisation , … . Pour le Locodrome on a choisi le sens de l’autorisation. Pour mettre en oeuvre cette levée d’ambiguïté on va retoucher très légèrement la méthode provenance()
:
Zone* provenance() {
Zone* p;
Zone* i;
// if (type==SENS_IMPAIR_SEUL) return suivantePaire(); // optimisation (si type)
// if (type==SENS_PAIR_SEUL) return suivanteImpaire(); // optimisation (si type)
p = suivantePaire();
if (p != NULL && p->libre()) {
p = NULL; // candidate si non nulle et occupee
}
i = suivanteImpaire();
if (i != NULL && i->libre()) {
i = NULL; // candidate si non nulle et occupee
}
if (p != NULL) {
if (i == NULL) {
return p;
} else {
return(ambiguite()); // deux candidates (ambiguite)
}
} else {
if (i != NULL) {
return i;
} else {
erreur(202); // aucune candidate (erreur)
}
}
return NULL;
}
virtual Zone* ambiguite() {
erreur(201);
return NULL;
}
En cas d’ambiguïté une méthode annexe ambiguite()
est appelée, elle signale une erreur et retourne NULL, le comportement de la méthode provenance()
ne change donc pas, mais la méthode ambiguite()
est virtuelle, elle peut donc être redéfinie pour lever cette ambiguité, par exemple dans le cas de la zone z3 du Locodrome :
class Z3 : Zone {
…
virtual Zone* ambiguite() {
if (!aut->getSens()) {
return z2;
} else {
return z4;
}
}
};
On a aussi le même problème avec z4.
Pour mettre en oeuvre le suivi des trains il faut ajouter une variable train dans la classe zone et quelques instructions dans les méthodes occuper()
et liberer()
de zone :
class Zone {
…
Train* train = NULL; // le train dans la zone
…
void occuper() {
Zone* z;
…
z = provenance(); // provenance du train
train = z->train; // train de la zone de provenance
train->zone = this; // la zone pour le train
}
void liberer() {
…
train = NULL;
}
};
Il faut remarquer le lien croisé entre le train et la zone.
Un train peut occuper plusieurs zones en même temps, il peut être intéressant de mémoriser dans la classe train la liste de ces zones sous forme d’un tableau, ou mieux dans une petite classe annexe "Zones" qui fait toute la gestion de la liste. Sur un réseau donné on peut borner le nombre de zones occupées par un train. Avec une telle liste de zones on peut réaliser des contrôles :
- test de rupture d’attelage, si dépassement de la borne (un simple compteur peut suffire dans ce cas)
- en cas de libération de zone, vérification que c’est bien la dernière de la liste
- en cas de changement de sens, vérification que le train est dans une seule zone (un simple compteur peut suffire dans ce cas)
- …
Au démarrage du gestionnaire, celui ci ne connait aucun train, il faut donc un mécanisme pour lui dire où sont les trains. Cela peut se faire par un dialogue avec le gestionnaire par le biais d’un moyen quelconque (petit écran, boutons, …), ou aussi s’envisager avec des méthodes automatiques de recherche des trains (voir le forum ici notamment). Il faut fournir au gestionnaire des couples zone/train, à lui de vérifier que la zone est bien occupée.
Sur un grand réseau, avec beaucoup de trains, c’est assez fastidieux, il est alors intéressant d’avoir une mémorisation. A l’arrêt du gestionnaire on mémorise tous les couples zone/train, au démarrage on les reprend (en faisant des vérifications d’occupation des zones). La mémorisation peut se faire dans l’EEPROM de l’Arduino, dans un fichier d’une carte SD ou d’un ordinateur, … .
Sur le Locodrome le suivi des trains est matérialisé avec le nom des trains dans des cercles.
Suivi des commandes
Le suivi des commandes est très semblable au suivi des trains, il faut ajouter une variable commande dans la classe zone et quelques instructions dans les méthodes occuper()
et liberer()
de zone :
class Zone {
…
Commande* comm = NULL; // la commande de la zone
…
void occuper() {
Zone* z;
…
z = provenance(); // provenance de la commande
comm = z->comm; // commande de la zone de provenance
comm->zone = this; // la zone pour la commande
}
void liberer() {
…
comm = NULL;
}
};
Comme pour le suivi des trains il faudra un dialogue pour associer une zone à une commande au démarrage. Le suivi des trains et le suivi des commandes font un peu double emploi, on peut choisir l’un ou l’autre en fonction des besoins et de ce que l’on veut mettre en oeuvre par la suite. Dans certains cas il peut être intéressant d’avoir les deux, par exemple si certaines zones sont découpées en sous-zones (sous-zones d’arrêt ou sous-zones pour avoir plusieurs engins-moteurs dans une zone (en analogique), ...
Poursuite en canton
La poursuite en canton, appelée aussi conduite sélective, permet de commander un train donné partout sur le réseau avec la même manette (quelle qu’elle soit), cela est quasiment indispensable en analogique.
En analogique on a plusieurs alimentations contrôlées par des manettes (ou une commande automatique), il faut donc du matériel pour soit commuter les alimentations aux zones, soit faire des passages de cran (PWM) de zone en zone, … . La poursuite en canton automatise ces opérations. La commutation peut se faire avec des matrices de relais ou tout autre système équivalent. En numérique n’a pas besoin d’un tel dispositif, mais c’est la seule "économie", tout le reste est nécessaire.
En analogique, la poursuite en canton, doit aller très vite pour alimenter la zone lors de l’occupation, sinon le train peut avoir un à-coup (manque d’alimentation un petit moment), on peut avoir aussi un système prédictif qui alimente les cantons en avance (mais c’est beaucoup plus difficile à gérer).
Le mécanisme de poursuite en cantons s’appuie logiquement sur le suivi des commandes (mais il peut aussi souvent s’appuyer sur le suivi des trains), il suffit de rajouter (au suivi des commandes ou des trains) des instructions pour agir sur les alimentations (commutation ou transfère de PWM) :
class Zone {
…
Train* commande = NULL; // le train dans la zone
…
void occuper() {
Zone* z;
…
z = provenance(); // provenance de la commande
commande = z->commande; // commande de la zone de provenance
commande->zone = this; // la zone pour la commande
// actions sur le materiel
}
void liberer() {
…
commande = NULL;
// actions sur le materiel
}
};
On a juste ajouté au suivi des commandes des actions sur le matériel, on pourrait faire de même avec le suivi des trains.
Il faut remarquer que c’est la première fois que l’on fait une distinction entre l’analogique et le numérique, mais les différences vont s’accentuer par la suite.
Cabsignal
Le cab-signal permet d’afficher sur la manette le signal que voit le train associé à cette manette. La mise en oeuvre du cab-signal est basée sur le suivi des trains (mais on peut le faire aussi avec le suivi des commandes). Il y a plusieurs cas ou il faut mettre à jour le cab-signal :
- quand le train change de zone
- quand le train change de sens
- quand le signal devant le train change de feux
- quand un train est associé à (ou dissocié d’) une commande
- …
C’est à la classe zone de prendre en compte les changements de zone d’un train, cela se fait dans la méthode occuper()
:
class Zone {
…
void occuper() {
…
if (train != NULL) {
signalVu(train);
}
...
}
};
void signalVu(Train* t) {
Signal* signal; // le signal vu par le train
if (t->sens == SENS_PAIR) {
signal = t->zone->signalPair;
} else {
signal = t->zone->signalImpair;
}
if (signal != NULL) { // test si signal
// envoi des feux du signal a la manette associee au train (si elle existe)
} else { // pas de signal (eteint)
// envoi des feux du signal a la manette associee au train (si elle existe)
}
}
La fonction signalVu()
n’est pas dans une classe, ce n’est donc pas une méthode, ceci pour s’adapter plus facilement à différents cas de figure, elle utilise le sens du train pour trouver un éventuel signal.
C’est à la classe train de prendre en compte les changements de sens d’un train :
class Train {
…
void setSens(byte s) { // positionner le sens
sens = s;
signalVu(this);
}
void invSens() { // inverser le sens
sens = !sens;
signalVu(this);
} // inverser le sens
};
Toutes les méthodes intervenant sur le sens du train appellent la fonction signalVu()
Quand un signal change de feux c’est à la classe signal de transmettre les nouveaux feux au cab-signal, cela se fait dans la méthode manoeuvrer() :
class Signal {
…
void manoeuvrer() { // appele quand un signal change
…
signalChange(this);
}
};
void signalChange(Signal* s) { // le signal vu par le train change
Zone* zone;
Train* train;
zone = s->zone;
train = zone->train;
if (train == NULL) {
return; // pas de train
}
if (train->sens == SENS_PAIR) {
if (s != zone->signalPair) {
return; // pas bon sens ou pas signal
}
} else {
if (s != zone->signalImpair) {
return; // pas bon sens ou pas signal
}
}
// envoi des feux du signal a la manette associee au train (si elle existe)
}
Ici aussi signalChange()
est une fonction pour plus de souplesse, cette fonction utilise les informations de la zone où est implanté le signal pour chercher un train éventuel sur cette zone puis en fonction du sens de ce train transmettre au cab-signal les nouveaux feux du signal (s’il est dans le bon sens).
Quand un train est associé à (ou dissocié d’) une commande il faut aussi mettre à jour le cab-signal.
Sur le Locodrome, comme il n’y a quasiment pas de manettes, le cab-signal est reporté sur le TCO.
Arrêt des trains
Pour assurer la sécurité des circulations des trains il faut que les trains respectent les signaux, avec un cab-signal c’est possible en conduite manuelle mais il est difficile de conduire plusieurs trains simultanément, en conduite automatique ce n’est pas possible. Il faut donc qu’il y ait, au moins, un arrêt automatique des trains devant les signaux fermés, mieux un respect des vitesses. Avec le KVB (contrôle de Vitesse par Balise, le K est utilisé à la place du C) la SNCF a de tels dispositifs. On peut s’étonner que sur le Locodrome les trains s’arrêtent automatiquement sans un tel dispositif, mais ce sont des trains virtuels programmés pour respecter les signaux. Avec des trains réels, même miniatures, il faut un dispositif idoine.
Commençons pas le respect des signaux d’arrêt (carrés, carrés violets, sémaphores, … ). Sur nos réseaux beaucoup de solutions existent, la plus répandue est la zone d’arrêt. En analogique il suffit de couper le courant dans la zone d’arrêt, le gestionnaire peut commander cette coupure (couplée avec la manœuvre du signal ou l’établissement de l’itinéraire). En numérique on peut utiliser sur la zone d’arrêt un générateur de freinage, le système ABC de Lenz ou autre, là aussi le gestionnaire peut commuter les dispositifs.
Le système avec zone d’arrêt présente pas mal d’inconvénients :
- il faut couper les rails (au moins un) en plus des inévitables coupures de zone
- la zone d’arrêt doit être suffisamment longue
- l’arrêt au pied du signal n’est pas facile à réaliser
- la détection de présence des trains (par consommation de courant) est plus compliquée (deux ou trois sous-zones)
- la double traction est plus délicate
- il faut prévoir les circulations en contresens
- il est difficile d’avoir des circulations en pousse (rames de banlieues, corail réversibles, autorails et automotrices multicaisses, … )
- arrêt brutal en analogique (sauf si dispositif de ralentissement et d’accélération)
- …
L’avantage est la simplicité du système, surtout en analogique.
Une autre possibilité pour arrêter les trains devant les signaux fermés est le "tir au but", dans ce cas pas besoin de matériel, c’est le gestionnaire qui prend tout en charge (ou presque). Pour pouvoir faire cela il y a plusieurs pré-conditions :
- en analogique avoir des alimentations à régulation de charge, en numérique la plupart des décodeurs font la régulation de charge
- mesurer précisément la longueur des zones (éventuellement en fonction de la position d’aiguilles)
- de préférence étalonner les engins-moteurs pour connaitre la vitesse réelle en fonction du cran (et inversement)
Le principe est simple, quand un train franchit un signal à l’avertissement (le signal suivant est donc fermé) le gestionnaire connait la vitesse réelle du train (à partir du cran) et la distance avec le signal d’arrêt, il peut alors calculer une courbe de décélération progressive pour arrêter le train juste devant le signal et envoyer périodiquement à l’alimentation ou au décodeur des consignes de vitesse pour respecter cette courbe de décélération. Cela peut nécessiter l’envoi de beaucoup de consignes et il faut que matériel supporte la charge.
Les inconvénients sont les pré-conditions, mais les avantages sont nombreux :
- pas de matériel supplémentaire nécessaire
- pas de zone d’arrêt
- arrêt au pied du signal quelque soit le train
- arrêt progressif et redémarrage progressif
- pas de problèmes avec la double traction, ni les circulations en contresens
- possibilité de circulations en pousse (une belle rame de VB2N poussée par une 16500 !)
- …
Une détection ponctuelle (cellule photo électrique ou infrarouge, … ) un peu avant le signal peut améliorer le tir au but. Cela peut aussi s’envisager avec des alimentations analogiques sans régulation de charge avec éventuellement une autre détection ponctuelle à une distance plus importante du signal. Bien évidemment tout cela peut être pris en charge par le gestionnaire.
D’autres systèmes de tir au but sont possibles notamment avec le dispositif de "distance de freinage constante" inclus dans certains décodeurs DCC.
Avec les mécanismes de tir au but le gestionnaire peut aussi contrôler la vitesse réelle des trains et borner cette vitesse. En conduite manuelle on peut reporter une alarme sur la manette associée au train s’il dépasse la vitesse permise, en conduite automatique c’est directement pris en compte. On peut aussi imaginer de respecter tous les panneaux de limitation de vitesse rencontrés.
Quel plaisir de voir une belle rame qui se met à ralentir au passage d’un avertissement pour s’arrêter pile-poil devant le carré, puis repartir progressivement quand celui-ci passe au vert. Ou une rame qui se met à ralentir progressivement en franchissant un signal de ralentissement pour aborder le signal de rappel de ralentissement à 30 Km/h (ou 60). Ou une rame qui reprend de la vitesse avant d’atteindre le signal si celui ci s’ouvre avant.
Bilan
Cet article (et les précédents) montre bien que le gestionnaire décrit permet de faire tout, ou presque, ce que l’on veut avec le matériel que l’on veut. Beaucoup de logiciels commerciaux ou libres font la même chose (plutôt en numérique qu’en analogique), généralement avec des contraintes sur le matériel, l’intérêt du gestionnaire décrit ici est que l’on fait tout soi-même (matériel et logiciel), comme on veut, quand on veut, pas à pas, en maîtrisant (à peu près) tout.
Comme dans les articles précédent on trouvera dans le fichier ci-dessous le programme de gestion du Locodrome bien écrit en C++, ainsi que le TCO virtuel écrit en Processing :
Dans les différents fichiers, du programme de gestion du Locodrome, tout ce qui est spécifique à l’interface avec le TCO virtuel est marqué "TCO" et tout ce qui est spécifique au cab-signal est marqué "CAB". Cette version devient pour l’instant la version de référence, elle peut être utilisée pour d’autres réseaux en réécrivant le fichier "LocodromeBAL".
Dans le gestionnaire le suivi des trains et le cab-signal ont étés ajoutés, une réorganisation des fichiers a aussi été faite, ainsi que quelques modifications et corrections. Le TCO a été l’objet de grosses modifications, outre l’ajout du suivi des trains et du cab-signal, des simplifications on été faites dans le déplacement des trains (simplification du calcul des points de passage et lissage du déplacement, orientation des trains, nouveaux boutons de commande des trains).
Pour essayer le programme de gestion du Locodrome avec le TCO virtuel et faire circuler des trains tout aussi virtuels consulter le fichier "Notice". La discussion est ouverte sur le Forum : Modélisation logicielle d’un réseau - le système de Pierre59.