Rcube

1. Introduction

Le serveur est essentiellement constitué du logiciel r3Server qui gère une API REST pour le calcul de routes.

Il existe aussi des pages statiques (html, css, js...) utiles à l'application. Le logiciel r3Server est aussi un serveur Web, même si il est recommandé d'utiliser Nginx pour :

Un utilitaire r3gribget doit être lancé périodiquement pour récupérer les grib (NOAA, ECMWF, autres).

La "crontable" permet d'automatiser ces lancements, toutes les 6 ou toutes les 12 heures selon les Grib.

En annexe, on trouvera aussi la description d'un autre serveur, utilisé sur poste client qui peut ainsi collecter les information GPS et AIS

2. Procédure d'installation coté serveur

De façon générale, l'installation du serveur suppose :

Les commandes qui suivent sont données pour un environnement Debian (Ubuntu ou autre).

Préliminaires

Vérifier que gcc est installé. Si ce n’est pas le cas, l’installer.


sudo apt install gcc

Installer les bibliothèques

Installez les paquets nécessaires pour curl, eccodes :


sudo apt-get install -y libcurl4 libcurl4-openssl-dev 
sudo apt-get install -y libeccodes0 libeccodes-dev
sudo apt-get install libeccodes-tools  # pour les outils ligne de commande

JSdoc

Il n'est pas nécessaire d'installer JSdoc côté serveur.

Ce peut être néanmoins utile pour générer automatiquement la documentation des fichiers .js hébergés sur le serveur.

Dans ce cas installer d'abord nodejs.

sudo apt update
sudo apt install nodejs npm

Puis JSsoc.

sudo npm install -g jsdoc

Vérifier l'installation avec 

jsdoc --version

Basic Authentication avec NGINX

sudo apt install apache2-utils
Permet d'accéder à la commande htpasswd.

wgrib2

Voir section relative à r3gribget.

Construction du code

Construction de l'arborescence.


mkdir csources gribget currentgrib geo grib par pol wavepol www
cd www
mkdir doc

Dans le répertoire d’exécution, on doit trouver les répertoires suivants :

csources
gribget
currentgrib
geo
grib
par
pol
wavepol
www

csources

csources contient les programmes sources et le Makefile ainsi que ccs.

La production du fichier exécutable nécessite les fichiers sources .c et .h suivants :

engine.c
polar.c
r3grib.c
r3server.c
r3util.c
readgriballeccodes.c
readgriballwithouteccodes.c

r3gribget.c (programme indépendant utilisant quand même glibwrapper.h)

engine.h
glibwrapper.h
grib.h
inline.h
polar.h
r3types.h
r3util.h
readgriball.h

Ainsi que les fichiers :
ccg
ccs
ccs0 : idem ccs mais ne nécessite pas ECCODES
MakeFile

Pour la construction du logiciel r3server, la commande de compilation et d’édition des liens est assurée par le fichier shell exécutable :

./ccs
ou
./ccs0

geo

Le répertoire geo contient les fichiers géographiques.

Le fichier issea.txt permet de savoir si un point de la carte est «en mer» ou «à terre».

Le fichier portprinc.csv donne la latitude et la longitude des ports principaux pour retrouver les marées du SHOM.

grib

Ce répertoire contient les fichiers Grib, téléchargés avec r3gribget.

Il est utile que ce répertoire contienne au moins un fichier. C'est pourquoi il est conseillé de ne pas détruire le petit fichier Grib : reference.grb2, ou de lancer r3gribget pour avoir au moins un fichier dans le répertoire.

gribget

gribget contient le programme source r3gribget.c ainsi que ccg.

currentgrib

Ce répertoire contient les fichiers Grib pour les courants.

www

Ce répertoire contient les fichiers html, css, js textes statiques.

par

Ce répertoire contient les fichiers de paramètres (scénarios) suffixés par “.par”.

Le fichier “.par” par défaut est : routing.par.

La description des paramètres se trouve dans le répertoire helpdir, fichier docpar.txt

pol

Ce répertoire contient les fichiers polaires au format ".pol" ou ".csv".

Les fichiers csv ont pour séparateurs le caractère ";".

Les fichiers pol ont pour séparateurs le caractère tabulation.

wavepol

Ce répertoire contient les fichiers polaires de vagues suffixées par "polwave.csv".

3. Documentation

La documentation est constituée :

4. Lancement de l'application

Le lancement de l’application se fait via la commande :

./r3server <port> [parameter file]

Le numéro de port est obligatoire. Recommandé: 8080

Si le nom du fichier de paramètre est absent, le serveur utilise "routing.par".

5. API

Présentation

L'API est une API REST. Les requêtes sont des requêtes HTTP POST sur le port spécifié lors du lancement du serveur.

Exemple de requête :

curl http://localhost:8080 -d "type=1&boat=hoho,47.0,-3.0&waypoints=47.5,-3.0;47.0,-5.0&timeStep=3600&isoc=false"

Derrière un serveur NGINX répondant en HTTPS et configuré en reverse Proxy  :

curl https://rcube.ddns.net/post-api/ -d "type=1&boat=hoho,47.0,-3.0&waypoints=47.5,-3.0;47.0,-5.0&timeStep=3600&isoc=false"

La commande curl envoie sur le port 8080 (ici au serveur local) la requête HTTP POST dont le contenu est spécifié après -d.

Le résultat de la requête est une réponse JSON.

Détail de la requête

L'ordre dans le quel les paramètres sont indiqués n'a pas d'importance.

Il sont séparés par l'esperluète &.

Exemple :

type=1&boat=hoho, 47.0, -3.0&waypoints=47.5,-3.0;47.0,-5.0&timeStep=3600&isoc=false

Type de la requête

