Sauf que ça scintille plus lol
De mon coté j ai bien superposé un algorithme de Bresenham pour répartir les ON sur le mode multisinus…
Bref chacun fait comme il veut
(20-11-2025, 09:00 PM)59jag a écrit : j ai repris les courbes graphiques sur F1ATB des différents mode pour comparer visuellement
Bonjour,
bravo pour cet outil de comparaison
j'ai apporté quelques retouches :
élargi la plage visible à 2 secondes pour voir l'enchainement de cycles et justifier l'équilibre éléc quand les séquences identiques se suivent
+ ajout d'un peu d'information sur les durées des séquences et % réellement commandé
+ correction pour la séquence demi sinus basé sur 1000ms et pas 990
+ correction pour la séquence train de sinus basé sur 990ms et pas 1000ms
+ différentiation multi sinus tel quel programmé dans RMS (la table codé en dur est écrasé lors de l'exécution du setup) et la retouche de répartition (revue grâce a cette appli / merci) [c'est dans le code JS en PJ]
Hier, 05:40 PM (Modification du message : Aujourd’hui, 08:34 AM par Lolo69.)
Chapeau bas pour ce JS Michy, je vais l enrichir du mode multisinus reparti , pour mieux comprendre les différences
j ai donc ajouté dans le code de JS "mon mode mulitsinus" et génial ce qu' a realisée Michy car ca permet de faire une analyse qui me laisse perplexe lol
En comparant demi sinus et multi sinus on constate
globalement le demi sinus avec des trames sont globalement plus courtes , donc avantage demi sinus
mais finalement l'avantage du demi sinus sur une repartition parfaite fait que visuellement sur les effets d'une ampoule cette régularité la rend plus perceptible par l'oeil, alors que le mutli sinus ( qui est finalement moins reparti) à un meilleur effet de remanence sur la retine, ce qui rend le scintillement moins visible bien que présent
cette régularité "parfaite" augmente les chances de synchronisation avec les modes de mesures "lentes" et donc probabilité plus grande que la mesure soit un peu plus erronée sur un shelly
Si je résume avantage au demi-sinus , mode multi sinus reparti pour les cas ou le scintillement induit sur les ampoules est génant
if (pulseOn === 0) return repartition;
if (pulseOn >= pulseTotal) {
repartition.fill(true);
return repartition;
}
// Pour l'équilibre électrique, on travaille par paires de demi-sinusoïdes
// Si pulseOn est impair, on arrondit au pair supérieur pour la répartition
let pulseOnPaires = Math.floor(pulseOn / 2);
let pulseTotalPaires = Math.floor(pulseTotal / 2);
// Algorithme de Bresenham pour répartir les PAIRES
let erreur = pulseTotalPaires / 2;
let compteurPaires = 0;
for (let i = 0; i < pulseTotalPaires && compteurPaires < pulseOnPaires; i++) {
erreur += pulseOnPaires;
if (erreur >= pulseTotalPaires) {
erreur -= pulseTotalPaires;
// Marquer la paire (positive ET négative)
repartition[i * 2] = true;
if (i * 2 + 1 < pulseTotal) {
repartition[i * 2 + 1] = true;
}
compteurPaires++;
}
}
// Si pulseOn est impair, ajouter une dernière demi-sinusoïde
if (pulseOn % 2 === 1 && pulseOn < pulseTotal) {
// Trouver le premier emplacement libre pour ajouter la demi-sinusoïde impaire
for (let i = 0; i < pulseTotal; i++) {
if (!repartition[i]) {
repartition[i] = true;
break;
}
}
}
return repartition;
}
// =====================================
function remakeTableMS() { // table avec tolérance 0,4%, [interval max = 730ms (1%+0.4% = 1/73 = 1.3698%]
let natif = document.getElementById('natif').checked;
let minimalDuration = natif ? 20 : 2; // en natif le mini est a 20 demi sinus soit 200ms
let erreur = 0.0;
let vrai = 0.0;
let target = 0.0;
for (let I = 0; I < 101; I++) {
tabPulseSinusTotal[I] = -1;
tabPulseSinusOn[I] = -1;
target = I / 100.0;
for (let T = minimalDuration; T < 101; T++) {
for (let N = 0; N <= T; N++) {
if (T % 2 == 1 || N % 2 == 0) { // Valeurs impaires du total ou pulses pairs pour éviter courant continu
vrai = N / T;
erreur = Math.abs(vrai - target);
if (erreur < 0.004) {
tabPulseSinusTotal[I] = T;
tabPulseSinusOn[I] = N;
N = 101;
T = 101;
}
}
}
}
}
if (natif) {trace();return;}
// les tables sont prêtes, on refait une passe pour affiner la précision avec une durée de cycle modifiée
for (let I = 0; I <= 100; I++) {
if ((10000000.0 * tabPulseSinusOn[I] / tabPulseSinusTotal[I]) == (100000 * I)) continue; // on ne fera pas mieux arrondi 5 chiffres apres virgule identique
if ((40.0 * I / 100.0) == Math.floor(40.0 * (I / 100.0))) { // avec 400ms // corrige 5%, 15%, 35%, 45%, 55%, 65%, 85%, 95%
tabPulseSinusTotal[I] = 40;
tabPulseSinusOn[I] = 40.0 * (I / 100.0); // pulses pairs Ok! (multiplie par 0.4) avec 5% comme premiere modif
}
if ((25.0 * I / 100.0) == Math.floor(25.0 * (I / 100.0))) { // avec 250ms // corrige 4%, 8%, 12%, 16%, 24%, 36%, 48%, 52%, 64%, 76, 84%, 92%, 96%
tabPulseSinusTotal[I] = 25; // total impaires 25 Ok!
tabPulseSinusOn[I] = 25 * (I / 100.0);
}
}
// saut de 17% a 18% est precedé par 0.6667 suivi 1.5152 suivi de 0.8658 ==> meilleur regularité avec 7 / 39 passe a 0.6667 => 1.2821 => 1.0989
tabPulseSinusTotal[18] = 39; tabPulseSinusOn[18] = 7; // total impaires Ok!
// saut de 46% a 47% est precedé par 1.1538 suivi 0.5128 suivi de 1.3333 ==> meilleur regularité avec 8 / 17 passe a 1.1538 => 0.9050 => 0.9412
tabPulseSinusTotal[47] = 17; tabPulseSinusOn[47] = 8; // total impaires Ok!
// saut de 53% a 54% est precedé par 1.3333 suivi 0.5128 suivi de 1.1538 ==> meilleur regularité avec 20 / 37 passe a 1.3333 => 0.7207 => 0.9459
tabPulseSinusTotal[54] = 37; tabPulseSinusOn[54] = 20; // total impaires Ok!
// saut de 82% a 83% est precedé par 0.8658 suivi 1.5152 suivi de 0.6667 ==> meilleur regularité avec 34 / 41 passe a 0.8658 => 1.1086 => 1.0732
tabPulseSinusTotal[83] = 41; tabPulseSinusOn[83] = 34; // total impaires Ok!
trace();
}
function calculerLongueurTrame(mode, pourcentage) { // retouche pour affichage % reel qui differe de l'ouverture souhaitée
if (mode === "decoupe") {
let invPI = 1.0 / Math.PI;
let real = 100 * 0.5 * (invPI + invPI * Math.cos(Math.PI * ((100-pourcentage)/100))) / invPI;
let msg = "";
return msg.concat(real.toPrecision(4), "% -> 20"); // reglage sur période d'une demi sinus, l'equilibre électrique se fait sur 2 demi sinus
}
if (mode === "demi") {
if (pourcentage === 0) return 20;
if (pourcentage === 100) return 20;
// cycle basé sur 2000ms mais motif repetitif au mieux
// la decoupe se fait par multiple de 10ms entier
let R = 2000 / pourcentage;
if (pourcentage > 50) R = 2000 / (100 - pourcentage);
let approx = "";
if ( R/10 != Math.floor(R/10)) {
R = Math.floor(Math.floor(R+10)/10)*10;
approx = " Moy ~";
}
return approx + R;
}
if (mode === "multi") {
if (pourcentage === 0) return 20;
if (pourcentage === 100) return 20;
let PulseTotal = tabPulseSinusTotal[pourcentage];
if (tabPulseSinusTotal[pourcentage] % 2 == 1) PulseTotal *= 2; // quand impaire, l'équilibre se fait sur 2 sequences
let real = 100.0 * tabPulseSinusOn[pourcentage]/tabPulseSinusTotal[pourcentage];
let msg = "";
return msg.concat(tabPulseSinusOn[pourcentage], "/", tabPulseSinusTotal[pourcentage], " = ", real.toPrecision(4), "% -> ", PulseTotal * 10); // Nombre de périodes * 10ms
}
// ===== AJOUT: Mode Réparti =====
if (mode === "reparti") {
if (pourcentage === 0) return 20;
if (pourcentage === 100) return 20;
let PulseTotal = tabPulseSinusTotal[pourcentage];
if (tabPulseSinusTotal[pourcentage] % 2 == 1) PulseTotal *= 2;
let real = 100.0 * tabPulseSinusOn[pourcentage]/tabPulseSinusTotal[pourcentage];
let msg = "";
return msg.concat(tabPulseSinusOn[pourcentage], "/", tabPulseSinusTotal[pourcentage], " Bresenham = ", real.toPrecision(4), "% -> ", PulseTotal * 10);
}
// ================================
if (mode === "train") {
let real = Math.min((100.0 * pourcentage / 99.0), 100.0);
let msg = "";
return msg.concat(" Moy ", real.toPrecision(4), "% / 1980"); // équilibre éléctrique après deux cycle de 990ms
}
return 0;
}
function genererSignal(mode, pourcentage, nbPoints) {
const signal = [];
if (mode === "decoupe") {
const retard = 20 - pourcentage / 5;
for (let t = 0; t < 4000; t++) { // courbe sur 2 secondes
if (t % 20 < retard) {
signal[t] = 0;
} else {
const phi = Math.PI * 2 * 50 * t /2000;
signal[t] = Math.sin(phi);
}
}
}
if (mode === "demi") {
let Phase = 0;
let Plot = false;
let last_signe = false;
for (let t = 0; t < 4000; t++) { // courbe sur 2 secondes
const phi = Math.PI * 2 * 50 * t / 2000;
if (t % 20 === 0) {
const phi2 = Math.PI * 2 * 50 * (t + 10) / 2000;
const signe = Math.sin(phi2) > 0;
Phase += pourcentage;
if (Phase >= 100 && last_signe !== signe) { // en demi sinus, pas de notion de 990ms
Plot = true;
Phase -= 100;
last_signe = signe;
} else {
Plot = false;
}
}
signal[t] = Plot ? Math.sin(phi) : 0;
}
}
if (mode === "multi") {
let PulseComptage = -1; // pour demarrer la sequence au bord gauche
const PulseOn = tabPulseSinusOn[pourcentage];
const PulseTotal = tabPulseSinusTotal[pourcentage];
for (let t = 0; t < 4000; t++) { // courbe sur 2 secondes
if (PulseComptage < PulseOn) {
const phi = Math.PI * 2 * 50 * t / 2000;
signal[t] = Math.sin(phi);
} else {
signal[t] = 0;
}
if (t % 20 === 0) {
PulseComptage++;
if (PulseComptage >= PulseTotal) PulseComptage = 0;
}
}
}
// ===== AJOUT: Mode Réparti avec Bresenham =====
if (mode === "reparti") {
const PulseOn = tabPulseSinusOn[pourcentage];
const PulseTotal = tabPulseSinusTotal[pourcentage];
const repartition = genererRepartitionBresenham(PulseOn, PulseTotal);
let PulseComptage = -1; // pour demarrer la sequence au bord gauche
for (let t = 0; t < 4000; t++) {
// Test la condition AVANT d'incrémenter (comme dans multi, mais PulseComptage < PulseOn devient repartition[PulseComptage])
const indexActuel = PulseComptage < 0 ? 0 : PulseComptage;
if (repartition[indexActuel]) {
const phi = Math.PI * 2 * 50 * t / 2000;
signal[t] = Math.sin(phi);
} else {
signal[t] = 0;
}
// Incrément APRÈS, exactement comme dans multi
if (t % 20 === 0) {
PulseComptage++;
if (PulseComptage >= PulseTotal) PulseComptage = 0;
}
}
}
// ==============================================
if (mode === "train") {
for (let t = 0; t < 4000; t++) { // courbe sur 2 secondes
if ((t % 1980) > pourcentage * 20) { // prise en compte du seuil 990ms pour avoir un total impair
signal[t] = 0;
} else {
const phi = Math.PI * 2 * 50 * t / 2000;
signal[t] = Math.sin(phi);
}
}
}
return signal;
}
function tracerGraphe(divId, signal, duree, nbPoints) {
const div = document.querySelector(divId);
const H = div.clientHeight;
const W = div.clientWidth;
let svg = `<svg height="${H}" width="${W}">`;
svg += `<line x1="0" y1="${H * 0.9}" x2="${W}" y2="${H * 0.9}" style="stroke:white;stroke-width:1" />`;
svg += `<polyline points="`;
for (let t = 0; t < nbPoints; t++) {
const Y = H * (0.45 - 0.43 * signal[t]);
const X = W * t / nbPoints;
svg += `${X},${Y} `;
}
svg += `" style="fill:none;stroke:yellow;stroke-width:1" />`;
// Graduation temporelle
const intervalleGrad = duree >= 500 ? 100 : 50;
const pasGrad = intervalleGrad * nbPoints / duree;
for (let t = 0; t < nbPoints; t += pasGrad) {
const X = W * t / nbPoints;
const X3 = W * (t + 4) / nbPoints;
const Y = H * 0.9;
const Y2 = H * 0.95;
const Y3 = H * 0.99;
const T = Math.round(t * duree / nbPoints);
svg += `<line x1="${X}" y1="${Y2}" x2="${X}" y2="${Y}" style="stroke:white;stroke-width:1" />`;
svg += `<text x="${X3}" y="${Y3}" fill="white">${T}</text>`;
}
// Boutons Ouverture
document.getElementById('btnOuverturePlus').addEventListener('click', function() {
let val = parseInt(curseurOuverture.value);
if (val < 100) {
curseurOuverture.value = val + 1;
trace();
}
});
document.getElementById('btnOuvertureMinus').addEventListener('click', function() {
let val = parseInt(curseurOuverture.value);
if (val > 0) {
curseurOuverture.value = val - 1;
trace();
}
});
// Boutons Durée
document.getElementById('btnDureePlus').addEventListener('click', function() {
let val = parseInt(curseurDuree.value);
if (val < 2000) { // courbe sur 2 secondes
curseurDuree.value = val + 50;
trace();
}
});
document.getElementById('btnDureeMinus').addEventListener('click', function() {
let val = parseInt(curseurDuree.value);
if (val > 100) {
curseurDuree.value = val - 50;
trace();
}
});
});
</script>
</body>
</html>
Le demi-sinus à 990ms, c'est pour équilibrer les pulses positifs et négatifs. Si vous avez un nombre impair et vous avez une trame de 1000ms, vous aurez une composante continue sur le long terme.