Forum Électro-Bidouilleur

Merci de vous connecter ou de vous inscrire.

Connexion avec identifiant, mot de passe et durée de la session
Recherche avancée  

Nouvelles:

Bienvenue au Forum de discussion Électro-Bidouilleur! Inscrivez-vous pour participer aux discussions!: 
http://forum.bidouilleur.ca/index.php?action=register Les demandes d'inscription sont révisées quotidiennement.

Voir les contributions

Cette section vous permet de consulter les contributions (messages, sujets et fichiers joints) d'un utilisateur. Vous ne pourrez voir que les contributions des zones auxquelles vous avez accès.

Sujets - sylvainmahe

Pages: [1]
1
Vos projets et Montages d'Électronique / Programmer sans <math.h>
« le: décembre 31, 2020, 10:50:42 am »
Bonjour ;)

Je souhaite partager avec vous un travail amateur mais autodidacte que j'ai titré : Programmer sans <math.h>

(cette proposition s'adresse au langage C++ à destination de microcontrôleurs, mais est facilement applicable au C ou à tout autre langage de programmation)


En électronique numérique embarquée qui met en œuvre des calculateurs programmables, et plus largement en programmation générale C++ (le langage utilisé pour écrire les fonctions énoncées ici), la logique pure est généralement accompagnée de calculs arithmétiques pour mener à bien la plupart des projets. Pour se faire, les calculateurs utilisés (soit les microcontrôleurs) sont équipés chacun d'une unité arithmétique et logique, partie du circuit intégré dédiée à de telles opérations, mais ce matériel ne permet pas d'effectuer des opérations arithmétiques autres que les calculs élémentaires, soit les additions, les soustractions, les multiplications, ou les divisions :

Comprendre que des opérations plus complexes comme par exemple sinus ou cosinus (que l'on retrouve habituellement dans les calculatrices modernes), ne sont pas câblées dans ce type de circuit intégré (comme c'est le cas dans la majorité des microcontrôleurs).

En conséquence, les opérations mathématiques plus élaborées doivent être écrites manuellement lors de la programmation logicielle, ceci en utilisant un certain nombre d'instructions logiques de base (structures conditionnelles, comparaisons, décalages) et d'instructions arithmétiques simples (additions, soustractions, multiplications, et divisions), seules disponibles dans les architectures matérielles des microcontrôleurs.

Cette recherche et construction logicielle effectuée, toutes les opérations (dont les ensembles constituent les algorithmes) qui œuvrent dans le fonctionnement intime de sinus ou cosinus (pour reprendre l'exemple) peuvent pour une question pratique de lisibilité et portabilité du programme être encapsulées dans des fonctions autonomes dédiées, autrement-dit regroupées dans des blocs de code distincts contenant chacun toutes les opérations nécessaires à leur fonctionnement interne, de sorte de pouvoir à posteriori en faire usage facilement en un minimum de lignes de programmation, ceci sans avoir besoin de ressources annexes externes. C'est ce que permettent les fonctions mathématiques décrites ci-dessous.

Descriptif des fonctions mathématiques :

- floor : arrondit un nombre décimal à l'entier inférieur.
- round : arrondit un nombre décimal à l'entier le plus proche.
- ceil : arrondit un nombre décimal à l'entier supérieur.
- curve : créé des interpolations linéaires ou exponentielles de courbes.
- wurve : créé des interpolations linéaires ou exponentielles dissymétriques de courbes.
- range : cadre une valeur dans un intervalle.
- center : calcule la moyenne de deux valeurs.
- pow : calcule la puissance d'un nombre.
- sqrt : calcule la racine carrée d'un nombre.
- fact : calcule la factorielle d'un nombre.
- sin : trouve le sinus d'un angle.
- cos : trouve le cosinus d'un angle.
- tan : trouve la tangente d'un angle.
- arcsin : trouve l'angle à partir d'un sinus.
- arccos : trouve l'angle à partir d'un cosinus.
- arctan : trouve l'angle à partir d'une tangente.
- arctan2 : trouve l'angle à partir de la tangente de deux arguments x et y.

Il est important d'appréhender que les fonctions mathématiques implémentées ne sont qu'une des façons possibles d'effectuer les calculs et résoudre les fonctions énoncées, ceci dans un certain langage de programmation (le C++), et d'une manière subjective avec une approche et une résolution des problématiques tout à fait singulière et arbitraire, modeste expression partagée libre et ouverte de son auteur.





Détails des fonctions

Fonction floor :
signed long floor (const float VALUE)
{
signed long value = 0;

if (VALUE <= 0)
{
value = VALUE - 1.0;
}
else
{
value = VALUE;
}

return value;
}

Fonction round :
signed long round (const float VALUE)
{
signed long value = 0;

if (VALUE <= 0)
{
value = VALUE - 0.5;
}
else
{
value = VALUE + 0.5;
}

return value;
}

Fonction ceil :
signed long ceil (const float VALUE)
{
signed long value = 0;

if (VALUE <= 0)
{
value = VALUE;
}
else
{
value = VALUE + 1.0;
}

return value;
}

Fonction curve :
float curve (const float POSITION_START, const float POSITION_CURRENT, const float POSITION_END, const float INTERPOLATION_START, const float INTERPOLATION_END, const float CURVE)
{
float value = 0;

if ((POSITION_START < POSITION_END && POSITION_CURRENT <= POSITION_START) || (POSITION_START > POSITION_END && POSITION_CURRENT >= POSITION_START))
{
value = INTERPOLATION_START;
}
else if ((POSITION_START < POSITION_END && POSITION_CURRENT >= POSITION_END) || (POSITION_START > POSITION_END && POSITION_CURRENT <= POSITION_END))
{
value = INTERPOLATION_END;
}
else
{
if (INTERPOLATION_END > INTERPOLATION_START)
{
if (CURVE == 0)
{
value = INTERPOLATION_START + ((INTERPOLATION_END - INTERPOLATION_START) * ((POSITION_CURRENT - POSITION_START) / (POSITION_END - POSITION_START)));
}
else if (CURVE > 0)
{
value = INTERPOLATION_START + (((INTERPOLATION_END - INTERPOLATION_START) * ((POSITION_CURRENT - POSITION_START) / (POSITION_END - POSITION_START))) / ((CURVE + 1.0) - (CURVE * ((POSITION_CURRENT - POSITION_START) / (POSITION_END - POSITION_START)))));
}
else
{
value = INTERPOLATION_END - (((INTERPOLATION_END - INTERPOLATION_START) * ((POSITION_CURRENT - POSITION_END) / (POSITION_START - POSITION_END))) / (1.0 + (-CURVE * ((POSITION_CURRENT - POSITION_START) / (POSITION_END - POSITION_START)))));
}
}
else
{
if (CURVE == 0)
{
value = INTERPOLATION_START - ((INTERPOLATION_START - INTERPOLATION_END) * ((POSITION_CURRENT - POSITION_START) / (POSITION_END - POSITION_START)));
}
else if (CURVE > 0)
{
value = INTERPOLATION_START - (((INTERPOLATION_START - INTERPOLATION_END) * ((POSITION_CURRENT - POSITION_START) / (POSITION_END - POSITION_START))) / ((CURVE + 1.0) - (CURVE * ((POSITION_CURRENT - POSITION_START) / (POSITION_END - POSITION_START)))));
}
else
{
value = INTERPOLATION_END + (((INTERPOLATION_START - INTERPOLATION_END) * ((POSITION_CURRENT - POSITION_END) / (POSITION_START - POSITION_END))) / (1.0 + (-CURVE * ((POSITION_CURRENT - POSITION_START) / (POSITION_END - POSITION_START)))));
}
}
}

return value;
}

Fonction wurve :
float wurve (const float POSITION_START, const float POSITION_CURRENT, const float POSITION_END, const float INTERPOLATION_START, const float INTERPOLATION_CENTER, const float INTERPOLATION_END, const float CURVE_START, const float CURVE_END)
{
const float POSITION_CENTER = POSITION_START + ((POSITION_END - POSITION_START) / 2.0);
float value = 0;

if ((POSITION_START < POSITION_END && POSITION_CURRENT < POSITION_CENTER) || (POSITION_START > POSITION_END && POSITION_CURRENT > POSITION_CENTER))
{
if ((POSITION_START < POSITION_CENTER && POSITION_CURRENT <= POSITION_START) || (POSITION_START > POSITION_CENTER && POSITION_CURRENT >= POSITION_START))
{
value = INTERPOLATION_START;
}
else if ((POSITION_START < POSITION_CENTER && POSITION_CURRENT >= POSITION_CENTER) || (POSITION_START > POSITION_CENTER && POSITION_CURRENT <= POSITION_CENTER))
{
value = INTERPOLATION_CENTER;
}
else
{
if (INTERPOLATION_CENTER > INTERPOLATION_START)
{
if (CURVE_START == 0)
{
value = INTERPOLATION_START + ((INTERPOLATION_CENTER - INTERPOLATION_START) * ((POSITION_CURRENT - POSITION_START) / (POSITION_CENTER - POSITION_START)));
}
else if (CURVE_START > 0)
{
value = INTERPOLATION_CENTER - (((INTERPOLATION_CENTER - INTERPOLATION_START) * ((POSITION_CURRENT - POSITION_CENTER) / (POSITION_START - POSITION_CENTER))) / (1.0 + (CURVE_START * ((POSITION_CURRENT - POSITION_START) / (POSITION_CENTER - POSITION_START)))));
}
else
{
value = INTERPOLATION_START + (((INTERPOLATION_CENTER - INTERPOLATION_START) * ((POSITION_CURRENT - POSITION_START) / (POSITION_CENTER - POSITION_START))) / ((-CURVE_START + 1.0) - (-CURVE_START * ((POSITION_CURRENT - POSITION_START) / (POSITION_CENTER - POSITION_START)))));
}
}
else
{
if (CURVE_START == 0)
{
value = INTERPOLATION_START - ((INTERPOLATION_START - INTERPOLATION_CENTER) * ((POSITION_CURRENT - POSITION_START) / (POSITION_CENTER - POSITION_START)));
}
else if (CURVE_START > 0)
{
value = INTERPOLATION_CENTER + (((INTERPOLATION_START - INTERPOLATION_CENTER) * ((POSITION_CURRENT - POSITION_CENTER) / (POSITION_START - POSITION_CENTER))) / (1.0 + (CURVE_START * ((POSITION_CURRENT - POSITION_START) / (POSITION_CENTER - POSITION_START)))));
}
else
{
value = INTERPOLATION_START - (((INTERPOLATION_START - INTERPOLATION_CENTER) * ((POSITION_CURRENT - POSITION_START) / (POSITION_CENTER - POSITION_START))) / ((-CURVE_START + 1.0) - (-CURVE_START * ((POSITION_CURRENT - POSITION_START) / (POSITION_CENTER - POSITION_START)))));
}
}
}
}
else if ((POSITION_START < POSITION_END && POSITION_CURRENT > POSITION_CENTER) || (POSITION_START > POSITION_END && POSITION_CURRENT < POSITION_CENTER))
{
if ((POSITION_CENTER < POSITION_END && POSITION_CURRENT <= POSITION_CENTER) || (POSITION_CENTER > POSITION_END && POSITION_CURRENT >= POSITION_CENTER))
{
value = INTERPOLATION_CENTER;
}
else if ((POSITION_CENTER < POSITION_END && POSITION_CURRENT >= POSITION_END) || (POSITION_CENTER > POSITION_END && POSITION_CURRENT <= POSITION_END))
{
value = INTERPOLATION_END;
}
else
{
if (INTERPOLATION_END > INTERPOLATION_CENTER)
{
if (CURVE_END == 0)
{
value = INTERPOLATION_CENTER + ((INTERPOLATION_END - INTERPOLATION_CENTER) * ((POSITION_CURRENT - POSITION_CENTER) / (POSITION_END - POSITION_CENTER)));
}
else if (CURVE_END > 0)
{
value = INTERPOLATION_CENTER + (((INTERPOLATION_END - INTERPOLATION_CENTER) * ((POSITION_CURRENT - POSITION_CENTER) / (POSITION_END - POSITION_CENTER))) / ((CURVE_END + 1.0) - (CURVE_END * ((POSITION_CURRENT - POSITION_CENTER) / (POSITION_END - POSITION_CENTER)))));
}
else
{
value = INTERPOLATION_END - (((INTERPOLATION_END - INTERPOLATION_CENTER) * ((POSITION_CURRENT - POSITION_END) / (POSITION_CENTER - POSITION_END))) / (1.0 + (-CURVE_END * ((POSITION_CURRENT - POSITION_CENTER) / (POSITION_END - POSITION_CENTER)))));
}
}
else
{
if (CURVE_END == 0)
{
value = INTERPOLATION_CENTER - ((INTERPOLATION_CENTER - INTERPOLATION_END) * ((POSITION_CURRENT - POSITION_CENTER) / (POSITION_END - POSITION_CENTER)));
}
else if (CURVE_END > 0)
{
value = INTERPOLATION_CENTER - (((INTERPOLATION_CENTER - INTERPOLATION_END) * ((POSITION_CURRENT - POSITION_CENTER) / (POSITION_END - POSITION_CENTER))) / ((CURVE_END + 1.0) - (CURVE_END * ((POSITION_CURRENT - POSITION_CENTER) / (POSITION_END - POSITION_CENTER)))));
}
else
{
value = INTERPOLATION_END + (((INTERPOLATION_CENTER - INTERPOLATION_END) * ((POSITION_CURRENT - POSITION_END) / (POSITION_CENTER - POSITION_END))) / (1.0 + (-CURVE_END * ((POSITION_CURRENT - POSITION_CENTER) / (POSITION_END - POSITION_CENTER)))));
}
}
}
}
else
{
value = INTERPOLATION_CENTER;
}

return value;
}

Fonction range :
float range (const float RANGE_START, const float VALUE_CURRENT, const float RANGE_END)
{
float value = 0;

if (RANGE_START < RANGE_END)
{
if (VALUE_CURRENT <= RANGE_START)
{
value = RANGE_START;
}
else if (VALUE_CURRENT >= RANGE_END)
{
value = RANGE_END;
}
else
{
value = VALUE_CURRENT;
}
}
else if (RANGE_START > RANGE_END)
{
if (VALUE_CURRENT <= RANGE_END)
{
value = RANGE_END;
}
else if (VALUE_CURRENT >= RANGE_START)
{
value = RANGE_START;
}
else
{
value = VALUE_CURRENT;
}
}
else
{
value = RANGE_START;
}

return value;
}

Fonction center :
float center (const float VALUE_START, const float VALUE_END)
{
float value = 0;

if (VALUE_START == VALUE_END)
{
value = VALUE_START;
}
else
{
value = VALUE_START + ((VALUE_END - VALUE_START) / 2.0);
}

return value;
}

Fonction pow :
float pow (const float VALUE, const signed long EXPONENT)
{
signed long iterationPower = 0;
float value = 0;

if (EXPONENT < 0)
{
if (VALUE < 0)
{
value = -VALUE;
}
else
{
value = VALUE;
}

for (iterationPower = -1; iterationPower > EXPONENT; iterationPower--)
{
value *= VALUE;
}

value = 1.0 / value;

if (VALUE < 0)
{
value = -value;
}
}
else if (EXPONENT == 0)
{
if (VALUE < 0)
{
value = -1;
}
else
{
value = 1;
}
}
else
{
if (VALUE < 0)
{
value = -VALUE;
}
else
{
value = VALUE;
}

for (iterationPower = 1; iterationPower < EXPONENT; iterationPower++)
{
value *= VALUE;
}

if (VALUE < 0)
{
value = -value;
}
}

return value;
}

Fonction sqrt :
float sqrt (const float RADICAND)
{
unsigned char iterationPrecision = 0;
float searchDecimal = 0.1;
float foundDecimal = 0;
bool quickSearch = false;
unsigned long searchInteger = 1000;
unsigned int incrementInteger = 1000;
float sr1 = 0;
float sr2 = 0;
float sr3 = 0;
float value = 0;

if (RADICAND < 1)
{
while (searchDecimal * searchDecimal <= RADICAND)
{
searchDecimal += 0.1;
}

for (iterationPrecision = 0; iterationPrecision < 4; iterationPrecision++)
{
foundDecimal = ((RADICAND / searchDecimal) + searchDecimal) / 2.0;
searchDecimal = foundDecimal;
}

value = foundDecimal;
}
else if (RADICAND == 1)
{
value = 1;
}
else
{
while (quickSearch == false)
{
while (searchInteger * searchInteger <= RADICAND)
{
searchInteger += incrementInteger;
}

searchInteger -= incrementInteger;

if (incrementInteger == 1)
{
quickSearch = true;
}
else
{
incrementInteger /= 10;
}
}

sr1 = RADICAND - (float (searchInteger) * float (searchInteger));
sr2 = sr1 / (2.0 * float (searchInteger));
sr3 = float (searchInteger) + sr2;

value = sr3 - ((sr2 * sr2) / (2.0 * sr3));
}

return value;
}

Fonction fact :
unsigned long long fact (const unsigned long INTEGER)
{
unsigned long iterationFactorial = 1;
unsigned long long value = 1;

for (iterationFactorial = 1; iterationFactorial <= INTEGER; iterationFactorial++)
{
value *= iterationFactorial;
}

return value;
}

Fonction sin :
float sin (const float ANGLE)
{
unsigned long iterationModulo = 0;
unsigned long foundMultiply = 0;
bool quickSearch = false;
float angle = 0;
float alpha = 0;
float alpha2 = 0;
float alpha3 = 0;
float alpha5 = 0;
float sinus = 0;

if (ANGLE >= -180 && ANGLE <= 180)
{
angle = ANGLE;
}
else if (ANGLE < -180)
{
while (quickSearch == false)
{
for (iterationModulo = 1; ANGLE + (360.0 * (float (foundMultiply) + float (iterationModulo))) < -180; iterationModulo *= 10)
{
}

if (iterationModulo == 1)
{
quickSearch = true;
}
else
{
foundMultiply += iterationModulo / 10ul;
}
}

foundMultiply++;

angle = ANGLE + (360.0 * float (foundMultiply));
}
else
{
while (quickSearch == false)
{
for (iterationModulo = 1; ANGLE - (360.0 * (float (foundMultiply) + float (iterationModulo))) > 180; iterationModulo *= 10)
{
}

if (iterationModulo == 1)
{
quickSearch = true;
}
else
{
foundMultiply += iterationModulo / 10ul;
}
}

foundMultiply++;

angle = ANGLE - (360.0 * float (foundMultiply));
}

if (angle == -90 || angle == 270)
{
sinus = -1;
}
else if (angle == -270 || angle == 90)
{
sinus = 1;
}
else if (angle != -360 && angle != -180 && angle != 0 && angle != 180 && angle != 360)
{
alpha = angle * 0.0174532925199432;

alpha2 = alpha * alpha;
alpha3 = alpha2 * alpha;
alpha5 = alpha2 * alpha3;

sinus = alpha - (alpha3 / 6.0) + (alpha5 / 120.0) - ((alpha2 * alpha5) / 5040.0);
}

return sinus;
}

Fonction cos :
float cos (const float ANGLE)
{
unsigned long iterationModulo = 0;
unsigned long foundMultiply = 0;
bool quickSearch = false;
float angle = 0;
float alpha = 0;
float alpha2 = 0;
float alpha4 = 0;
float cosinus = 0;

if (ANGLE >= -180 && ANGLE <= 180)
{
angle = ANGLE;
}
else if (ANGLE < -180)
{
while (quickSearch == false)
{
for (iterationModulo = 1; ANGLE + (360.0 * (float (foundMultiply) + float (iterationModulo))) < -180; iterationModulo *= 10)
{
}

if (iterationModulo == 1)
{
quickSearch = true;
}
else
{
foundMultiply += iterationModulo / 10ul;
}
}

foundMultiply++;

angle = ANGLE + (360.0 * float (foundMultiply));
}
else
{
while (quickSearch == false)
{
for (iterationModulo = 1; ANGLE - (360.0 * (float (foundMultiply) + float (iterationModulo))) > 180; iterationModulo *= 10)
{
}

if (iterationModulo == 1)
{
quickSearch = true;
}
else
{
foundMultiply += iterationModulo / 10ul;
}
}

foundMultiply++;

angle = ANGLE - (360.0 * float (foundMultiply));
}

if (angle == -180 || angle == 180)
{
cosinus = -1;
}
else if (angle == -360 || angle == 0 || angle == 360)
{
cosinus = 1;
}
else if (angle != -270 && angle != -90 && angle != 90 && angle != 270)
{
alpha = angle * 0.0174532925199432;

alpha2 = alpha * alpha;
alpha4 = alpha2 * alpha2;

cosinus = 1.0 - ((alpha2 / 2.0) - (alpha4 / 24.0) + ((alpha2 * alpha4) / 720.0) - ((alpha4 * alpha4) / 40320.0));
}

return cosinus;
}

Fonction tan :
float tan (const float ANGLE)
{
unsigned long iterationModulo = 0;
unsigned long foundMultiply = 0;
bool quickSearch = false;
float angle = 0;
float alpha = 0;
float alpha2 = 0;
float alpha3 = 0;
float alpha5 = 0;
float tangent = 0;

if (ANGLE >= -90 && ANGLE <= 90)
{
angle = ANGLE;
}
else if (ANGLE < -90)
{
while (quickSearch == false)
{
for (iterationModulo = 1; ANGLE + (180.0 * (float (foundMultiply) + float (iterationModulo))) < -90; iterationModulo *= 10)
{
}

if (iterationModulo == 1)
{
quickSearch = true;
}
else
{
foundMultiply += iterationModulo / 10ul;
}
}

foundMultiply++;

angle = ANGLE + (180.0 * float (foundMultiply));
}
else
{
while (quickSearch == false)
{
for (iterationModulo = 1; ANGLE - (180.0 * (float (foundMultiply) + float (iterationModulo))) > 90; iterationModulo *= 10)
{
}

if (iterationModulo == 1)
{
quickSearch = true;
}
else
{
foundMultiply += iterationModulo / 10ul;
}
}

foundMultiply++;

angle = ANGLE - (180.0 * float (foundMultiply));
}

if (angle == -45)
{
tangent = -1;
}
else if (angle == -90 || angle == 0 || angle == 90)
{
tangent = 0;
}
else if (angle == 45)
{
tangent = 1;
}
else
{
if (angle > -45 && angle < 45)
{
alpha = angle * 0.0174532925199432;
}
else
{
if (angle < 0)
{
alpha = (-90.0 - angle) * 0.0174532925199432;
}
else
{
alpha = (90.0 - angle) * 0.0174532925199432;
}
}

alpha2 = alpha * alpha;
alpha3 = alpha2 * alpha;
alpha5 = alpha2 * alpha3;

if (angle > -45 && angle < 45)
{
tangent = alpha + ((alpha3 / 3.0) + ((2.0 * alpha5) / 15.0) + ((17.0 * (alpha5 * alpha2)) / 315.0));
}
else
{
tangent = 1.0 / (alpha + ((alpha3 / 3.0) + ((2.0 * alpha5) / 15.0) + ((17.0 * (alpha5 * alpha2)) / 315.0)));
}
}

return tangent;
}

Fonction arcsin :
float arcsin (const float SINUS)
{
float angle = 0;
float anglePrevious = 0;
float sinus = 0;
float sinusPositive = 0;
float alpha = 0;
float alpha2 = 0;
float alpha3 = 0;
float alpha5 = 0;
float incrementAngle = 10;
unsigned char decimal = 0;
unsigned char error = 0;
bool quickSearch = false;

if (SINUS <= -1)
{
angle = -90;
}
else if (SINUS == 0)
{
angle = 0;
}
else if (SINUS >= 1)
{
angle = 90;
}
else
{
if (SINUS < 0)
{
sinusPositive = -SINUS;
}
else
{
sinusPositive = SINUS;
}

while (quickSearch == false)
{
alpha = angle * 0.0174532925199432;

alpha2 = alpha * alpha;
alpha3 = alpha2 * alpha;
alpha5 = alpha2 * alpha3;

sinus = alpha - (alpha3 / 6.0) + (alpha5 / 120.0) - ((alpha2 * alpha5) / 5040.0);

if (sinus < sinusPositive && incrementAngle >= 1)
{
anglePrevious = angle;
angle += incrementAngle;
}
else if (sinus < sinusPositive && incrementAngle < 1 && error < 9)
{
anglePrevious = angle;
angle += incrementAngle;

error++;
}
else if (sinus == sinusPositive || decimal == 4 + 1)
{
quickSearch = true;
}
else
{
incrementAngle /= 10;
angle = anglePrevious + incrementAngle;

decimal++;
error = 0;
}

if (angle > 90.0001)
{
angle = 89.9999;
quickSearch = true;
}
}

if (SINUS < 0)
{
angle = -angle;
}
}

return angle;
}

Fonction arccos :
float arccos (const float COSINUS)
{
float angle = 0;
float anglePrevious = 0;
float cosinus = 0;
float cosinusPositive = 0;
float alpha = 0;
float alpha2 = 0;
float alpha4 = 0;
float incrementAngle = 10;
unsigned char decimal = 0;
unsigned char error = 0;
bool quickSearch = false;

if (COSINUS <= -1)
{
angle = 180;
}
else if (COSINUS == 0)
{
angle = 90;
}
else if (COSINUS >= 1)
{
angle = 0;
}
else
{
if (COSINUS < 0)
{
cosinusPositive = -COSINUS;
}
else
{
cosinusPositive = COSINUS;
}

while (quickSearch == false)
{
alpha = angle * 0.0174532925199432;

alpha2 = alpha * alpha;
alpha4 = alpha2 * alpha2;

cosinus = 1.0 - ((alpha2 / 2.0) - (alpha4 / 24.0) + ((alpha2 * alpha4) / 720.0) - ((alpha4 * alpha4) / 40320.0));

if (cosinus > cosinusPositive && incrementAngle >= 1)
{
anglePrevious = angle;
angle += incrementAngle;
}
else if (cosinus > cosinusPositive && incrementAngle < 1 && error < 9)
{
anglePrevious = angle;
angle += incrementAngle;

error++;
}
else if (cosinus == cosinusPositive || decimal == 4 + 1)
{
quickSearch = true;
}
else
{
incrementAngle /= 10;
angle = anglePrevious + incrementAngle;

decimal++;
error = 0;
}

if (angle > 90.0001)
{
angle = 89.9999;
quickSearch = true;
}
}

if (COSINUS < 0)
{
angle = 180.0 - angle;
}
}

return angle;
}

Fonction arctan :
float arctan (const float TANGENT)
{
float angle = 0;
float anglePrevious = 0;
float sinus = 0;
float cosinus = 0;
float tangent = 0;
float tangentPositive = 0;
float alpha = 0;
float alpha2 = 0;
float alpha3 = 0;
float alpha4 = 0;
float alpha5 = 0;
float incrementAngle = 10;
unsigned char decimal = 0;
unsigned char error = 0;
bool quickSearch = false;

if (TANGENT <= -9999999999999999)
{
angle = -90;
}
else if (TANGENT == -1)
{
angle = -45;
}
else if (TANGENT == 0)
{
angle = 0;
}
else if (TANGENT == 1)
{
angle = 45;
}
else if (TANGENT >= 9999999999999999)
{
angle = 90;
}
else
{
if (TANGENT < 0)
{
tangentPositive = -TANGENT;
}
else
{
tangentPositive = TANGENT;
}

while (quickSearch == false)
{
alpha = angle * 0.0174532925199432;

alpha2 = alpha * alpha;
alpha3 = alpha2 * alpha;
alpha4 = alpha2 * alpha2;
alpha5 = alpha2 * alpha3;

sinus = alpha - (alpha3 / 6.0) + (alpha5 / 120.0) - ((alpha2 * alpha5) / 5040.0);
cosinus = 1.0 - ((alpha2 / 2.0) - (alpha4 / 24.0) + ((alpha2 * alpha4) / 720.0) - ((alpha4 * alpha4) / 40320.0));
tangent = sinus / cosinus;

if (tangent < tangentPositive && incrementAngle >= 1)
{
anglePrevious = angle;
angle += incrementAngle;
}
else if (tangent < tangentPositive && incrementAngle < 1 && error < 9)
{
anglePrevious = angle;
angle += incrementAngle;

error++;
}
else if (tangent == tangentPositive || decimal == 4 + 1)
{
quickSearch = true;
}
else
{
incrementAngle /= 10;
angle = anglePrevious + incrementAngle;

decimal++;
error = 0;
}

if (angle > 90.0001)
{
angle = 89.9999;
quickSearch = true;
}
}

if (TANGENT < 0)
{
angle = -angle;
}
}

return angle;
}

Fonction arctan2 :
float arctan2 (const float X, const float Y)
{
const float TANGENT = Y / X;
float angle = 0;
float anglePrevious = 0;
float sinus = 0;
float cosinus = 0;
float tangent = 0;
float tangentPositive = 0;
float alpha = 0;
float alpha2 = 0;
float alpha3 = 0;
float alpha4 = 0;
float alpha5 = 0;
float incrementAngle = 10;
unsigned char decimal = 0;
unsigned char error = 0;
bool quickSearch = false;

if (TANGENT <= -9999999999999999)
{
angle = -90;
}
else if (TANGENT == -1)
{
angle = -45;
}
else if (TANGENT == 0)
{
angle = 0;
}
else if (TANGENT == 1)
{
angle = 45;
}
else if (TANGENT >= 9999999999999999)
{
angle = 90;
}
else
{
if (TANGENT < 0)
{
tangentPositive = -TANGENT;
}
else
{
tangentPositive = TANGENT;
}

while (quickSearch == false)
{
alpha = angle * 0.0174532925199432;

alpha2 = alpha * alpha;
alpha3 = alpha2 * alpha;
alpha4 = alpha2 * alpha2;
alpha5 = alpha2 * alpha3;

sinus = alpha - (alpha3 / 6.0) + (alpha5 / 120.0) - ((alpha2 * alpha5) / 5040.0);
cosinus = 1.0 - ((alpha2 / 2.0) - (alpha4 / 24.0) + ((alpha2 * alpha4) / 720.0) - ((alpha4 * alpha4) / 40320.0));
tangent = sinus / cosinus;

if (tangent < tangentPositive && incrementAngle >= 1)
{
anglePrevious = angle;
angle += incrementAngle;
}
else if (tangent < tangentPositive && incrementAngle < 1 && error < 9)
{
anglePrevious = angle;
angle += incrementAngle;

error++;
}
else if (tangent == tangentPositive || decimal == 4 + 1)
{
quickSearch = true;
}
else
{
incrementAngle /= 10;
angle = anglePrevious + incrementAngle;

decimal++;
error = 0;
}

if (angle > 90.0001)
{
angle = 89.9999;
quickSearch = true;
}
}

if (TANGENT < 0)
{
angle = -angle;
}
}

if (X < 0)
{
if (Y >= 0)
{
angle += 180;
}
else
{
angle -= 180;
}
}

return angle;
}




Exemples pour se servir des fonctions

Arrondir des nombres décimaux :

Des calculs complexes en virgule flottante (float), comprenez en nombre décimaux, sont parfois nécessaires afin de conserver la précision tout au long des opérations arithmétiques. Mais la finalité de ces opérations se retrouveront souvent dans un but de faire fonctionner des systèmes prenant en entrée uniquement des nombres entiers. Il est donc nécessaire d'arrondir le résultat en toute fin du calcul.

Exemple pour arrondir un nombre décimal à l'entier inférieur :
int main()
{
floor (14.6);
//la fonction retourne la valeur 14

return 0;
}

Dans l'exemple ci-dessus, la fonction statique floor est appelée, elle prend en paramètre 14.6, le nombre décimal à arrondir à l'entier inférieur, et retourne (dans ce cas de figure) le nombre entier 14.

Exemple pour arrondir un nombre décimal à l'entier le plus proche :
int main()
{
round (23.7);
//la fonction retourne la valeur 24

return 0;
}

Dans cet exemple la fonction statique round est appelée, elle prend en paramètre 23.7, le nombre décimal à arrondir à l'entier le plus proche, et retourne le nombre entier 24.

Exemple pour arrondir un nombre décimal à l'entier supérieur :
int main()
{
ceil (5.12);
//la fonction retourne la valeur 6

return 0;
}

Ici la fonction statique ceil est appelée, elle prend en paramètre 5.12, le nombre décimal à arrondir à l'entier supérieur, et retourne le nombre entier 6.



Créer des interpolations de courbes :

Il est très simple avec les fonctions de courbes que je propose de changer des valeurs d'échelle, d'interpoler des courbes vers d'autres valeurs, tout en ajoutant de l'exponentiel afin d'adoucir ou de rendre plus vif l'évolution des courbes ainsi créées. Il est également possible de créer des interpolations dissymétriques autour d'un centre arbitraire, mettant en jeu de part et d'autre deux types d'interpolations différentes (linéaires et exponentielles positives et négatives).

Exemple pour créer une interpolation linéaire d'une courbe :
int main()
{
curve (-25, 50, 100, 1, 10, 0);
//la fonction retourne la valeur 6.4

return 0;
}

Dans l'exemple ci-dessus, la fonction statique curve est appelée prenant plusieurs paramètres :
- Le 1er paramètre -25 est le point initial de la courbe.
- Le 2ème paramètre 50 est le point courant de la courbe.
- Le 3ème paramètre 100 est le point final de la courbe.
- Le 4ème paramètre 1 est le point initial de l'interpolation souhaitée.
- Le 5ème paramètre 10 est le point final de l'interpolation souhaitée.
- Le 6ème paramètre 0 défini le type d'interpolation (0 = linéaire) à partir du point initial vers le point final.

Le résultat du calcul est 6.4.

À noter que les valeurs indiquées en paramètre peuvent indifféremment être positives ou négatives et inférieures ou supérieures les unes par rapport aux autres, la fonction curve est prévue à cet effet.

Exemple pour créer une interpolation exponentielle d'une courbe :
int main()
{
curve (-25, 50, 100, 1, 10, 40);
//la fonction retourne la valeur ≈ 1.317 (valeur arrondie)

return 0;
}

L'exemple est identique au précédent, sauf le 6ème paramètre 40 (différent de 0 donc non-linéaire) qui permet une interpolation exponentielle positive, plus ce nombre est élevé, plus l'exponentielle est prononcée. Un nombre négatif produit une exponentielle négative, c'est-à-dire non pas une interpolation exponentielle qui évolue doucement proche du point initial de la courbe, mais plutôt évoluant rapidement proche du point initial pour terminer plus douce à l'approche du point final.

Le résultat du calcul est maintenant ≈ 1.317 (valeur arrondie).

Exemple pour créer une interpolation linéaire dissymétrique d'une courbe :
int main()
{
wurve (0, 50, 70, 0, 9, 10, 0, 0);
//la fonction retourne la valeur ≈ 9.428 (valeur arrondie)

return 0;
}

Dans l'exemple ci-dessus la fonction statique wurve est appelée, voici le détail des paramètres (similaires à la fonction statique curve) :
- Le 1er paramètre 0 est le point initial de la courbe.
- Le 2ème paramètre 50 est le point courant de la courbe.
- Le 3ème paramètre 70 est le point final de la courbe.
- Le 4ème paramètre 0 est le point initial de l'interpolation souhaitée.
- Le 5ème paramètre 9 est le point central de l'interpolation souhaitée.
- Le 6ème paramètre 10 est le point final de l'interpolation souhaitée.
- Le 7ème paramètre 0 défini le type d'interpolation (0 = linéaire) à partir du point central vers le point initial.
- Le 8ème paramètre 0 défini le type d'interpolation (0 = linéaire) à partir du point central vers le point final.

Le résultat du calcul est ≈ 9.428 (valeur arrondie).

À l'instar de la fonction curve, les valeurs indiquées en paramètre avec la fonction wurve peuvent indifféremment être positives ou négatives et inférieures ou supérieures les unes par rapport aux autres.

Exemple pour créer une interpolation double exponentielle centrée d'une courbe :
int main()
{
wurve (0, 473, 1023, 1000, 1500, 2000, 20, 20);
//la fonction retourne la valeur ≈ 1498.069 (valeur arrondie)

return 0;
}

Ci-dessus la fonction statique wurve prend en entrée la valeur d'un potentiomètre pouvant varier de 0 à 1023, c'est la courbe à interpoler, les valeurs d'interpolation vont de 1000 à 2000 en passant par un centre à 1500, ce centre sert à créer une double exponentielle centrée sur 1500 ayant des valeurs de 20 (non-linéaire) de part et d'autre de ce point central (ou neutre).

Le résultat du calcul est ≈ 1498.069 (valeur arrondie).

Cette simple expression pourrait servir à transposer un ordre analogique via un potentiomètre (0 à 1023) vers un signal PWM (pouvant varier de 1000 à 2000 microsecondes) dans le but d'asservir un servo-moteur.

Beaucoup de mes projets utilisent les fonctions de courbes, car elles permettent de transposer facilement des valeurs d'un domaine vers un autre, sans pour autant compliquer la programmation (une ligne de programme est utilisée soit un seul appel de fonction et quelques paramètres).



Cadrer une valeur dans un intervalle :

Il peut être utile de solliciter une fonction dédiée lorsque nous souhaitons qu'une valeur ne sorte pas d'un certain intervalle, sans être obligé de rajouter les conditions logiques nécessaires à cette fonctionnalité dans notre programme (si inférieur ou égal à, si supérieur ou égal à, etc...), ce qui permet de diminuer le nombre d'instructions visibles et d'augmenter la lisibilité du code source.

Exemple pour cadrer une valeur dans un intervalle :
int main()
{
range (-10, 23, 10);
//la fonction retourne une valeur qui ne sortira jamais de l'intervalle -10 à 10

return 0;
}

Dans l'exemple ci-dessus, la fonction statique range limite l'intervalle que peut prendre le nombre 23, ceci dans un cadre -10 à 10. La fonction retourne alors le nombre 10.

Les valeurs indiquées en paramètre peuvent indifféremment être positives ou négatives et inférieures ou supérieures les unes par rapport aux autres, la fonction range est implémentée à cet effet.



Calculer la moyenne de deux valeurs :

De même que la fonction présentée ci-dessus, une fonction pour trouver la moyenne de deux nombres (ou centre) semble facultative, néanmoins elle reste bien pratique !

Exemple pour calculer la moyenne de deux valeurs :
int main()
{
center (125, 85.3);
//la fonction retourne la valeur 105.15

return 0;
}

Dans l'exemple ci-dessus la fonction statique center prend en paramètre les nombres 125 et 85.3, elle retourne donc la moyenne de ces deux nombre, soit 105.15.

L'ordre des nombres indiqués en paramètre à la fonction statique center peuvent être permutés indifféremment.



Calculer la puissance d'un nombre :

La puissance d'un nombre est le résultat de la multiplication répétée (via un exposant) de ce nombre avec lui même.

Exemple pour calculer la puissance d'un nombre :
int main()
{
pow (5, 4);
//la fonction retourne la valeur 625

return 0;
}

Ici le nombre 5 représente le nombre à multiplier avec lui-même, 4 est l'exposant, soit le calcul 5 x 5 x 5 x 5 = 625.

Le nombre à multiplier avec lui même ainsi que l'exposant peuvent être positifs, nuls, ou négatifs.



Calculer la racine carrée d'un nombre :

Le résultat de la racine carrée d'un nombre, est un nombre qui, multiplié par lui-même, donne le radicande (soit le nombre entré en paramètre à la fonction).

Exemple pour calculer la racine carrée d'un nombre :
int main()
{
sqrt (25);
//la fonction retourne la valeur 5

return 0;
}

Ci-dessus le nombre 25 est le résultat du nombre 5 multiplié par lui-même (5 x 5 = 25), la fonction retourne donc le nombre 5.



Calculer la factorielle d'un nombre :

La factorielle d'un nombre est le produit des nombres entiers strictement positifs inférieurs ou égaux à ce nombre. Cet algèbre permet d'effectuer des approches du nombre Pi, de faire converger des équations pour obtenir le sinus, le cosinus, etc...

Exemple pour calculer la factorielle d'un nombre :
int main()
{
fact (7);
//la fonction retourne la valeur 5040

return 0;
}

Dans l'exemple ci-dessus la factorielle du nombre 7 entré en paramètre donne le résultat 5040, car 1 x 2 x 3 x 4 x 5 x 6 x 7 = 5040.

Par convention la factorielle du nombre 0 est 1.



Les fonctions sinus, cosinus, et tangente :

Les fonctions trigonométriques élémentaires comme sinus, cosinus, ou encore tangente s'avèrent utiles dans le domaine des automates programmables afin d'obtenir la position angulaire d'un robot, calculer la trajectoire d'un objet à l'aide d'un gyroscope, etc...

Attention, mes fonctions trigonométriques sin (sinus), cos (cosinus), et tan (tangente) prennent en entrée un angle en degrés (et non pas en radians).

Exemple pour calculer le sinus d'un angle :
int main()
{
sin (30);
//la fonction retourne la valeur 0.5

return 0;
}

Dans cet exemple le sinus de l'angle 30 degrés est 0.5.

Exemple pour calculer le cosinus d'un angle :
int main()
{
cos (45);
//la fonction retourne la valeur ≈ 0.707 (valeur arrondie)

return 0;
}

Dans l'exemple ci-dessus, le cosinus de l'angle 45 degrés est ≈ 0.707 (valeur arrondie).

Exemple pour calculer la tangente d'un angle :
int main()
{
tan (45);
//la fonction retourne la valeur 1

return 0;
}

Ci-dessus la tangente de l'angle 45 degrés est 1.



Les fonctions réciproques arc sinus, arc cosinus, arc tangente, et arc tangente 2 :

En complément des fonctions sinus, cosinus, et tangente, il est fort utile de disposer des fonctions réciproques comme arc sinus, arc cosinus, arc tangente, et arc tangente 2.

Attention, mes fonctions trigonométriques arcsin (arc sinus), arccos (arc cosinus), arctan (arc tangente), et arctan2 (arc tangente à partir de deux arguments x et y) retournent en sortie un angle en degrés (et non pas en radians).

Exemple pour trouver l'angle à partir d'un sinus :
int main()
{
arcsin (0.5);
//la fonction retourne la valeur 30

return 0;
}

Dans l'exemple ci-dessus, un sinus de 0.5 correspond à un angle de 30 degrés.

Exemple pour trouver l'angle à partir d'un cosinus :
int main()
{
arccos (0.707);
//la fonction retourne la valeur ≈ 45.008 (valeur arrondie)

return 0;
}

Avec la fonction statique arccos, on trouve qu'un cosinus de 0.707 correspond à un angle de ≈ 45.008 degrés (valeur arrondie).

Exemple pour trouver l'angle à partir d'une tangente :
int main()
{
arctan (1);
//la fonction retourne la valeur 45

return 0;
}

Une tangente de 1 correspond à un angle de 45 degrés.

Exemple pour trouver l'angle à partir de la tangente de deux arguments x et y (ou fonction circulaire réciproque à quatre cadrans) :
int main()
{
arctan2 (0.5, 0.5);
//la fonction retourne la valeur 45

return 0;
}

Dans l'exemple ci-dessus, une tangente à partir de deux arguments x égal à 0.5 et y égal à 0.5 correspond à un angle de 45 degrés.



Récapitulatif des paramètres des fonctions :

static signed long floor (const float VALUE);
static signed long round (const float VALUE);
static signed long ceil (const float VALUE);
static float curve (const float POSITION_START, const float POSITION_CURRENT, const float POSITION_END, const float INTERPOLATION_START, const float INTERPOLATION_END, const float CURVE);
static float wurve (const float POSITION_START, const float POSITION_CURRENT, const float POSITION_END, const float INTERPOLATION_START, const float INTERPOLATION_CENTER, const float INTERPOLATION_END, const float CURVE_START, const float CURVE_END);
static float range (const float RANGE_START, const float VALUE_CURRENT, const float RANGE_END);
static float center (const float VALUE_START, const float VALUE_END);
static float pow (const float NUMBER, const unsigned long EXPONENT);
static float sqrt (const float RADICAND);
static unsigned long long fact (const unsigned long INTEGER);
static float sin (const float ANGLE);
static float cos (const float ANGLE);
static float tan (const float ANGLE);
static float arcsin (const float SINUS);
static float arccos (const float COSINUS);
static float arctan (const float TANGENT);
static float arctan2 (const float X, const float Y);



Les fonctions ci-dessus simplifient grandement la mise en œuvre de nombreuses applications, vous pouvez combiner les fonctions entres-elles et obtenir des résultats étonnants de complexité !

Si vos applications requièrent des fonctions mathématiques qui ne sont pas présentes ici, libre à vous d'en ajouter si besoin ;)

2
Bonjour à vous :)

Citer
Ce que je souhaite partager avec vous : Conversion d'images bitmap vers une sérigraphie KiCad via une routine bash (.sh)

Avec cette routine de conversion d'images (pour le système d'exploitation Linux), vous allez pouvoir importer des logos (et autres graphismes) dans vos dessins KiCad en vue PCB sur le calque de sérigraphie de la face avant (F.SilkS).