Type de la requête
ValSémantiqueCommentaire
0TestPour vérifier réponse serveur.
1Demande de routage pour un seul bateauLa principale requête.
2Demande d'informations géographiques et météorologiques sur un point.Coordonnées lat, lon du point et optionnellement nom du grib.
3Demande les polygones d'interdictionSans paramètres.
4Demande de polaire.Le nom de la polaire doit être donné.
5Demande des informations Grib.Le nom du fichier doit être donné.
6Liste un répertoire sur le serveur.Le nom du répertoire doit être donné.
7Demande le jeu de paramètres courant au serveur. Réponse au format brut (RAW)Pas d'élément à ajouter.
8Demande le jeu de paramètres courant au serveur. Réponse au format Json.Pas d'élément à ajouter.
9Réinitialise le serveurPas d'élément à ajouter.
10Envoie feedback vers le serveurLe feedback doit être donné.
11Dump fichier texteLe nom du fichier doit être donné..
12Demande le port le plus procheA éviter. Préférer la gestion à partir du client. La latitude et la longitude du point doivent être donnés.
13MarquesPas de paramètre.
14Vérification de gribLe nom du grib vent et du grib courant doivent être donnés.
15Route au format GPXPas de paramètre.
17Dump Grib au format interne float array de uv[gw]Model ou grib. onlyUV

Paramètres de la requêtes

La latitude (lat) est un nombre décimal sur l'intervalle [-90..90].

:%s

La longitude (lon) est un nombre décimal sur l'intervalle [-180..180] ou sur l'intervalle [0..360].

Le tableau suivant indique pour chaque paramètre les dépendance par rapport au type.

Paramètres de la requête
KeyType/unitésValueDépendancesValeur par défaut
typeEntier (0..10)Type de la requête.NAPas de défaut. Champ obligatoire.
boatListe de triplets name, lat, lon;Liste les bateaux.Pour requête 1Pas de défaut. Champ obligatoire.
waypointsListe de couples lat, lon;Liste les Waypoints.Pour requête 1Pas de défaut. Champ obligatoire.
timeStepEntier (secondes)Valeur du temps entre chaque isochrone.Pour requête 13600
epochStartEntier (secondes)Date de départ en temps Epoch Unix.Pour requête 1Valeur actuelle (now) du temps Epoch (Unix).
polarChaîne de caractèresNom de la polaire.Pour requête 1, 4.Par défaut fourni par le serveur.
wavePolarChaîne de caractèresNom de la polaire de vaques.Pour requête 1, 4.Par défaut fourni par le serveur.
modelChaîne de caractèresModel du gribPour requête 1, 5GFS, ECMWF, ARPEGE, METEOCONSULT
gribChaîne de caractèresNom du fichier grib.Pour requête 1, 2, 5.Par défaut fourni par le serveur.
currentGribChaîne de caractèresNom du fichier grib courant.Pour requête 1, 5.Par défaut fourni par le serveur.
fileChaîne de caractèresNom de fichierPour requête 11.vide
dirChaîne de caractèresNom de répertoire. (soit "grib" soit "pol" soit vide).Pour requête 6.vide
forbidBooléen: true | falsePrend en compte terre et zones interdites.Pour requête 1false
isocBooléen: true | falseDemande les isochrones.Pour requête 1false
isodescBooléen: true | falseDemande les descripteurs d'isochrones.Pour requête 1false
withWavesBooléen: true | falseVagues prises en compte.Pour requête 1false
withCurrentBooléen: true | falseCourant pris en compte .Pour requête 1false
sortByNameBooléen: true | falseTri des fichiers par noms si vrai, par date si faux.Pour requête 6.false
cogstepEntierPas de cap en degrés pour calcul isochronesPour requête 15
cogRangeEntierRange en degrés pour calcul isochronePour requête 190
jFactorEntier0 ou valeur positive quelconque.Pour requête 10
kFactorEntier0, 1, 2, 3Pour requête 11
nSectorsEntierNombre de secteursPour requête 1720
penalty0Entier (secondes)Temps pour un virement de bord (tack)Pour requête 10
penalty1Entier (secondes)Temps pour un empannage (gybe)Pour requête 10
penalty2Entier (secondes)Temps pour changement de voile (sail)Pour requête 10
staminaVRDécimal 0 à 110Stamina (énergie) pour Virtual RegattaPour requête 1100
initialAmureEntier 0 ou 1Amure initiale. 0 Tribord, 1 Babord. Pour Virtual RegattaPour requête 1100
motorSpeedDécimalVitesse en noeuds au moteurPour requête 16
thresholdDécimalDéclenchement du moteur si vitesse voile inférieurePour requête 10
nightEfficiencyDécimal 0 à 1Facteur d'efficacité la nuitPour requête 11
dayEfficiencyDécimal 0 à 1Facteur d'efficacité le jourPour requête 11
xWindDécimalCoeff. Multiplicateur de la vitesse du ventPour requête 11
maxWindDécimalVitesse maximale du ventPour requête 1100
constWindTwsDécimalVitesse constante du vents en noeudsPour requête 10
constWindTwdDécimalDirection constante du vents en degrésPour requête 10
constWaveDécimalHauteur constante des vagues en mètresPour requête 10
constCurrentSDécimalVitesse constante du courant en noeudsPour requête 10
constCurrentDDécimalDirection constante du courant en degrésPour requête 10
feedbackChaîne de caractèresTexte rédigé par l'utilisateur.Pour requête 10vide
onlyUVBooléen: true | falseVrai si seulement vent (U, V) demandéPour requête 16false

Utilisation

Requête test type= 0

Permet de vérifier la présence du serveur.

Exemple de Requête :

curl https://rcube.ddns.net/post-api/ -d "type=0"

{
   "Prog-version": "RCube, 0.1, René Rigault",
   "API server port": 8080,
   "Grib Reader": "ECCODES 2.34.1",
   "Memory for Grib Wind": "171 445 680",
   "Memory for Grib Current": "0",
   "Compilation-date": "Oct  6 2025",
   "PID": 9520,
   "Memory usage in KB": "195 484",
   "Client IP Address": "90.12.244.150",
   "User Agent": "curl/8.5.0",
   "Authorization-Level": 0
}
Autre requête possible :
curl http://localhost:8080 -d "type=0"

Requête de routage pour un seul bateau type=1

Lance un routage.

Exemple de Requête :

curl https://rcube.ddns.net/post-api/ -u admin:admin -d "type=1&boat=banane,41.24,-16.78;
&waypoints=43.068888,-40.957031;43.707594,-56.953125
&model=ECMWF
&timeStep=7200
&polar=pol/class40VR.csv

Réponse du serveur :