Il existe un outil par défaut dans cette suite KiCad nommé Bitmap2Component qui est censé remplir cette fonction, mais notamment dépendant de la résolution (hauteur/largeur) de l'image d'origine, il ne permet pas le redimensionnement, est basé sur un principe de conversion vectorielle qui détruit l'information, et au final fait apparaître pour certains dessins de petites tailles des lignes courbes là où il y a des droites parfaites, et plus une bouillie de pixels qu'une sérigraphie de qualité...

Ainsi cet outil proposé par défaut est pour moi inutilisable lorsque l'on souhaite un rendu exact par rapport à notre image noir et blanc initiale.

À l'instar de Bitmap2Component, ma routine d'automatisation prend en entrée une image qui doit être initialement en noir et blanc (monochrome) afin que les pixels blancs soient utilisés dans le traitement :

Citer
Ce que je vous propose ici permettra qu'un pixel blanc dans votre image d'origine sera très exactement un carré de sérigraphie (de la taille souhaitée) dans votre dessin en vue PCB.

Ci-dessous, la routine (.sh) permettant la conversion d'images bitmap (.bmp) vers KiCad (.mod) :

#!/bin/bash

echo "Pixel size in mil ?"
read sizeRaw

clear

rm tmp -r -f
mkdir tmp

size=$(echo $sizeRaw"*10" | bc -l)