{
"banane": {
"duration": 710255,
"totDist": 1982.56,
"routingRet": 97,
"isocTimeStep": 7200.00,
"calculationTime": 0.2669,
"destinationReached": true,
"lastStepDuration": [7618.6958, 11437.0309],
"motorDist": 0.00, "starboardDist": 1066.88, "portDist": 915.68,
"nSailChange": 26, "nAmureChange": 7,
"bottomLat": -60.00, "leftLon": -60.00, "topLat": 60.00, "rightLon": 20.00,
"polar": "class40VR.csv",
"wavePolar": "polwave.csv",
"grib": "ECMWF_20251005_12Z_240.grb",
"currentGrib": ".",
"track": [
   [0, 41.244772, -16.787108, 0.000000, 10.712832, 5.356416, 146.477188, 5.366323, 279.003272, -132.448718, 0.000000, 0.000000, 100.000000, "LG", false, -1, -1],
   [0, 41.272475, -17.021744, 7200.000000, 12.523172, 6.261586, 162.450041, 6.299242, 293.914355, -131.380405, 0.000000, 0.000000, 100.000000, "LG", false, 18, -1],
   [0, 41.356804, -17.275940, 14400.000000, 13.085164, 6.542582, 165.576586, 6.768942, 298.634402, -132.973345, 0.000000, 0.000000, 100.000000, "LG", false, 683, 18],
...
]}}

Une route en JSON est générée pour le bateau.

Si le paramètre "isoc" était positionné à true dans la requête, l'isochrone de la route est envoyée.

Si le paramètre "isocdesc" était positionné à true dans la requête, un descripteur d'isochrones de la route est envoyée.

La route "track" elle même est un tableau Json (array).

Chaque élément du tableau est un tableau: 

[wp, lat, lon, time, dist, sog, twd, tws, hdg, twa, g, w, stamina, sail, motor, id, father]

Eléments d'une route
ElémentType/unitésValeur
wpEntier -1, 0...nNo de way point. -1 = destination
latDécimalLatitude
lonDécimalLongitude
timeEntierNombre de seconde à partir du début de la route
distDécimalDistance en miles marin.
sogDécimalSpeed over Ground. Vitesse en noeuds.
twdDécimal [0..360]True Wind Direction. Direction du vent par rapport au Nord en degrés.
twsDécimalTrue Wind Speed. Vitesse du vent en noeuds.
hdgDécimal [0..360]Heading. Direction du bateau en degrés..
twaDécimal [0..360]True Wind Angle. Angle du bateau par rapport au vent en degrés.
gDécimal.Gust. Vitesse du vent dans les rafales.
wDécimal.Waves. Hauteur des vagues en mètres.
staminaDécimalEnergie (concept Virtual Regatta).
sailChaîne de caractèresVoile utilisé.
motorBooléen: true / false Moteur ou non.
idEntierId du point
fatherEntierId du father

Element sur un point type=2

Requête donnant des informations sur un point. La requête a un paramètre qui est un triplet (nom du point, latitude, longitude) avec éventuellement le grib pour les information météo utiles pour un météogramme. Le nom du point n'est pas interprété, mais doit être présent.

Les informations renvoyées indiquent si le point est en mer en zone non interdite, et si il est a l'intérieur des zones Grib vent et courant, le nom du grib courant et le tableau des info météo.

Chaque ligne du tableau correspond à une heure. La première ligne correspond au temps de départ du grib.

Chaque élément du tableau est un tableau de 4 valeurs u, v, g, w.

u, v sont les composantes Nord Sud et Est Ouest du vent en surface en m/s.

g est la vitesses des rafales en m/s.

w est la hauteur des vagues en mètres.

curl https://rcube.ddns.net/post-api/ -u admin:admin -d "type=2&boat=bidon, 41.24, -16.0"
{
  "isSea": true,
  "isSeaTolerant": true,
  "inWind": true,
  "inCurrent": false,
  "epochGribStart": 1743141600,
  "grib": "METEOCONSULT06Z_VENT_0328_Nord_Atlantique.grb",
  "meteoArray":
  [
    [0.0000, 0.0000, 0.0000, 0.0000],
    [0.0000, 0.0000, 0.0000, 0.0000],
    ...
   ]
}


Liste les polygones d'interdiction type=3

Requête sans paramètre, renvoie un tableau de polygones.

Chaque polygones est un tableau de coordonnées.

Une coordonnée est un tableau avec deux éléments : [lat, lon]

Exemple avec deux polygones :

curl https://rcube.ddns.net/post-api/ -d "type=3"

[
  [[-43.5000, -2.7500], [-46.0000, 17.3000], [-48.0000, 35.0000], [-51.0000, 69.5000], [-49.0000, 96.0000]],
  [[-46.0000, 17.3000], [-48.0000, 35.0000], [-51.0000, 69.5000]]
]

Chargement de la polaire type=4

Exemples de Requête :

curl https://rcube.ddns.net/post-api/ -d "type=4"
curl http://localhost:8080 -d "type=4&polar=dufour.csv"

Réponse du serveur :

Le serveur envoie en Json la polaire en Json sous forme de méta-données et d'un tableau array à deux dimensions.

Si la polaire est une composition de polaire, trois tableaux sont générés

Méta information sur un fichier Grib type=5

La requête contient soit :

Exemple de Requête :

curl https://rcube.ddns.net/post-api/ -d "type=5&model=GFS"
ou
curl https://rcube.ddns.net/post-api/ -d "type=5&model=GFS"

Réponse du serveur :

Les méta informations sur le grib et non pas le fichier Grib lui même.

Répertoire type=6

Liste le répertoire donné en paramètre dir=. Ce peut être :

Le paramètre complémentaire sortByName permet d'obtenir la liste des fichiers triés par nom.

Par défaut, les fichiers sont envoyés du plus récent au plus ancien selon leurs dates de modification.

Exemple de Requête :

curl https://rcube.ddns.net/post-api/ -d "type=6&dir=pol&sortByName=true"
ou
curl https://rcube.ddns.net/post-api/ -d "type=6&dir=grib"

Réponse du serveur :