for file in *.bmp
do
if [ -f "$file" ]
then
echo $file" to "${file%%.*}.mod

convert "$file" tmp/tmp.txt

grep "white" tmp/tmp.txt > tmp/tmp
grep -o ".*:" tmp/tmp > tmp/tmpxy
grep -o ".*," tmp/tmpxy > tmp/x
sed -i tmp/x -e "s/,//"
grep -o ",.*:" tmp/tmpxy > tmp/y
sed -i tmp/y -e "s/,//" -e "s/://"

countList=0

while read x
do
xList[countList]=$(echo $x"*"$size | bc -l)
((countList++))
done < tmp/x

countList=0

while read y
do
yList[countList]=$(echo $y"*"$size | bc -l)
((countList++))
done < tmp/y

echo "PCBNEW-LibModule-V1" > tmp/tmp.mod
echo "\$INDEX" >> tmp/tmp.mod
echo ${file%%.*} >> tmp/tmp.mod
echo "\$EndINDEX" >> tmp/tmp.mod
echo "\$MODULE "${file%%.*} >> tmp/tmp.mod
echo "Li "${file%%.*} >> tmp/tmp.mod

for ((iterationList=0; iterationList < countList; iterationList++))
do
echo "DP 0 0 0 0 4 0 200" >> tmp/tmp.mod
echo "Dl "${xList[iterationList]}" "${yList[iterationList]} >> tmp/tmp.mod
echo "Dl "$(echo ${xList[iterationList]}"+"$size | bc -l)" "${yList[iterationList]} >> tmp/tmp.mod
echo "Dl "$(echo ${xList[iterationList]}"+"$size | bc -l)" "$(echo ${yList[iterationList]}"+"$size | bc -l) >> tmp/tmp.mod
echo "Dl "${xList[iterationList]}" "$(echo ${yList[iterationList]}"+"$size | bc -l) >> tmp/tmp.mod
done