Chaque ligne de tableau est de la forme :
[nom fichier, taille fichier, date de modification]
[
   ["2024-VR-VG-RR.csv", 2162, "2025-01-11 01:52:20"],
   ["2025-Imoca.csv", 4188, "2025-01-03 19:07:14"],
   ["Dufou500GV+FocOriginal.pol", 665, "2024-05-04 15:57:52"]
]

ou
[
   ["GFS_20251006_00Z_384.grb", 137104881, "2025-10-06 07:16:21"],
   ["GFS_20251005_18Z_384.grb", 137297882, "2025-10-06 01:16:19"],
   ["GFS_20251005_12Z_384.grb", 138417282, "2025-10-05 19:16:20"]
]

Paramètres (RAW) type=7

Permet de dumper les paramètres courants du serveur au format brut (RAW).

Voir section suivante pour comprendre les paramètres de configuration du serveur

curl https://rcube.ddns.net/post-api/ -u admin:admin -d "type=7"

Réponse du serveur :

DESC:            Routing parameters. See also docpar.txt for documentation
WD:              /home/rr/routing/
ALLWAYS_SEA:     0
...

Paramètres (Json) type=8

Permet de dumper les paramètres courants du serveur au format Json.

Voir section suivante pour comprendre les paramètres de configuration du serveur

curl https://rcube.ddns.net/post-api/ -d "type=8"

Réponse du serveur :

{
   "wd": "/home/rr/routing/",
   "grib": "GFS_20250429_00Z_384.grb",
   "bottomLat": -20.00, "leftLon": -80.00, "topLat": 60.00, "rightLon": 10.00,
   "currentGrib": ".",
   "polar": "moteur.pol",
   "wavePolar": "polwave.csv",
   "issea": "issea.txt"
}

Réinit type=9

Utilisé notamment pour faire en sorte que le serveur charge le grib le plus récent.

Le script suivant r3init réinitialise l'ensemble des instances.

#!/bin/bash
for port in $(seq 8080 8081);
do
   curl http://localhost:$port -d type=8
done

Feedback type=10

Utilisé pour permettre à l'utilisateur de donner un feedback.

curl https://rcube.ddns.net/post-api/ -d "type=10&feedback=bug report..."

Dump type=11

Utilisé pour télécharger un fichier texte. Mode "RAW".

curl https://rcube.ddns.net/post-api/ -d "type=11&file=routing.log"

Port le plus proche type=12

Utilisé pour trouver le port le plus proche (SHOM) et l'identifiant Marée Info.

Peut être aussi générée à partir du client.

curl https://rcube.ddns.net/post-api/ -d "type=12&waypoints=47.2,-2.5"
{"nearestPort": "LE_POULIGUEN", "idPort": 115}

Marques type=13

Pas de paramètre.

Donne au format Json les marques. Utilisation avec Vitual Regatta.

Vérification de Grib type=14

Paramètres obligatoires : nom du grib vent et du grib pour courant

Renvoie au format text des informations sur la cohérence des grib.

Route au format GPX type 15

Pas de paramètre.

Renvoie la route stockée dans le serveur au format GPX.

Attention en cas d'utilisateurs multiples, c'est la dernière route calculée qui est envoyée, pas nécessairement celle de l'utilisateur qui fait la demande...

Téléchargement du grib type 16

curl -v http://localhost:8080 -o data.bin "type=16&model=GFS"

Le paramètre onlyUV=true peut être ajouté pour indiquer au serveur de ne renvoyer que u et v

Le fichier généré n'est pas au format grib, mais dans un format ad-hoc pour communiquer entre le serveur et le client (Javascript).

C'est un fichier binaire contenant une succession de flottants (codés sur 32 bits).

Chaque point est décrit par un vecteur de shortnames (u, v, g, w)

u et v sont obligatoires

L'entête HTTP contient X-Shortnames:, la liste des shortnames.

g (gust ou rafale) et w (swh ou hauteur des vagues) sont optionnels.

Les possibilités sont donc : uv, uvg, uvw, uvgw.

Le nombre de valeur est déterminé par :

La nombre de float émis est N : nTimeStamp * nLat * nLon * nShortname.

La taille du fichier en octets est : 4 * N.

6. Fichier de configuration du serveur

Les fichiers suffixés par ".par" ou par ".yaml" sont situés dans le répertoire "par".

Le fichier par défaut est "routing.par" et est essentiel pour le bon fonctionnement de RCube.

Un autre nom de fichier peut être spécifié dans la ligne de commande au lancement du serveur.

Un fichier ".par" ou ".yaml" est un fichier texte comportant des lignes de la forme :

NOM: valeur

Des commentaires peuvent être ajoutés après le signe "#".

Le format est compatible avec YAML.

Exemple :


WD: /home/rr/....             # Working directory
SERVER_PORT: 80               # TCP PORT
FORBID_ZONES:
   - [[lat0, lon0], [lat1, lon], [latN, lonN]]
   - ...

...

Informations sur les paramètres : docpar.txt

Exemple de fichier par : ../par/routing.par

7. Code source

Présentation

Le logiciel côté serveur est écrit en langage C.

La librairie eccodes peut être utilisée pour le décodage des fichiers Grib. C'est optionnel, il existe également la possibilité des se passer de eccodes.

Le répertoire csources contient le code C.