echo "\$EndMODULE "${file%%.*} >> tmp/tmp.mod
echo "\$EndLIBRARY" >> tmp/tmp.mod

mv tmp/tmp.mod "${file%%.*}".mod
fi
done

rm tmp -r -f

exit 0

La routine présentée ci-dessus utilise le programme (ou paquet) imagemagick pour extraire les valeurs matricielles des images traitées.

Il vous faut donc l'installer à l'aide de la ligne de commande suivante (à écrire dans le terminal de Linux) :

sudo apt-get install imagemagick -y
Une fois cette installation effectuée, lorsque vous exécutez la routine (.sh) se propose à vous une question "Pixel size in mil ?", il vous faut alors renseigner la taille souhaitée en millième de pouce que fera un pixel dans votre dessin KiCad (cohérent lorsque les unités de mesures sont indiquées en pouces et non pas en millimètres) :

Par exemple si vous indiquez en taille de pixel le nombre 25, avec une grille dans votre dessin KiCad réglée sur 25 (et des unités de mesures en pouces) vous verrez que le pas des pixels de votre graphisme en vue PCB fera très exactement le même pas que la grille.

Après avoir validé le choix de la taille des pixels, toutes les images ayant l'extension .bmp (bitmap) dans le répertoire courant sont converties successivement en fichiers .mod (footprint) que vous pouvez ajouter à vos projets KiCad par l'intermédiaire du menu d'ajout de bibliothèques (Preferences > Library) en vue PCB.

Si vous apercevez le crénelage des pixels de votre graphisme sérigraphié une fois votre circuit réalisé, je vous conseille d'utiliser une image bitmap (.bmp) initiale de plus grande résolution, et d'indiquer une taille de pixel (lors de la conversion avec la routine) d'autant plus faible, par exemple 2.5 (attention cependant, les temps de traitement peuvent s'avérer assez longs avec des résolutions d'images trop grandes).

Libre à vous de faire une utilisation de ce petit programme de conversion d'image, qui vous permettra aisément d'apposer vos logos et divers graphismes sur vos circuits imprimés avec une parfaite exactitude de conversion (1 pixel bitmap vers 1 pixel sérigraphié).

Ci-dessous un exemple de code matriciel (QR Code) sérigraphié avec mon outil :


3
Vos projets et Montages d'Électronique / Les livres de sauvegarde
« le: novembre 21, 2020, 06:14:36 pm »
Bonjour ;)

Je me permets de partager avec vous une idée que j'ai titré "Les livres de sauvegarde" :

Citer
Face à l'obsolescence progressive, mais bien réelle, des systèmes analogiques et numériques dans le paysage informatique complexe actuel (multitude de technologies matérielles et logicielles plus ou moins hétérogènes), nul ne sait si un matériel de sauvegarde standard considéré de nos jours pourra encore être lu (ouvert en lecture) dans les décennies à venir, et ce malgré le soin apporté à la propagation dans le temps des normes de standardisation de l'analogique et du numérique dans leur ensemble.

Le support imprimé comme le papier relié sous la forme d'un livre peut, dans de bonnes conditions de conservation, rester lisible après plusieurs siècles, voir millénaires. Contrairement aux mémoires non volatiles analogiques ou numériques qui peuvent faire office de supports de sauvegarde (rubans perforés, bandes magnétiques, mémoires à base de semi-conducteurs, etc...), la lecture du papier imprimé s'affranchit d'utiliser un matériel spécifique pour être compréhensible ou décodable, ce qui en fait un candidat relativement pérenne afin d'archiver et de transmettre des informations aux générations futures, en considération de la dépréciation et de l'évolution continue et accélérée respectivement des anciennes et des nouvelles technologies. De surcroît, la grande accessibilité du livre en lecture/écriture est fonction des caractéristiques rudimentaires propres aux techniques de l'imprimerie. La rusticité du support d'impression et de fait, son fonctionnement et sa mise en œuvre minimaliste (sans source d'énergie et dépourvu de composants complexes), participent grandement à sa relative robustesse eu égard les aléas et la sensibilité électromagnétique à l'environnement extérieur des technologies de rétention informatiques analogiques et numériques conventionnelles.