Fichiers et actions associées
FichierCommentaire
r3types.hDéfinition des constantes générales #defines, des enum et des typedef.
inline.hDéfinition de petites fonctions inline incluses dans les fichiers .c
glibwrapper.hDéfinition de petites fonctions inline simulant les fonctions éponymes de la GLIB.
engine.c ou enginesimple.cCœur du programme avec calcul des isochrones.
engine.hInterface de engine (prototypes des fonctions externes).
r3grib.cLecture des fichiers Grib et utilitaires Grib.
grib.hInterface de grib (prototypes des fonctions externes).
option.cGère les différentes commandes du mode CLI (voir routing.c
r3util.cFonctions utiles.
r3util.hInterface de r3util (prototypes des fonctions externes).
polar.cLectures des fichiers polaires et utilitaires associés.
polar.hInterface de polar (prototypes des fonctions externes).
readgriballeccodes.cChargement de fichiers Grib en mémoire, utilisant l'API ECMWF ECCODES.
readgriballwithouteccodes.cChargement de fichiers Grib en mémoire, n'utilisant pas l'API ECMWF ECCODES.
readgriball.hInterface commune à readgribeccodes.h et de readgribwithoutccodes.c
r3server.cLe serveur.
routing.cProgramme en mode CLI uniquement.

Style et documentation

Doxygen donc utilisé pour produire la documentation.

L’anglais américain est utilisé pour les identificateurs (constantes, variables, fonction, etc) et les commentaires.

Les commentaires préférés sont de type //.

Les fonctions et typedef sont précédés de commentaires interprétables par Doxygen et précisant la nature du typedef ou de la fonction.

/*! Description */

Les #define sont en majuscules avec _ comme séparateur

		
#define MAX_N_ISOC 512

Les noms des variables et fonction sont en minuscules, en utilisant la notation camel.

Exemple : ceciEstConformeNotationCamel

Les nom des définitions de types commencent par une majuscule, le reste est en majuscule.

Les décimaux sont en général de type double. Le type float n’est utilisé que pour le stockage des grib. Les constantes de type double sont suffixées par .0 au besoin, pour éviter les ambigüités avec les types entiers.

Le type bool est largement utilisé avec les valeurs associées true, false.


myInt = 10;
double myDouble = 10.5;
double myOtherDouble = 10.0;
myBool = false;
const int xTop = 1;

Vérification du code

Des directives de compilation strictes sont utilisées pour vérifier le code voir ccs, ccs0.

cppcheck est utilisé pour compléter la vérification statique du code.

gcc analyser est également utilisé.

Voir le fichier ccheck.

Mesures de performances

Les option de compilation -g et -pg sont à ajouter (compilation et édition de liens).

Un fichier gmon.out est généré.

Exploiter ce fichier par la commande : gprof ./r3server gmon.out > profil.txt

Gestion du temps

La gestion du temps est assez complexe et mérite quelques éclaircissements.

Fichiers Grib

Les fichiers Grib contiennent des informations sur le temps, en heures UTC. Côté serveur, toutes les informations relatives au temps se réfèrent au temps UTC.

Variables globales dans RCube

par.startTimeInHours stocke en heures le temps t comme la différence en heure entre le temps t et le début du grib.

startInfo est une variable globale struct tm qui stocke le temps.

Elle est initialisée à la valeur du temps de début de Grib par la fonction

initStart (&startInfo)

on peut traduire startInfo en heures par rapport au début du Grib avec la fonction suivante :

par.startTimeInHours = getDepartureTimeInHour (&startInfo)

vOffsetLocalUTC stocke la différence en secondes entre le temps UTC et le temps local.

Par exemple, en France la valeur est donc de 3600 ou de 7200, selon l'heure d'été ou d'hiver

Il s'obtient par l'appel à la fonction suivante :

vOffsetLocalUTC = offsetLocalUTC ()

theTime est un double qui stocke la valeur du temps exprimée en heures après le début du grib pour l'affichage de la carte météo

8. Chargement des GRIB

L'utilitaire r3gribget permet télécharger les fichiers Grib de la NOAA et de l'ECMWF.

Utilisation

Synopsys :

r3gribget dir mode [maxStep topLat leftLon bottomLat rightLon]

dir: directory for grib file
mode: 1 NOAA, 2 ECMWF, 3 ARPEGE, 4 AROME

for METEO CONSULT WIND: mode = 50y with no other parameter,
for METEO CONSULT CURRENT: mode = 60y with no other parameter.

Example: ./r3getgrib grib 1 96 60 -20 10 1 # NOAA request up to 96 hours, and specified region
Example: ./r3getgrib grib 1                # NOAA request with default values
Example: ./r3getgrib grib 502              # METEO CONSULT Request for Wind Centre_Atlantique"

maxStep  step maximal (384 pour NOAA)

topLat, letLon, bottomLat, rightLon : Zone géographique

Calcul du run (00Z, 06Z, 12Z, 18Z) automatique en fonction de l'heure UTC et d'un delay (constante dans le code).

Création de fichiers temporaires .tmp

le fichier résultant se trouve dans le répertoire Grib, créé si besoin.

Construction du code

r3gribget est écrit en langage C. Un seul fichier source : r3gribget.c

La bibliothèque curl est utilisée pour le téléchargement des fichiers Grib.

gcc -o r3gribget r3gribget.c -lcurl

La commande de compilation et d’édition des liens est dans le fichier shell exécutable :

./ccg

grib_copy

Cet utilitaire fourni par l'ECMWF avec la bibliothèque ECCODES permet de sélectionner dans un grib les shortnames utiles.

Il est nécessaire pour la gestion des Grib venant de l'ECMWF et de Météo France (AROME et ARPEGE).

wgrib2

wgrib2 : cet utilitaire permet notamment d'extraire un sous ensemble d'un fichier Grib. Il est utilisé dans le cas des téléchargement web sur les sites open data de l'ECMWF et de Météo France. Si non installé, le logiciel fonctionne soit avec la NOAA (fichiers GFS) et avec Meteo Consult..

RCube utilise maintenant wgrib2 (abandon définitif de CDO qui n'est pas compatible Arpege) au travers de la commande extrayant une région.


wgrib2 input.grib -small_grib lonLeft:lonRight latMin:latMax output.grib
wgrib2 /home/rr/rcube/grib/inter0.tmp -small_grib -13:-1 43:48 /home/rr/rcube/grib/inter1.tmp

Si wgrib2 n'est pas installé, le téléchargement direct des fichiers ECMWF et ou Météo France n'est pas possible

Installation de wgrib2 :

Un peu compliquée. Si on dispose déjà d'un exécutable, notez la procédure suivante :

scp from_directory/wgrib2 user@serveur:/home/user/bin
chmod +x /home/user/bin/wgrib2
export PATH=$HOME/bin:$PATH

Installation des dépendances :
sudo apt update
sudo apt install libgfortran5 libgomp1 libgcc-s1

Vérification de l'installation :


wgrib2 -version

9. Génération de grib

Un utilitaire mkgrib.c permet de générer un grib.

Lancement :

./mkgrib spec.yaml
spec.yaml contient les spécifications du fichier en YAML. Il est possible de définir plusieurs "area". Chaque Area défini la zone géographique couverte (latFirst, latLast, lonFirst, lonLast) et l'évolution temporelle du vent (twd en degré, tws en noeuds) et de la hauteur des vaques (w en mètres).)
# Wind specification for mkgrib.c
---
stepHours: 3
# dataDate: 20251008
# dataTime: 1200
areas:
  # bug avec 51 pas avec 53
  - name: 0
    latFirst: 60
    latLast: 0
    lonFirst: -50
    lonLast: 15
    # t twd tws  w
    winds:
      - [0, 0, 20, 0]
      - [50, 0, 20, 0]
      - [70, -90, 5, 0.5]
      - [100, -90, 5, 0.5]
      - [150, 45, 28, 1]
      - [200, 45, 28, 1]
      - [240, 90, 14, 2]
  - name: 1
    latFirst: 30
    latLast: 10
    lonFirst: -40
    lonLast: -15
    # t tmax twd tws  w
    winds:
      - [0, 180, 0, 0]
      - [240, 180, 0, 0]

Sur la durée d'un plateau, les valeurs twd, tws w sont constantes.

Entre plateaux, interpolation linéaire.

10. Hébergement

Prérequis

Pour fonctionner en HTTPS, installer certbot et générer le certificat SSL :

sudo apt update
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d rcube.ddns.net

Renouvellement automatique du certificat (3 mois)

sudo certbot renew --dry-run

Le serveur r3server

Architecture NGINX et r3server

NGINX écoute sur le port 443 (HTTPS), sert les pages statiques situées dans le répertoire www et joue le rôle de proxy pour le serveur API qui fonctionne en HTTP sur les ports 8080, 8081, gère l'authentification "basic" HTTP.

L'application r3server écoute sur les ports 808x. (en fait, une instance pour chaque port).

screenshot

Configuration NGINX

Edition du fichier de configuration

/etc/nginx/sites-available/rcube

upstream api_servers {
    # Load balancing entre les serveurs API internes (en HTTP)
    #least_conn;
    server 127.0.0.1:8080;
    server 127.0.0.1:8081;
}

# Mapping identité niveau
map $remote_user $user_level {
    default 0;     # anonyme ou non authentifié : niveau 0
    alice   1;
    bob     1;
    admin   10;
}

server {
    listen 443 ssl;
    server_name rcube.ddns.net;

    # Certificat SSL géré par Certbot
    ssl_certificate /etc/letsencrypt/live/rcube.ddns.net/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/rcube.ddns.net/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # Fichiers statiques
    location / {
        root /home/rr/rcube/www;
        index index.html;
        try_files $uri $uri/ =404;

        expires -1;  # Désactive le cache
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header X-Frame-Options "DENY";
    }
    # Load Balancer + Auth "optionnelle"
    location /post-api/ {
      # Auth Basic : si échec, on redirige en interne (niveau 0)
      auth_basic "Restricted";
      auth_basic_user_file /etc/nginx/.htpasswd;
      error_page 401 = @anonymous;

      # Retire le préfixe /post-api/ avant d'envoyer au backend
      rewrite ^/post-api/(.*)$ /$1 break;

      # En-têtes vers le backend (auth OK ici)
      proxy_set_header X-User       $remote_user;
      proxy_set_header X-User-Level $user_level;
      proxy_set_header Authorization "";

      # IMPORTANT: pas de partie URI ici non plus (on a déjà réécrit l'URI)
      proxy_pass http://api_servers;
      proxy_http_version 1.1;
      proxy_set_header Connection "";
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

      # Debug optionnel
      # add_header X-User       $remote_user always;
      # add_header X-User-Level $user_level  always;
   }
   location @anonymous {
      # Même réécriture du chemin (pour garder le même upstream URI)
      rewrite ^/post-api/(.*)$ /$1 break;

      proxy_set_header X-User       anonymous;
      proxy_set_header X-User-Level 0;
      proxy_set_header Authorization "";

      # IMPORTANT: pas de partie URI dans une named location
      proxy_pass http://api_servers;
      proxy_http_version 1.1;
      proxy_set_header Connection "";
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

      # Debug optionnel
      # add_header X-User       anonymous always;
      # add_header X-User-Level 0         always;
    }
}

Vérification de la configuration

sudo nginx -t

redémarrage nginx

sudo systemctl restart nginx

Lancement du serveur

On peut lancer plusieurs instances en load balancing

./r3server 8080 &
./r3server 8081 &

Vérification

sudo netstat -tulnp | grep 808

Charge

for i in {1..20}; do curl https://rcube.ddns.net/post-api/ -d "type=0"; sleep 0.5; done

Permet aussi de vérifier le round robin entre ports 8080, 8081...

Pour pouvoir se déconnecter du serveur, le lancer avec la commande r3launch qui contient :

#!/bin/bash
nohup /home/rr/rcube/r3server 8080 /home/rr/rcube/par/routing.par > /home/rr/rcube/r3server_8080.log 2>&1 &
nohup /home/rr/rcube/r3server 8081 /home/rr/rcube/par/routing.par > /home/rr/rcube/r3server_8081.log 2>&1 &

Note: chemins absolus requis

nohup empêche le processus d'être stoppé lors de la déconnexion.

& exécute le processus en arrière-plan.

> r3server.log 2>&1 redirige les sorties standard et d'erreur vers un fichier r3server.log.

Vérifier que le serveur marche

ps aux | grep r3server
ou
pgrep r3server

Arrêter le serveur

kill PID
ou
kill -9 PID
PID est le process ID rendu par la commande ps ou pgrep vue au dessus.

Chargement des grib

Mettre dans la crontable la commande r3gribget avec les bons paramètres.

15 1,7,13,19 * * * /home/rr/rcube/r3gribget /home/rr/rcube/grib 1 384 60 -60 -60 20 >> /home/rr/rcube/r3gribget.log 2>>&1 && /home/rr/rcube/r3init # NOAA
30 4,10,16,22 * * * /home/rr/rcube/r3gribget /home/rr/rcube/grib 500 >> /home/rr/rcube/r3gribget.log  2>&1 # Meteoconsult Wind

0 5,11,17,23 * * * /chemin/vers/r3gribget >paramètres< >> /chemin/vers/r3gribget.log 2>&1

Effacer régulièrement les fichiers Grib anciens.

0 0 * * * find /chemin/vers/grib -type f -name "*.grb" -mtime +4 -exec rm {} \;

Exemple de crontable

Chemins absolus requis.


15 1,7,13,19 * * * /home/rr/rcube/r3gribget /home/rr/rcube/grib 1 384 60 -60 -60 20 >> /home/rr/rcube/r3gribget.log 2>&1 && /home/rr/rcube/r3init # NOAA
30 4,10,16,22 * * * /home/rr/rcube/r3gribget /home/rr/rcube/grib 500 >> /home/rr/rcube/r3gribget.log  2>&1 # Meteoconsult Wind
32 4,10,16,22 * * * /home/rr/rcube/r3gribget /home/rr/rcube/grib 501 >> /home/rr/rcube/r3gribget.log  2>&1 # Meteoconsult Wind
34 4,16 * * * /home/rr/rcube/r3gribget /home/rr/rcube/currentgrib 600 >> /home/rr/rcube/r3gribget.log  2>&1 # Meteoconsult Current
36 4,16 * * * /home/rr/rcube/r3gribget /home/rr/rcube/currentgrib 601 >> /home/rr/rcube/r3gribget.log  2>&1 # Meteoconsult Current
35 1,7,13,19 * * * /home/rr/rcube/r3gribget /home/rr/rcube/grib 3  102 60 -60 -60 20 >> /home/rr/rcube/r3gribget.log  2>&1 # ARPEGE
46 1,13 * * * /home/rr/rcube/r3gribget /home/rr/rcube/grib 2 9 60 -60 -60 20 >> /home/rr/rcube/r3gribget.log 2>&1 # ECMWF
0 0 * * * find /home/rr/rcube/grib -type f -name "*.grb" -mtime +1 -exec rm {} \;
0 0 * * * find /home/rr/rcube/currentgrib -type f -name "*.grb" -mtime +1 -exec rm {} \;
@reboot sleep 60 && /home/rr/rcube/r3launch

Vérifier la planification

crontab -l

Réseau

WAN

NO IP

LAN

11. Authentification

But

NGINX gère le HTTPS et l’authentification HTTP Basic (via htpasswd), puis traduit l’utilisateur en un niveau d’accès (0, 1, ADMIN) qu’il transmet au serveur C dans X-User-Level. Le serveur C applique les droits à partir de ce header.

Flux

  1. Le client appelle l'API avec Basic Auth.
  2. NGINX vérifie les identifiants dans /etc/nginx/.htpasswd.
  3. NGINX associé l’utilisateur validé au niveau et envoie X-User-Level au serveur C.
  4. Le serveur C autorise ou refuse selon X-User-Level.

Création des users

   Premier user :
   sudo htpasswd -cB -C 10 /etc/nginx/.htpasswd alice
   Puis :
   sudo htpasswd -B -C 10 /etc/nginx/.htpasswd bob
 

Commande de test

curl https://rcube.ddns.net/post-api/ -u alice:pistache \
  -d "type=1&boat=hoho, 47.0,-3.0&waypoints=47.5,-3.0;47.0,-5.0&timeStep=3600&isodesc=true&isoc=false"
   

Notes sécurité

11. Comportements bizarres

Pour fonctionner sans NGINX (en local) et éviter les erreurs CORS :

Annexe 1, ServeurGPS et AIS

Introduction

Côté client, il est possible d'installer un petit module ou plugin permettant de disposer des informations AIS et GPS.

La lecture des données se fait directement sur l’interface USB, muni d'un ou plusieurs capteurs (AIS et/ou GPS). Cette méthode fonctionne sous Windows comme sous Unix et Mac OS.

Gestion des ports USB pour AIS et GPS

Identitication des ports

ls -l /dev/tty/USB* /dev/ttyACM*

S'ajouter au groupe Dialout

sudo usermod -aG dialout $USER

Ouvrir le port en mode commande

cat /dev/ttyUSB0

Redémarrer si les droits ne sont pas mis à jour.

Sous Unix et Mac OS, s’assurer que le port a les droits nécessaires. Pour ce faire, passer la commande :

sudo chmod 666 /dev/ttyACM0.
sudo chmod 666 /dev/ttyUSB0.

Attention : Le bon fonctionnement peut nécessiter la déconnexion physique de l’interface USB, sa reconnexion, le passage de la commande changeant les droits d’accès et le relancement de l’application.

Lancement du serveur

./aisgpsserver <port> [fichierParametres]

Utiliser un port non déjà occupé.

./aisgpsserver 8090

param.par est le nom du fichier paramètres par défaut.

Le fichier midcodes.csv permet de traduire les premiers chiffres du MMSI en nom de pays, code ISO et emoticon du drapeau.

Les ports utilisés doivent être spécifiés dans le fichier paramètres

Exemple de fichier paramètres sous Unix :


DESC:            AIS GPS parameters.                        # optionnel
MID_COUNTRY:     /home/rr/routing/geo/midcodes.csv          # optionnel pour traduction mid code en country
NMEA:            /dev/ttyACM0 13                            # 13 pour 9600 bauds
NMEA:            /dev/ttyUSB0 15                            # 15 pour 38400 bauds
SPECIAL:         1                                          # optionnel, permet d'avoir des navires de test AIS
 

Sous Windows, la difficulté est de repérer les noms des ports série COM3, COM4…

La vitesse est donnée explicitement :


NMEA: COM3 9600
NMEA: COM4 38400


Utilisation côté client

Le logiciel client appelle de le serveur avec le mot clef "gps" ou "ais" selon les informations souhaitées

GPS

Les informations renvoyées sont :

curl http://localhost:8090/gps
{
  "time": "1744649357",
  "lat": -0.016667,
  "lon": -2.347094,
  "alt m": 13.90,
  "sog": 3.18,
  "cog": 12.00,
  "numSat": 4,
  "status": "V"
}

AIS

Les informations renvoyées sont pour chaque navire :

curl http://localhost:8090/ais
[
   {"name": "bobo", "messageId": 3, "country": "United Kingdom;GB;🇬🇧", "mindist": 0, "mmsi": 232191800, "lat": 45.3000, "lon": -2.2000, "sog": 15.00, "cog": 315, "lastupdate": 1744706554},
   {"name": "hello", "messageId": 0, "country": "France (Inland);FR;🇫🇷", "mindist": 0, "mmsi": 227191400, "lat": 45.2000, "lon": -2.5000, "sog": 5.00, "cog": 45, "lastupdate": 1744707094},
   {"name": "coco", "messageId": 3, "country": "Spain;ES;🇪🇸", "mindist": 140, "mmsi": 224193900, "lat": 45.4000, "lon": -3.0000, "sog": 0.00, "cog": 180, "lastupdate": 1744707154}
]

Noter que "country" comporte le nom du pays, le code ISO et le code Unicode de l'émoticon du drapeau si le fichier midcodes.csv est référencé dans le fichier paramètres.

Construction de aisgpsserver

La GLIB est ici nécessaire.

Fichiers et actions associées
FichierCommentaire
aisgpsserver.cModule principal.
rtypes.hDéfinition des constantes générales #defines, des enum et des typedef.
r3util.cFonctions utiles.
r3util.hInterface de r3util (prototypes des fonctions externes).
aisgps.cGestion ais et gps
aisgps.hInterface de engine (prototypes des fonctions externes).

Compilation

./ccag

Annexe 2, Composition de Polaires

Le script transpolar transforme le fichier Json fourni par le dashboard de Virtual Regatta en un fichier lisible par jq.

Le script saillist ou list liste les voiles décrites dans le fichier json en question.

Le script jstocsv basé sur jq transforme la structure Json fournie par le dashboard Virtual Regatta (après transformation avec transpolar) en n fichiers CSV.

composepol

Le programme composepol.c lit n fichiers CSV et produit

Lancement de composepol

./composepol -c file1 file2 file3 ...

L'ordre est important. Le fichier VRespol.sailpol contient une matrice dont chaque cellule a la valeur :

La convention généralement adoptée est :

No, Acronymes et signification associée
NoAbréviationvoileCouleur
0NANot applicableblack
1C0Code 0green
2HGHeavy Gennakerpurple
3JibJibgray
4LGLight Gennakerblue
5LJLight Jibyellow
6SpiSpiorange
7SSStay Sailred

Construction du code

Il nécessite :

Pour la construction du code, utiliser : ./ccc.

Enchainement des opérations

allcompose enchaine :

Annexe 3, Transformations de Grib

Cas des fichiers Meteoconsult

La version sans ECCCODES nécessite de traiter les fichiers Meteoconsult de façon à obtenir un format non compressé

La commande wgrib adaptée est automatiquement gérée par r3gribget dans la fonction uncompress :

/*! Uncompress Grib file received from METOCONSULT to Template 5.0 */
static bool uncompress (const char *in, const char *out) {
   char command [MAX_SIZE_LINE * 4];
   snprintf (command, sizeof (command), 
      "wgrib2 %s -set_scaling same same -set_grib_type simple -grib_out %s >/dev/null", in, out);
   return (system (command) == 0);
}

Cas des fichiers Saildoc

La version sans ECCCODES nécessite de traiter les fichiers Saildoc de façon à 

Installation de grb1to2

Ce script NOAA convertit GRIB1→GRIB2 en s’appuyant sur wgrib (GRIB1) + wgrib2.

Installer/Compiler wgrib (GRIB1) :

mkdir -p ~/bin && cd ~/bin
wget -O wgrib.c https://ftp.cpc.ncep.noaa.gov/wd51we/wgrib/wgrib.c
gcc -O2 -o wgrib wgrib.c
chmod +x wgrib


Récupérer le convertisseur et ses deux fichiers compagnons :

# dans ~/bin par ex.
wget https://ftp.cpc.ncep.noaa.gov/wd51we/grb1to2/grb1to2.pl
wget https://ftp.cpc.ncep.noaa.gov/wd51we/grb1to2/grib1to2_metadata.pl
wget https://ftp.cpc.ncep.noaa.gov/wd51we/grb1to2/global_template.g2
chmod +x grb1to2.p

Enchaînement avec simple packing

Le script bash grib1togrib2 prend un entrée un fichier saildoc et produit en sortie un fichier lisible par la version sans ECCOES de Rcube.

#!/bin/bash
in="$1"
out1="${in%.grb}_g2.grb"
out2="${in%.grb}_g2_simple.grb"

# si GRIB1 (octet 8 == 1) → convertir
if [ "$(dd if="$in" bs=1 skip=7 count=1 2 <> /dev/null | od -An -tu1)" -eq 1 ]; then
  ~/bin/grb1to2.pl -packing c3 "$in" -o "$out1" || exit 1
else
  out1="$in"
fi

# forcer simple packing
wgrib2 "$out1" -set_grib_type simple -grib_out "$out2" || exit 1
echo "Output: ${$out2}"

Annexe 4, Exemple de session MAJ server

rr@rr-Zenbook-UX3402ZA-UX3402ZA:~/rcube$ ./upload
Mot de passe SSH : 
www files...
www/doc files...
csources files...
gribget files...
Done
rr@rr-Zenbook-UX3402ZA-UX3402ZA:~/rcube$ ssh rcube
rr@rcube's password: 

Last login: Mon Sep  1 09:35:49 2025 from 2a01:cb05:8f15:5a00:41ed:5ba5:9f25:482f
rr@rcube:~$ cd rcube/csources/
rr@rcube:~/rcube/csources$ ./ccs
rr@rcube:~/rcube/csources$ cd ..
rr@rcube:~/rcube$ pgrep r3server
858891
rr@rcube:~/rcube$ kill 858891
rr@rcube:~/rcube$ ./launch
==== Lancement des serveurs ====
→ Lancement du serveur sur le port 8080
   PID = 866224, log = /home/rr/rcube/r3server_8080.log
✅ Tous les serveurs sont lancés.


Liens

Doxygen Doc

Documentation sur les paramètres

Tous Fichiers sur serveur

Fichiers Grib sur serveur

Fichiers Grib Courants sur serveur

Fichiers Polaires sur serveur

Fichiers Polaires de Vagues sur serveur

Fichiers Grib NOAA

Fichiers Grib ECMWF

Fichiers Grib Météo France

Fichiers Grib Meteo consult