C'est pourquoi je pense être utile l'existence de ce présent dispositif particulièrement dans des contextes critiques, c'est-à-dire en cas d'obsolescence des techniques informatiques dites modernes à l'échéance de périodes dépréciatives et évolutives, de crash d'un ou de plusieurs systèmes redondants à différents lieux géographiques, de suppression intentionnelle de données à l'aide des techniques de piratage sur des machines particulières ou des serveurs connectés à internet, ceci conjointement à des situations éventuelles de crises systémiques, de conflits armés de types multiples, ou plus largement d'effondrement civilisationnel favorisant la dispersion et l'élimination d'informations importantes.




Le contenu de chacun de mes livres a pour origine un seul fichier PDF généré automatiquement via une routine que j'ai développé. En effet cette routine explore l'arborescence de fichiers que vous souhaitez sauvegarder, en extrait le contenu UTF-8, et compile le tout en un fichier PDF que vous pouvez directement envoyer à un imprimeur (voir plus bas).



Cette routine (pour le système d'exploitation Linux que j'utilise) permet de générer automatiquement le contenu d'un livre complet sous la forme d'un seul fichier PDF via une arborescence de fichiers (organisation hiérarchique des fichiers dans des répertoires) dont le contenu UTF-8 est extrait :
Citer
C'est le contenu UTF-8 (pour "Unicode Transformation Format 8" ou format de transformation Unicode sur 8 bits) de vos fichiers à sauvegarder qui est utilisé afin de générer un fichier unique PDF imprimable (book content.pdf), contenu prenant ainsi la forme de caractères normalisés qui peut être issu de divers langages, soit par exemple du binaire, de l'hexadécimal, du C++, ou tout autre caractère UTF-8 imprimable. Attention cependant, si des caractères dans vos fichiers ne font pas partie de la norme UTF-8, il ne pourront pas être traités correctement.

Ainsi ce que je propose ici est un outil qui, bien utilisé, vous permettra de sauvegarder vos fichiers sur des supports imprimés (livres, documents divers), via la simple génération d'un seul fichier .pdf (pour "Portable Document Format" ou format de document portable), un format de fichier pris en charge par tous les imprimeurs.

Ma routine d'automatisation prend donc en entrée une arborescence de fichiers, elle permet de choisir les dimensions des pages du livre, les dimensions des marges générales, la dimension de la marge de reliure qui servira à positionner le texte en retrait par rapport à la reliure du livre (ceci afin de faciliter l'ergonomie de lecture), la taille des caractères du texte qui sera imprimé, la police de caractère utilisée, et les dimensions du fond perdu (soit la marge qui sera rognée par la machine de coupe). Si besoin, il est possible également de trier manuellement l'ordre d'apparition des fichiers dans le livre, ce qui peut être intéressant si le tri alphanumérique par défaut n'est pas souhaité.

Une fois ces choix effectués, un fichier nommé book content.pdf est créé. Ce fichier répertorie tout le contenu des fichiers de l'arborescence, avec la pagination, la numérotation des lignes, la numérotation des pages (paires et impaires), et la table des fichiers en toute fin du livre. Ci dessous, la routine (.sh) permettant la création automatique du contenu d'un livre PDF (.pdf) à partir d'une arborescence de fichiers :

#!/bin/bash

echo "Page width in mm ?"
read widthPage

echo -e "\nPage height in mm ?"
read heightPage

echo -e "\nPage margin in mm ?"
read marginPage

echo -e "\nGutter margin in mm ?"
read marginGutter

echo -e "\nFont size in mm ?"
read fontSize

echo -e "\nFont name ?"
read fontName

echo -e "\nCutter margin in mm ?"
read marginCutter

echo ""

next=false

while [ $next == false ]
do
echo "Sort option ?"
echo "a = Alphanumeric sort"
echo "m = Manual sort"

read menuOptionSort

if [ $menuOptionSort == "a" ] || [ $menuOptionSort == "m" ]
then
next=true
fi

clear
done

rm tmp -r -f
rm "book content.pdf" -f

nameBash=$(basename "$0")
nameLink=$(basename "$1")
countTable=0

shopt -s globstar

for file in **/*
do
if [ -f "$file" ] && [ "$file" != "$nameBash" ] && [ "$file" != "$nameLink" ]
then
fileTable[countTable]=$file
((countTable++))
fi
done

if [ $countTable == 0 ]
then
echo "No file detected"
else
iterationTable=0

if [ $menuOptionSort == "m" ]
then
next=false

while [ $next == false ]
do
echo "Manual sort :"
echo "e = Exit"
echo "s = Save file tree (file.tree)"
echo "l = Load file tree (file.tree)"
echo "d = Delete file tree (file.tree)"
echo ""
echo "File tree :"

for ((iterationTableDisplay=0; iterationTableDisplay < countTable; iterationTableDisplay++))
do
if [ $iterationTableDisplay == $iterationTable ]
then
echo $((iterationTableDisplay+1))" = "${fileTable[iterationTableDisplay]}" <"
else
echo $((iterationTableDisplay+1))" = "${fileTable[iterationTableDisplay]}
fi
done

read menuManualSort

if [ $menuManualSort == "e" ]
then
next=true
elif [ $menuManualSort == "s" ]
then
rm file.tree -f

for ((iterationTableForWriting=0; iterationTableForWriting < countTable; iterationTableForWriting++))
do
echo ${fileTable[iterationTableForWriting]} >> file.tree
done
elif [ $menuManualSort == "l" ]
then
countTable=0

while read line
do
fileTable[$countTable]=$line
((countTable++))
done < file.tree
elif [ $menuManualSort == "d" ]
then
rm file.tree -f
elif (($menuManualSort >= 1 && $menuManualSort <= $countTable))
then
saveTable=${fileTable[$iterationTable]}

fileTable[$iterationTable]=${fileTable[$((menuManualSort-1))]}
fileTable[$((menuManualSort-1))]=$saveTable
fi

clear

if (($iterationTable == $countTable - 1))
then
iterationTable=0
else
((iterationTable++))
fi
done
fi

echo "Progress = 0.0%"

rm tmp -r -f
mkdir tmp

bashString="__BASH_"
countLineTotal=0

characterMeasure=""

for ((iterationCharacter=0; iterationCharacter < 1000; iterationCharacter++))
do
characterMeasure=$characterMeasure"_"
done

ratioFontSize=$(inkscape --without-gui -W <(echo "<svg><text font-family=\""$fontName"\" font-size=\""$fontSize"\">"$characterMeasure"</text></svg>") 2>/dev/null)
countCharacterPerLine=$(echo "("$widthPage"-(("$marginPage"*2)+"$marginGutter"))/("$ratioFontSize"/1000)" | bc -l)
countCharacterPerLineDisplay=$(echo "scale=0;("$countCharacterPerLine"*10)/10" | bc -l)

countLinePerPage=$(echo "("$heightPage"-(("$marginPage"*2)+("$fontSize"/3)))/"$fontSize | bc -l)
countLinePerPageDisplay=$(echo "scale=0;("$countLinePerPage"*10)/10" | bc -l)

for ((iterationTable=0; iterationTable < countTable; iterationTable++))
do
clear

progress=$(echo "25/("$countTable"/("$iterationTable"+1))" | bc -l)
progressDisplay=$(echo "scale=1;(("$progress"+0.05)*10)/10" | bc -l)

if ((${#progressDisplay} == 2))
then
echo "Progress = 0"$progressDisplay"%"
else
echo "Progress = "$progressDisplay"%"
fi

sizeRaw=$(stat -c "%s" "${fileTable[iterationTable]}")

if [ $sizeRaw == 0 ]
then
size="(0 octet)"
elif [ $sizeRaw == 1 ]
then
size="(1 octet)"
elif (($sizeRaw < 1024))
then
size="("$sizeRaw" octets)"
elif (($sizeRaw < 1048576))
then
sizeInteger=$((sizeRaw/1024))
sizeDecimal=$(echo "("$sizeRaw"/1024)-"$((sizeRaw/1024)) | bc -l)
sizeDecimalDisplay=$(echo "scale=1;("$sizeDecimal"*10)/10" | bc -l)
sizeNumber=$(echo $sizeRaw"/1024" | bc -l)
sizeNumberDisplay=$(echo "scale=1;("$sizeNumber"*10)/10" | bc -l)

if [ $sizeDecimalDisplay == 0 ]
then
if (($(echo $sizeNumberDisplay" == "$sizeNumber | bc -l)))
then
size="("$sizeRaw" octets = "$sizeInteger"Kio)"
else
size="("$sizeRaw" octets ≈ "$sizeInteger"Kio)"
fi
else
if (($(echo $sizeNumberDisplay" == "$sizeNumber | bc -l)))
then
size="("$sizeRaw" octets = "$sizeNumberDisplay"Kio)"
else
size="("$sizeRaw" octets ≈ "$sizeNumberDisplay"Kio)"
fi
fi
elif (($sizeRaw < 1073741824))
then
sizeInteger=$((sizeRaw/1048576))
sizeDecimal=$(echo "("$sizeRaw"/1048576)-"$((sizeRaw/1048576)) | bc -l)
sizeDecimalDisplay=$(echo "scale=1;("$sizeDecimal"*10)/10" | bc -l)
sizeNumber=$(echo $sizeRaw"/1048576" | bc -l)
sizeNumberDisplay=$(echo "scale=1;("$sizeNumber"*10)/10" | bc -l)

if [ $sizeDecimalDisplay == 0 ]
then
if (($(echo $sizeNumberDisplay" == "$sizeNumber | bc -l)))
then
size="("$sizeRaw" octets = "$sizeInteger"Mio)"
else
size="("$sizeRaw" octets ≈ "$sizeInteger"Mio)"
fi
else
if (($(echo $sizeNumberDisplay" == "$sizeNumber | bc -l)))
then
size="("$sizeRaw" octets = "$sizeNumberDisplay"Mio)"
else
size="("$sizeRaw" octets ≈ "$sizeNumberDisplay"Mio)"
fi
fi
fi

echo -e "Fichier "${fileTable[iterationTable]}" "$size"\u00a0:\n" | fold -s -w$countCharacterPerLineDisplay >> tmp/tmp3

cp "${fileTable[iterationTable]}" tmp/tmp2
sed -i tmp/tmp2 -e "s/ /"$bashString"SPACE/g" -e "s/\t/"$bashString"TABULATION/g" -e "s/*/"$bashString"STAR/g" -e "s/\\\/"$bashString"BACKSLASH/g"

countTmpLine=$(wc -l < tmp/tmp2)
iterationTmpLine=1

while read line
do
spacePrefixLine=""

for ((iterationSpace=0; iterationSpace < ${#countTmpLine} - ${#iterationTmpLine}; iterationSpace++))
do
spacePrefixLine=$spacePrefixLine$bashString"SPACE"
done

spacePrefixSubline=""

for ((iterationSpace=0; iterationSpace < ${#countTmpLine}; iterationSpace++))
do
spacePrefixSubline=$spacePrefixSubline$bashString"SPACE"
done

echo $line | sed -e "s/"$bashString"SPACE/ /g" -e "s/"$bashString"TABULATION/   /g" -e "s/"$bashString"STAR/*/g" -e "s/"$bashString"BACKSLASH/\\\/g" | fold -s -w$((countCharacterPerLineDisplay-(${#countTmpLine}+3))) | sed -e "s/ /"$bashString"SPACE/g" -e "s/*/"$bashString"STAR/g" -e "s/\\\/"$bashString"BACKSLASH/g" > tmp/tmp1

iterationTmpSubline=1

while read subline
do
if [ $iterationTmpSubline == 1 ]
then
echo $spacePrefixLine$iterationTmpLine" : "$subline | sed -e "s/"$bashString"SPACE/ /g" -e "s/"$bashString"STAR/*/g" -e "s/"$bashString"BACKSLASH/\\\/g" >> tmp/tmp3
else
echo $spacePrefixSubline" : "$subline | sed -e "s/"$bashString"SPACE/ /g" -e "s/"$bashString"STAR/*/g" -e "s/"$bashString"BACKSLASH/\\\/g" >> tmp/tmp3
fi

((iterationTmpSubline++))
done < tmp/tmp1

((iterationTmpLine++))
done < tmp/tmp2

countLine=$(wc -l < tmp/tmp3)
iterationLinePerPage=0
fillPage=$((countLinePerPageDisplay-2))

for ((iterationLine=0; iterationLine < countLine - 1; iterationLine++))
do
if (($iterationLinePerPage == $countLinePerPageDisplay - 3))
then
fillPage=$((fillPage+(countLinePerPageDisplay-2)))
iterationLinePerPage=0
else
((iterationLinePerPage++))
fi
done

for ((iterationLine=0; iterationLine < fillPage - countLine; iterationLine++))
do
echo "" >> tmp/tmp3
done

titlePageTable[iterationTable]=${fileTable[iterationTable]}

if [ $countLineTotal == 0 ]
then
numberPageTable[iterationTable]=1
else
numberPageTable[iterationTable]=$(((countLineTotal/(countLinePerPageDisplay-2))+1))
fi

countLineTotal=$(wc -l < tmp/tmp3)
done

iterationLinePerPage=0
numberPageContent=1
flipFlop=false

for ((iterationLine=0; iterationLine < countLineTotal; iterationLine++))
do
if (($iterationLinePerPage == $countLinePerPageDisplay - 1))
then
sed -i tmp/tmp3 -e $iterationLine"i\\\\"

if [ $flipFlop == false ]
then
lenghtNumberPageContent=${#numberPageContent}
fillSpace=""

for ((iterationCharacter=0; iterationCharacter < countCharacterPerLineDisplay - lenghtNumberPageContent; iterationCharacter++))
do
fillSpace=$fillSpace$bashString"SPACE"
done

sed -i tmp/tmp3 -e $((iterationLine+1))"i"$fillSpace$numberPageContent | sed -e "s/"$bashString"SPACE/ /g"

flipFlop=true
else
sed -i tmp/tmp3 -e $((iterationLine+1))"i"$numberPageContent
flipFlop=false
fi

iterationLinePerPage=0

((numberPageContent++))

clear

progress=$(echo "25+(25/("$countLineTotal"/("$iterationLine"+1)))" | bc -l)
progressDisplay=$(echo "scale=1;(("$progress"+0.05)*10)/10" | bc -l)

echo "Progress = "$progressDisplay"%"
else
((iterationLinePerPage++))
fi

countLineTotal=$(wc -l < tmp/tmp3)
done

if [ $flipFlop == false ]
then
lenghtNumberPageContent=${#numberPageContent}
fillSpace=""

for ((iterationCharacter=0; iterationCharacter < countCharacterPerLineDisplay - lenghtNumberPageContent; iterationCharacter++))
do
fillSpace=$fillSpace$bashString"SPACE"
done

echo -e "\n"$fillSpace$numberPageContent | sed -e "s/"$bashString"SPACE/ /g" >> tmp/tmp3
else
echo -e "\n"$numberPageContent >> tmp/tmp3
fi

echo -e "Table des fichiers\u00a0:\n" | fold -s -w$countCharacterPerLineDisplay >> tmp/tmp3

lenghtMaxPageNumber=${#numberPageTable[$((countTable-1))]}
lenghtMaxPageTitle=$(((countCharacterPerLineDisplay-3)-${#numberPageTable[$((countTable-1))]}))

for ((iterationTable=0; iterationTable < countTable; iterationTable++))
do
echo ${titlePageTable[$iterationTable]} | fold -s -w$lenghtMaxPageTitle > tmp/tmp4

countTmpLine=$(wc -l < tmp/tmp4)

iterationTmpLine=1
point="."

while read line
do
if [ $iterationTmpLine != $countTmpLine ]
then
echo $line >> tmp/tmp3
else
lenghtLastLine=$((${#line}+${#numberPageTable[iterationTable]}))

for ((iterationCharacter=0; iterationCharacter < countCharacterPerLineDisplay - (lenghtLastLine + 3); iterationCharacter++))
do
point=$point"."
done

echo $line" "$point" "${numberPageTable[iterationTable]} >> tmp/tmp3
fi

((iterationTmpLine++))
done < tmp/tmp4
done

countLineTotal=$(wc -l < tmp/tmp3)
iterationLinePerPage=0
fillPage=$countLinePerPageDisplay

for ((iterationLine=0; iterationLine < countLineTotal - 1; iterationLine++))
do
if (($iterationLinePerPage == $countLinePerPageDisplay - 1))
then
fillPage=$((fillPage+countLinePerPageDisplay))
iterationLinePerPage=0
else
((iterationLinePerPage++))
fi
done

for ((iterationLine=0; iterationLine < fillPage - countLineTotal; iterationLine++))
do
echo "" >> tmp/tmp3
done

cp tmp/tmp3 tmp/tmp5

sed -i tmp/tmp5 -e "s/ /_/g" -e "s/*/_/g" -e "s/\\\/_/g"

iterationLine=0

while read line
do
lenghtLineContent[iterationLine]=${#line}
((iterationLine++))
done < tmp/tmp5

countLineTotal=$(wc -l < tmp/tmp3)
iterationLine=0
iterationLineDisplayed=0

cp tmp/tmp3 tmp/tmp6

sed -i tmp/tmp6 -e "s/ /"$bashString"SPACE/g" -e "s/*/"$bashString"STAR/g" -e "s/\\\/"$bashString"BACKSLASH/g"

clear

echo "Progress = 50.0%"

while read line
do
fillSpace=""

for ((iterationCharacter=0; iterationCharacter < countCharacterPerLineDisplay - lenghtLineContent[iterationLine]; iterationCharacter++))
do
fillSpace=$fillSpace$bashString"SPACE"
done

echo $line$fillSpace | sed -e "s/"$bashString"SPACE/ /g" -e "s/"$bashString"STAR/*/g" -e "s/"$bashString"BACKSLASH/\\\/g" >> tmp/tmp7

((iterationLine++))

if (($iterationLineDisplayed == $countLinePerPageDisplay - 1))
then
clear

progress=$(echo "50+(25/("$countLineTotal"/"$iterationLine"))" | bc -l)
progressDisplay=$(echo "scale=1;(("$progress"+0.05)*10)/10" | bc -l)

echo "Progress = "$progressDisplay"%"

iterationLineDisplayed=0
else
((iterationLineDisplayed++))
fi
done < tmp/tmp6

iterationLine=0
iterationFileDisplayed=0
firstPage=false
flipFlop=false
countPageTotal=$(($(wc -l < tmp/tmp3)/countLinePerPageDisplay))

sed -i tmp/tmp7 -e "s/ /"$bashString"SPACE/g" -e "s/*/"$bashString"STAR/g" -e "s/\\\/"$bashString"BACKSLASH/g"

while read line
do
if [ $iterationLine == 0 ]
then
echo "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" > tmp/tmp.svg
echo "<svg width=\""$(echo "("$marginCutter"*2)+"$widthPage | bc -l)"mm\" height=\""$(echo "("$marginCutter"*2)+"$heightPage | bc -l)"mm\">" >> tmp/tmp.svg
fi

if [ $flipFlop == false ]
then
echo "<text x=\""$(echo $marginCutter"+("$widthPage"-"$marginPage")" | bc -l)"mm\" y=\""$(echo $marginCutter"+("$marginPage"+("$fontSize"*("$iterationLine"+1)))" | bc -l)"mm\" text-anchor=\"end\" font-family=\""$fontName"\" font-size=\""$fontSize"mm\" fill=\"#000000\" xml:space=\"preserve\">"$(echo $line | sed -e "s/&/&amp;/g" -e "s/</\&lt;/g" -e "s/>/\&gt;/g" -e "s/\"/\&quot;/g" -e "s/'/\&apos;/g")"</text>" | sed -e "s/"$bashString"SPACE/ /g" -e "s/"$bashString"STAR/*/g" -e "s/"$bashString"BACKSLASH/\\\/g" >> tmp/tmp.svg
else
echo "<text x=\""$(echo $marginCutter"+"$marginPage | bc -l)"mm\" y=\""$(echo $marginCutter"+("$marginPage"+("$fontSize"*("$iterationLine"+1)))" | bc -l)"mm\" text-anchor=\"start\" font-family=\""$fontName"\" font-size=\""$fontSize"mm\" fill=\"#000000\" xml:space=\"preserve\">"$(echo $line | sed -e "s/&/&amp;/g" -e "s/</\&lt;/g" -e "s/>/\&gt;/g" -e "s/\"/\&quot;/g" -e "s/'/\&apos;/g")"</text>" | sed -e "s/"$bashString"SPACE/ /g" -e "s/"$bashString"STAR/*/g" -e "s/"$bashString"BACKSLASH/\\\/g" >> tmp/tmp.svg
fi

if (($iterationLine == $countLinePerPageDisplay - 1))
then
echo "<rect x=\"0mm\" y=\"0mm\" width=\""$(echo "("$marginCutter"*2)+"$widthPage | bc -l)"mm\" height=\""$(echo $marginCutter"+"$marginPage | bc -l)"mm\" fill=\"#ffffff\"/>" >> tmp/tmp.svg
echo "<rect x=\"0mm\" y=\""$(echo $marginCutter"+("$heightPage"-"$marginPage")" | bc -l)"mm\" width=\""$(echo "("$marginCutter"*2)+"$widthPage | bc -l)"mm\" height=\""$(echo $marginCutter"+"$marginPage | bc -l)"mm\" fill=\"#ffffff\"/>" >> tmp/tmp.svg
echo "<rect x=\"0mm\" y=\""$(echo $marginCutter"+"$marginPage | bc -l)"mm\" width=\""$(echo $marginCutter"+"$marginPage | bc -l)"mm\" height=\""$(echo $heightPage"-("$marginPage"*2)" | bc -l)"mm\" fill=\"#ffffff\"/>" >> tmp/tmp.svg
echo "<rect x=\""$(echo $marginCutter"+("$widthPage"-"$marginPage")" | bc -l)"mm\" y=\""$(echo $marginCutter"+"$marginPage | bc -l)"mm\" width=\""$(echo $marginCutter"+"$marginPage | bc -l)"mm\" height=\""$(echo $heightPage"-("$marginPage"*2)" | bc -l)"mm\" fill=\"#ffffff\"/>" >> tmp/tmp.svg

echo "</svg>" >> tmp/tmp.svg

if [ $firstPage == false ]
then
rsvg-convert -f pdf -d 72 -p 72 tmp/tmp.svg -o tmp/tmp1.pdf
firstPage=true
else
rsvg-convert -f pdf -d 72 -p 72 tmp/tmp.svg -o tmp/tmp2.pdf
pdftk tmp/tmp1.pdf tmp/tmp2.pdf cat output tmp/tmp3.pdf

mv tmp/tmp3.pdf tmp/tmp1.pdf
fi

if [ $flipFlop == false ]
then
flipFlop=true
else
flipFlop=false
fi

iterationLine=0

((iterationFileDisplayed++))

clear

progress=$(echo "75+(25/("$countPageTotal"/"$iterationFileDisplayed"))" | bc -l)
progressDisplay=$(echo "scale=1;(("$progress"+0.05)*10)/10" | bc -l)

echo "Progress = "$progressDisplay"%"
else
((iterationLine++))
fi
done < tmp/tmp7

exiftool -q -m -all= tmp/tmp1.pdf

mv tmp/tmp1.pdf "book content.pdf"
rm tmp -r -f

lenghtTotalCover=$(echo "(("$marginCutter"*2)+("$widthPage"*2))+("$countPageTotal"*0.06)" | bc -l)
widthFrontBackCover=$(echo $marginCutter"+"$widthPage | bc -l)
heightFrontBackCover=$(echo "("$marginCutter"*2)+"$heightPage | bc -l)
widthSideCover=$(echo "("$countPageTotal"*0.06)" | bc -l)

lenghtTotalCoverDisplay=$(echo "scale=3;(("$lenghtTotalCover"+0.0005)*10)/10" | bc -l)
widthFrontBackCoverDisplay=$(echo "scale=3;(("$widthFrontBackCover"+0.0005)*10)/10" | bc -l)
heightFrontBackCoverDisplay=$(echo "scale=3;(("$heightFrontBackCover"+0.0005)*10)/10" | bc -l)
widthSideCoverDisplay=$(echo "scale=3;(("$widthSideCover"+0.0005)*10)/10" | bc -l)

sizeRaw=0

for ((iterationTable=0; iterationTable < countTable; iterationTable++))
do
sizeRaw=$((sizeRaw+$(stat -c "%s" "${fileTable[iterationTable]}")))
done

if [ $sizeRaw == 0 ]
then
size="0 byte"
elif [ $sizeRaw == 1 ]
then
size="1 byte"
elif (($sizeRaw < 1024))
then
size=$sizeRaw" bytes"
elif (($sizeRaw < 1048576))
then
sizeInteger=$((sizeRaw/1024))
sizeDecimal=$(echo "("$sizeRaw"/1024)-"$((sizeRaw/1024)) | bc -l)
sizeDecimalDisplay=$(echo "scale=1;("$sizeDecimal"*10)/10" | bc -l)
sizeNumber=$(echo $sizeRaw"/1024" | bc -l)
sizeNumberDisplay=$(echo "scale=1;("$sizeNumber"*10)/10" | bc -l)

if [ $sizeDecimalDisplay == 0 ]
then
if (($(echo $sizeNumberDisplay" == "$sizeNumber | bc -l)))
then
size=$sizeRaw" bytes ("$sizeInteger"KiB)"
else
size=$sizeRaw" bytes (≈ "$sizeInteger"KiB)"
fi
else
if (($(echo $sizeNumberDisplay" == "$sizeNumber | bc -l)))
then
size=$sizeRaw" bytes ("$sizeNumberDisplay"KiB)"
else
size=$sizeRaw" bytes (≈ "$sizeNumberDisplay"KiB)"
fi
fi
elif (($sizeRaw < 1073741824))
then
sizeInteger=$((sizeRaw/1048576))
sizeDecimal=$(echo "("$sizeRaw"/1048576)-"$((sizeRaw/1048576)) | bc -l)
sizeDecimalDisplay=$(echo "scale=1;("$sizeDecimal"*10)/10" | bc -l)
sizeNumber=$(echo $sizeRaw"/1048576" | bc -l)
sizeNumberDisplay=$(echo "scale=1;("$sizeNumber"*10)/10" | bc -l)

if [ $sizeDecimalDisplay == 0 ]
then
if (($(echo $sizeNumberDisplay" == "$sizeNumber | bc -l)))
then
size=$sizeRaw" bytes ("$sizeInteger"MiB)"
else
size=$sizeRaw" bytes (≈ "$sizeInteger"MiB)"
fi
else
if (($(echo $sizeNumberDisplay" == "$sizeNumber | bc -l)))
then
size=$sizeRaw" bytes ("$sizeNumberDisplay"MiB)"
else
size=$sizeRaw" bytes (≈ "$sizeNumberOne"MiB)"
fi
fi
fi

clear

echo "Book cover specifications for printing :"

if ((${#lenghtTotalCoverDisplay} == 4))
then
echo "Total lenght (front + side + back) = 0"$lenghtTotalCoverDisplay"mm"
else
echo "Total lenght (front + side + back) = "$lenghtTotalCoverDisplay"mm"
fi

if ((${#widthFrontBackCoverDisplay} == 4))
then
echo "Front width = 0"$widthFrontBackCoverDisplay"mm"
else
echo "Front width = "$widthFrontBackCoverDisplay"mm"
fi

if ((${#heightFrontBackCoverDisplay} == 4))
then
echo "Front height = 0"$heightFrontBackCoverDisplay"mm"
else
echo "Front height = "$heightFrontBackCoverDisplay"mm"
fi

if ((${#widthSideCoverDisplay} == 4))
then
echo "Side width = 0"$widthSideCoverDisplay"mm"
else
echo "Side width = "$widthSideCoverDisplay"mm"
fi

if ((${#heightFrontBackCoverDisplay} == 4))
then
echo "Side height = 0"$heightFrontBackCoverDisplay"mm"
else
echo "Side height = "$heightFrontBackCoverDisplay"mm"
fi

if ((${#widthFrontBackCoverDisplay} == 4))
then
echo "Back width = 0"$widthFrontBackCoverDisplay"mm"
else
echo "Back width = "$widthFrontBackCoverDisplay"mm"
fi

if ((${#heightFrontBackCoverDisplay} == 4))
then
echo "Back height = 0"$heightFrontBackCoverDisplay"mm"
else
echo "Back height = "$heightFrontBackCoverDisplay"mm"
fi

echo -e "\nBook content specifications for printing :"
echo "Number of pages = "$countPageTotal

if [ $countTable == 1 ]
then
echo "Total file size = "$size
else
echo "Total files size = "$size
fi
fi

read

exit 0

La routine présentée ci-dessus utilise les programmes (ou paquets) inkscape, librsvg2-bin, pdftk, et exiftool afin d'effectuer tous les traitements nécessaires.

Il vous faut donc les installer à l'aide de la ligne de commande suivante (à écrire dans le terminal de Linux) :

sudo apt-get install inkscape librsvg2-bin pdftk exiftool -y
Une fois cette installation effectuée, lorsque vous exécutez la routine (.sh), se propose à vous dans l'ordre d'apparition les questions suivantes :

Citer
- Page width in mm ? : largeur des pages (en millimètres).
- Page height in mm ? : hauteur des pages (en millimètres).
- Page margin in mm ? : dimensions des marges générales (en millimètres).
- Gutter margin in mm ? : dimension de la marge de reliure qui servira à positionner le texte en retrait par rapport à la reliure du livre (en millimètres).
- Font size in mm ? : taille des caractères du texte qui sera imprimé (en millimètres).
- Font name ? : police de caractère utilisée (elle doit être à espacement unique, par exemple "ubuntu mono").
- Cutter margin in mm ? : dimensions du fond perdu (en millimètres).

Lorsque vous validez cette dernière question, se propose à vous le choix "Sort option ?". Choisissez "a" (pour "Alphanumeric sort" ou tri alphanumérique) puis validez pour trier automatiquement les fichiers dans l'ordre alphanumérique, ou choisissez "m" (pour "Manual sort" ou tri manuel) pour effectuer vous-même le tri.

Si vous avez choisi l'option de tri "m", le menu "Manual sort :" (tri manuel) s'affiche à l'écran suivi de quelques commandes (détaillées ci-dessous), ainsi que l'arborescence des fichiers qui vont être inclus dans le livre, avec successivement le numéro d'apparition des fichiers et le chemin d'accès, c'est à ce moment que vous pouvez modifier l'ordre du tri. Voici ci-dessous le détail des commandes possibles :

Détail des commandes du menu "Manual sort :" :

Citer
- e (pour "Exit" ou sortie) permet de sortir du menu et commencer la génération du livre.
- s (pour "Save" ou sauvegarde) permet de sauvegarder l'arborescence de fichiers dans un fichier nommé file.tree.
- l (pour "Load" ou chargement) permet de charger le fichier nommé file.tree contenant l'arborescence de fichiers.
- d (pour "Delete" ou suppression) permet de supprimer le fichier nommé file.tree contenant l'arborescence de fichiers.

Utilisez ces options pour sauvegarder l'arborescence avec la commande "s", la modifier en ouvrant manuellement le fichier file.tree avec un éditeur de texte (comme gedit par exemple). Puis rechargez cette arborescence modifiée avec la commande "l", vous verrez les modifications s'afficher dans le menu "File tree :".

Plus bas le menu "File tree :" est affiché, c'est l'arborescence de fichiers :

La flèche orientée vers la gauche en suffixe des chemins d'accès aux fichiers indique la position de la destination d'un autre fichier source que vous pouvez choisir de déplacer. Validez pour décaler ce curseur vers une autre destination (c'est-à-dire vers le bas), ou écrivez un nombre, puis validez afin de changer la position dans l'arborescence (l'ordre de tri) du fichier indiqué en source (soit le nombre indiqué) vers la destination (soit le curseur en forme de flèche).

Exemple :
- Le curseur est positionné sur le fichier numéro 3, c'est la destination.
- J'indique le numéro 10, c'est la source, puis je valide.
- Le fichier en position 10 (la source) prend la position 3 (la destination), et inversement, le 3 prend la position 10.

Lorsque l'ordre de tri des fichiers correspond à ce que vous souhaitez, indiquez la commande "e" puis validez pour sortir du menu et débuter la génération du livre.

La génération du livre commence alors. Elle prend logiquement un temps proportionnel à la complexité de l'arborescence de fichiers utilisée comme source, ainsi que le nombre de lignes et de caractères dans la programmation et l'encodage de chaque fichiers. Une fois la génération du livre terminée, un fichier PDF nommé book content.pdf est constitué, et les spécifications utiles pour l'impression du livre sont affichées :

Citer
- Book cover specifications for printing : caractéristiques de la couverture du livre pour l'impression.
- Total lenght (front + side + back) : longueur totale de la couverture (soit la somme des largeurs de la face avant, du dos, et de la face arrière).
- Front width : largeur de la face avant.
- Front height : hauteur de la face avant.
- Side width : largeur du dos.
- Side height : hauteur du dos.
- Back width : largeur de la face arrière.
- Back height : hauteur de la face arrière.

- Book content specifications for printing : caractéristiques du contenu du livre pour l'impression.
- Number of pages : nombre total de pages du contenu du livre.
- Total files size : taille totale des fichiers de votre arborescence faisant partie du contenu, ce qui correspond à la taille totale des fichiers inclus dans le livre, autrement dit, le contenu utile (cette taille exclue donc les titres et la numérotation des pages, de même que la table des fichiers à la toute fin du livre qui n'est là que pour faciliter la navigation dans l'ouvrage final imprimé).

Toutes ces caractéristiques vont vous permettre de créer la couverture du livre manuellement. Dans la continuité, la génération de la couverture aurait pu être également automatisée, mais je pense que c'est contre-artistique, ne laissant plus aucun libre cours à la création et l'imagination. C'est donc un choix mûrement réfléchit de ma part que de laisser la création de la couverture à la charge de l'auteur.

Après avoir créé la couverture de votre livre à votre guise, il suffit simplement d'envoyer cette dernière ainsi que le fichier book content.pdf à un imprimeur !

Libre à vous de faire une utilisation de ce petit programme de génération automatique de contenu de livre, lorsque vous aurez besoin de sauvegarder vos fichiers sur un autre support que ceux habituellement utilisés dans le monde du numérique.

4
Bonjour à vous :)

Je reviens vers vous avec ce schéma :


En effet ma question est : quel serait pour vous la meilleure solution afin de multiplier le nombre de transistors de puissance (en parallèles) dans ce montage ? (dans le but de répartir la dissipation thermique)

J'ai quelques idées et schémas posés sur papier, mais j'aimerais l'avis de personnes plus douées que moi à ce sujet. Je souhaite mettre des résitances de 1W 0.05 ohm à l'émetteur des transistors pour équilibrer les courants (si j'ai bien compris). Par contre je ne sais pas trop si il est mieux de placer le pont diviseur avant ou après ces résistances ballasts.

Merci d'avance pour votre aide.

5
Bonjour à toute la communauté des bidouilleurs et bidouilleuses :)


Bidouille partagée : L'élévateur de tension +400V pour tube Geiger

Le tube d'un compteur Geiger a besoin d'être alimenté avec une différence de potentielle entre son anode et sa cathode suffisamment élevée afin de détecter des particules issues de la désintégration d'isotopes radioactifs.



Le plan du PCB est disponible au format de fichiers Gerber ici : http://www.sylvainmahe.site/download/pcb/geiger_boost_converter.zip

Cette élévation de tension est obtenue à l'aide d'un montage qui dans son principe de fonctionnement est assez simple :
- Charge d'une inductance.
- Décharge de l'inductance dans un condensateur en passant par une diode de redressement.
- Charge du condensateur.
- Alimentation du tube Geiger via le condensateur.



C'est au moment ou l'inductance n'est plus alimentée (découpage du courant à l'aide d'un NE555), que celle-ci produit une surtension, cet excédent de tension charge un condensateur "haute tension".

Cette carte électronique permet donc d'élever la tension d'alimentation de +5V (délivrée par mes automates programmables, voir http://www.sylvainmahe.site/projectModulable32.html) à +400V, elle détecte également la fermeture du tube Geiger (ci-dessous le prototype connecté sur le tube Geiger SBM20 de conception Russe) :



Connexions (élévateur de tension +400V sur automate programmable) :
- Broche GND (masse) sur broche GND disponible.
- Broche +5V (pôle positif) sur broche +5V disponible.
- Broche SHOCK (coups) sur port GPIO relié à l'interruption externe (INT0, INT1, etc...) pour détecter la fermeture du tube Geiger.

Connexions (tube Geiger sur élévateur de tension +400V) :
- Anode du tube Geiger sur connecteur à braser GEIGER TUBE +400V (pôle positif).
- Cathode du tube Geiger sur connecteur à braser GEIGER TUBE GND (masse).



Les caractéristiques de la carte :
- 1 broche GND (masse) pour l'alimentation.
- 1 broche +5V (pôle positif) pour l'alimentation.
- 1 broche SHOCK (coups) pour détecter la fermeture du tube Geiger (0V = fermé, +5V = ouvert).
- 1 connecteur à braser GEIGER TUBE GND (masse) pour alimenter un tube Geiger.
- 1 connecteur à braser GEIGER TUBE +400V (pôle positif) pour alimenter un tube Geiger.
- Dimensions : 51.435mm x 25.4mm.
- Entre-axes de fixations : 43.815mm x 17.78mm.
- Fixations par vis M3 (perçages diamètre 3.2mm).

Liste des composants :
1x Oscillateur NE555 (boîtier DIP-8)
2x Résistances 1kΩ ± 5% carbone 0.25W
1x Résistance 1kΩ ± 1% métal 0.25W
1x Résistance 2.2kΩ ± 5% carbone 0.25W
2x Résistances 10kΩ ± 5% carbone 0.25W
1x Résistance 10kΩ ± 1% métal 0.25W
1x Résistance 10MΩ ± 5% carbone 0.25W
1x Condensateur 100pF céramique (pas 5.08mm, tension >=800V)
1x Condensateur 4.7nF céramique (pas 5.08mm, tension >=800V)
1x Condensateur 33nF céramique (pas 5.08mm, tension >=10V)
2x Condensateurs 100nF céramique (pas 5.08mm, tension >=10V)
1x Condensateur 10μF électrolytique aluminium (pas 2mm, tension >=10V)
1x Inductance 15mH self (pas 5.08mm, boîtier radial, intensité >=100mA)
1x Diode 1N4007
1x Transistor bipolaire 2N3904 (boîtier TO-92)
1x Transistor bipolaire MPSA44 (boîtier TO-92)
1x Support DIP-8 300mil
3x Broches mâles (pas 2.54mm)

Photo des composants nécessaires :


Photo du PCB nu :



Exemple d'application de la carte avec un tube Geiger LND712 équipé d'une fenêtre en mica afin de détecter la radioactivité Alpha, soit l'Hélium 4 :



Ci-dessous un échantillon de minerai naturel d'Uranium que j'ai vitrifié (plus exactement coulé dans la résine d'inclusion) pour plus de sécurité :




Cette fiole contient majoritairement de l'Uranium 238, voir plus bas : La chaîne de désintégration de l'Uranium 238 pour avoir une idée de tout ce qu'elle contient en isotopes !



Je vous transmettrai le schéma de la carte bientôt, j'écris avec mon smartphone, dès que j'ai accès à mon ordinateur je pourrais communiquer plus de détails si il faut :)

6
Bonjour à tous 🙂

Je profite de ce forum qui nous est mis à disposition afin de vous présenter plus ou moins succintement un projet de radiocommande, pour les personnes intéressées.

Ce projet de radiocommande est la suite logique de mon intérêt et de mes convictions pour un modélisme aérien tel qu'il devrait être pratiqué, c'est-à-dire la conception et la fabrication de systèmes radio-pilotés en partant réellement de zéro.





À partir des premières ébauches sur papier, études des possibilités et contraintes techniques, schémas des circuits électroniques, conception des programmes dans la radiocommande et les modèles associés, dessins du boîtier et définition des différentes pièces d'usinage (etc...), jusqu'à la finalisation du projet, il aura fallu plus d'une année de conception, fabrication, et tests de validation.

Les caractéristiques de la radiocommande :
- Automate programmable MODULABLE 32 équipé du microcontrôleur ATmega1284P.
- Émetteur/récepteur (transceiver) radio 2.4GHz (composant nRF24L01+).
- Antenne Trèfle omnidirectionnelle 3 branches (7dBm).
- Communication multidirectionnelle vers (sol/air air/sol) et entre (air/air) plusieurs modèles.
- Communication vers périphériques relais pour applications longues distances basse puissance.
- Communication par trames de 32 bits.
- Affichage digital avec mini afficheur à digits.
- Buzzer de signalement.
- Système à tolérance de pannes (fail-safe) sur 5 bits (0 à 31).
- Surveillance de l'activité du modèle (watchdog) sur 5 bits (0 à 31).
- 1 menu principal + 1 menu des paramètres/réglages.
- Aucune mémoire de modèles (la mémoire est située dans les modèles).
- Possibilité de copier les réglages d'un modèle vers un autre très facilement.
- 15 paramètres/réglages par défaut envoyés par le modèle.
- Réglage des trims (autour des neutres) des manches de gaz/roulis et profondeur/lacet.
- Réglage d'une alarme (visuelle et sonore) tension de batterie faible du modèle (de 0V à 100V).
- Réglage d'une alarme (visuelle et sonore) temporisation/chronomètre (de 0s à 3600s).
- Réglage de l'inversion des manches de gaz/roulis et profondeur/lacet.
- Réglage de courbes des manches de gaz/roulis et profondeur/lacet.
- Jusqu'à 16 paramètres/réglages personnalisés supplémentaires envoyés par le modèle.
- 5 paramètres/réglages par défaut propres à la radiocommande.
- Calibration des potentiomètres des manches et auxiliaire (si remplacement/autre).
- Verrouillage des menus (plus de réglages possibles, ni d'extinction de la radiocommande).
- Affichage de la tension de la batterie de la radiocommande.
- Affichage de la tension de la batterie du modèle.
- Affichage d'une temporisation/chronomètre (temps d'utilisation du modèle/autre).
- Affichage des trims (verrouillage et remise à 0 possible par le bouton de sélection).
- Affichage d'une télémétrie personnalisée.
- Affichage des paramètres/réglages par défaut et personnalisés.
- Menu de mise à jour des paramètres/réglages du modèle.
- Menu de sauvegarde des réglages propres à la radiocommande.
- Allumage ou extinction de la radiocommande ou du modèle dans n'importe quel ordre.
- Accumulateur Nickel-hydrure métallique (NiMH) 8S (+9.6V) 600mAh.
- Alarme niveau de batterie faible (en dessous de +6V).
- Prise de charge de la batterie (XT30).
- Prise de programmation du microcontrôleur (mini XLR).
- Boîtier fermé en aluminium, acier inoxydable, bois (contreplaqué 5mm), et ertalon.
- Dimensions : 214mm x 205mm x 118mm.



L'interface électromécanique entre l'homme et la machine :
- 2 manches analogiques (1 gaz/roulis + 1 tangage/lacet) sur 10 bits chacun (0 à 1023).
- 4 interrupteurs de trims (3 positions) sur 2 bits chacun (0 à 2).
- 1 interrupteur de coupure moteur/autre (2 positions) sur 1 bit (0 à 1).
- 3 interrupteurs auxiliaires (3 positions) sur 2 bits chacun (0 à 2).
- 1 bouton rotatif auxiliaire sur 10 bits (0 à 1023).
- 1 bouton rotatif de sélection/menus sur 10 bits (0 à 1023).
- 1 bouton poussoir de sélection/menus (2 positions, dont 1 momentanée) sur 1 bit (0 à 1).
- 1 interrupteur d'alimentation maintenue on/off (2 positions).



Programmation de l'automate programmable MODULABLE 32 avec MODULE :
Le programme en langage C++ fonctionnant avec MODULE est téléchargeable ici : http://www.sylvainmahe.site/download/cpp/radio_control.zip



Connexions (automate programmable MODULABLE 32 sur les différents systèmes embarqués) :
- Port GPIO 1 (PB0) sur broche STATE (état) interrupteur d'alimentation maintenue.
- Port GPIO 2 (PB1) sur broche HOLD (auto-maintien) interrupteur d'alimentation maintenue.
- Port GPIO 3 (PB2) sur broche SS (slave select) mini afficheur à digits.
- Port GPIO 4 (PB3) sur broche WAVE (onde) buzzer de signalement.
- Port GPIO 5 (PB4) sur broche CSN (slave select) composant nRF24L01+.
- Port GPIO 6 (PB5) sur broche MOSI (master output slave input) mini afficheur à digits et composant nRF24L01+.
- Port GPIO 7 (PB6) sur broche MISO (master input slave output) composant nRF24L01+.
- Port GPIO 8 (PB7) sur broche SCK (serial clock) mini afficheur à digits et composant nRF24L01+.
- Port GPIO 9 (PD0) sur bouton poussoir de sélection/menus.
- Port GPIO 10 (PD1) sur interrupteur de coupure moteur/autre.
- Port GPIO 11 (PD2) sur interrupteur auxiliaire A (position 1).
- Port GPIO 12 (PD3) sur interrupteur auxiliaire A (position 3).
- Port GPIO 13 (PD4) sur interrupteur auxiliaire B (position 1).
- Port GPIO 14 (PD5) sur interrupteur auxiliaire B (position 3).
- Port GPIO 15 (PD6) sur interrupteur auxiliaire C (position 1).
- Port GPIO 16 (PD7) sur interrupteur auxiliaire C (position 3).
- Port GPIO 17 (PC0) sur interrupteur trim de gaz (position 1).
- Port GPIO 18 (PC1) sur interrupteur trim de gaz (position 3).
- Port GPIO 19 (PC2) sur interrupteur trim de tangage (position 1).
- Port GPIO 20 (PC3) sur interrupteur trim de tangage (position 3).
- Port GPIO 21 (PC4) sur interrupteur trim de roulis (position 1).
- Port GPIO 22 (PC5) sur interrupteur trim de roulis (position 3).
- Port GPIO 23 (PC6) sur interrupteur trim de lacet (position 1).
- Port GPIO 24 (PC7) sur interrupteur trim de lacet (position 3).
- Port GPIO 25 (PA7) sur curseur bouton rotatif de sélection/menus.
- Port GPIO 26 (PA6) sur curseur manche analogique des gaz.
- Port GPIO 27 (PA5) sur curseur manche analogique de tangage.
- Port GPIO 28 (PA4) sur curseur manche analogique de roulis.
- Port GPIO 29 (PA3) sur curseur manche analogique de lacet.
- Port GPIO 30 (PA2) sur curseur bouton rotatif auxiliaire.
- Port GPIO 31 (PA1) sur broche VOLT (tension) interrupteur d'alimentation maintenue.

Le concept de cette radiocommande :

Depuis les premiers temps ou je pilote des modèles radiocommandés dans des associations d'aéromodélisme jusqu'à aujourd'hui, je me suis souvent demandé et au vu de l'avancée précédente et actuelle en matière d'électronique embarquée :
Mais pourquoi donc les radiocommandes du commerce ont-elles des mémoires de modèles ? Les mémoires de modèles permettent en effet de retenir les réglages relatifs à un modèle, dans la mémoire de la radiocommande.

À ce propos je ne citerais que l'exemple d'un ami au terrain de modélisme sans le nommer, qui par mégarde se trompe assez régulièrement de mémoire de modèles (il vole avec beaucoup de choses). En conséquence cela lui arrive souvent d'écraser ses réglages, ou d'écraser son modèle tout simplement au sens physique du terme !

En fait, cette "lacune" des mémoires de modèles dans les radiocommandes modernes trouve historiquement ses racines dans la conception même des premiers radio-émetteurs/récepteurs de modélisme. En effet, les premières radiocommandes se contentaient uniquement d'être émetteur d'information, et le modèle étant lui simplement un récepteur. Dans cette situation (unidirectionnelle), il est facile de comprendre que le modèle reste toujours muet. Plus tard, les concepteurs et divers industriels se sont décidés (au vue de la demande qui augmentait) à ajouter une communication du modèle vers la radiocommande, sur un circuit électronique bien distinct et avec une antenne radio supplémentaire sur le modèle et la radiocommande. C'est un retour air/sol qui a été appelé télémétrie (en rapport avec la télémétrie à l'époque des débuts de la conquête spatiale jusqu'à nos jours).

L'avancée en matière de miniaturisation des composants permet aujourd'hui d'avoir dans une même puce un émetteur et un récepteur, qui se sert de la même antenne radio pour communiquer, ces systèmes sont appelés transceivers (émetteurs/récepteurs).



Le concept que je développe ici permet de vous expliquer l'idée même de ma radiocommande, celle-ci n'a en effet aucune mémoire de modèles, ayant souhaité aller au bout de ma logique, c'est le modèle qui contient la mémoire .

Cette idée simple permet beaucoup de choses, notamment le fait que c'est le modèle qui envoi des paramètres et réglages personnalisés à la radiocommande, qui à l'origine dispose d'un menu des paramètres et réglages vide. La radiocommande se voit alors garnie de paramètres personnalisés relatifs au modèle qui est actuellement en communication avec elle. Ces paramètres peuvent être de n'importe quel type et agir sur n'importe quelle fonction du modèle. Ce retour radio air/sol permet également à la radiocommande de disposer de l'affichage de la tension de la batterie du modèle en temps réel, et d'une télémétrie personnalisée en fonction du modèle. Une fois cette notion comprise, tout est alors possible. Pour régler votre modèle, ma radiocommande dispose alors de 15 paramètres par défaut qui en général sont assez communs aux modèles réduits (trims, alarmes, inversion des voies, courbes de gaz, etc...), et d'un maximum de 16 paramètres personnalisés par le modèle (dont le type de paramètre n'est pas défini par défaut). Ceci est largement suffisant parce que par définition même, ces paramètres sont spécifiques au modèle considéré. Terminé les réglages et menus à n'en plus finir (souvent la plupart du temps inutilisés) dans les radiocommandes du commerce !



Dans ce que je propose ici, vous disposez des réglages uniquement nécessaires au modèle mis en œuvre, ce qui facilite grandement son utilisation sur le terrain de modélisme.


Liens utiles si vous êtes intéressés par mon projet :

Descriptif complet de la radiocommande : http://www.sylvainmahe.site/projectRadioControl.html
L'automate programmable MODULABLE 32 : http://www.sylvainmahe.site/projectModulable32.html
Un projet associé, le quadri-hélicoptère : http://www.sylvainmahe.site/projectQuadcopter.html
Le programme MODULE avec lequel je programme tous mes projets : http://www.sylvainmahe.site/understandWhatIsModule.html
 

Je vais essayer d'agrémenter ce sujet si besoin et de fournir des explications si des personnes sont intéressés. Je suis ouvert aux suggestions, commentaires, et interrogations :)

Bonne soirée à vous tous.



7
Bonjour à tous :)

Je m'adresse à vous car j'ai une question par rapport à la vidéo EB_#208 (https://youtu.be/sZ3ou1UCcls) en ce qui concerne le dernier schéma présenté sans le LM317, juste le TL431, le delta V ou dropout maximum entre la tension d'entrée et de sortie du schéma correspond t'elle grossièrement au VCE (ou VBE?) ou VGS du transistor bipolaire ou mosfet de puissance ? (+ l'alimentation du TL431)

Datasheet TL431 : https://www.google.com/url?sa=t&source=web&rct=j&url=http://www.ti.com/lit/gpn/tl431&ved=2ahUKEwiRwtSR2bfoAhUKfBoKHWnaC0oQFjAAegQIAxAB&usg=AOvVaw3t7FdGQ0X3a7MBox2uYQ7Q€

Exemple du datasheet avec NPN :


Je me demande si le dropout pourrait être inférieur avec un MOSFET canal p (ou bipolaire PNP) de puissance piloté par un transistor NPN qui tire vers la masse lui même piloté par le TL431 ?
Pour avoir un dropout faible j'ai l'habitude d'utiliser le transistor PNP MJE15033 dans mes montages qui commute à des tensions bien inférieures aux MOSFETs.

Je vous questionne à ce sujet car j'aimerais réaliser un schéma similaire avec une tension régulée de +5V en sortie, et avoir une tension minimum d'entrée de +6V (voir moins avec des courants de sortie inférieurs à 1A) pour éventuellement ~8A en pointe en sortie.

Merci par ailleurs si vous avez des idées à ce sujet ! :)

Pages: [1]