You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

459 lines
12 KiB

  1. /*
  2. * TRANSLINE.cpp - base for a transmission line implementation
  3. *
  4. * Copyright (C) 2005 Stefan Jahn <stefan@lkcc.org>
  5. * Modified for Kicad: 2018 jean-pierre.charras
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this package; see the file COPYING. If not, write to
  19. * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
  20. * Boston, MA 02110-1301, USA.
  21. *
  22. */
  23. #include <cmath>
  24. #include <limits>
  25. #include <wx/colour.h>
  26. #include <wx/settings.h>
  27. #include "transline.h"
  28. #include "units.h"
  29. #ifndef INFINITY
  30. #define INFINITY std::numeric_limits<double>::infinity()
  31. #endif
  32. #ifndef M_PI_2
  33. #define M_PI_2 ( M_PI / 2 )
  34. #endif
  35. // Functions to Read/Write parameters in pcb_calculator main frame:
  36. // They are wrapper to actual functions, so all transline functions do not
  37. // depend on Graphic User Interface
  38. void SetPropertyInDialog( enum PRMS_ID aPrmId, double value );
  39. /* Puts the text into the given result line.
  40. */
  41. void SetResultInDialog( int line, const char* text );
  42. /* print aValue into the given result line.
  43. */
  44. void SetResultInDialog( int aLineNumber, double aValue, const char* aText );
  45. /* Returns a named property value. */
  46. double GetPropertyInDialog( enum PRMS_ID aPrmId );
  47. // Returns true if the param aPrmId is selected
  48. // Has meaning only for params that have a radio button
  49. bool IsSelectedInDialog( enum PRMS_ID aPrmId );
  50. /** Function SetPropertyBgColorInDialog
  51. * Set the background color of a parameter
  52. * @param aPrmId = param id to set
  53. * @param aCol = new color
  54. */
  55. void SetPropertyBgColorInDialog( enum PRMS_ID aPrmId, const KIGFX::COLOR4D* aCol );
  56. /* Constructor creates a transmission line instance. */
  57. TRANSLINE::TRANSLINE()
  58. {
  59. m_parameters[MURC_PRM] = 1.0;
  60. m_Name = nullptr;
  61. Init();
  62. }
  63. /* Destructor destroys a transmission line instance. */
  64. TRANSLINE::~TRANSLINE()
  65. {
  66. }
  67. void TRANSLINE::Init()
  68. {
  69. wxColour wxcol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
  70. okCol = KIGFX::COLOR4D( wxcol );
  71. okCol.r = wxcol.Red() / 255.0;
  72. okCol.g = wxcol.Green() / 255.0;
  73. okCol.b = wxcol.Blue() / 255.0;
  74. int i;
  75. // Initialize these variables mainly to avoid warnings from a static analyzer
  76. for( i = 0; i < EXTRA_PRMS_COUNT; ++i )
  77. {
  78. m_parameters[i] = 0;
  79. }
  80. }
  81. /* Sets a named property to the given value, access through the
  82. * application.
  83. */
  84. void TRANSLINE::setProperty( enum PRMS_ID aPrmId, double value )
  85. {
  86. SetPropertyInDialog( aPrmId, value );
  87. }
  88. /*
  89. *Returns true if the param aPrmId is selected
  90. * Has meaning only for params that have a radio button
  91. */
  92. bool TRANSLINE::isSelected( enum PRMS_ID aPrmId )
  93. {
  94. return IsSelectedInDialog( aPrmId );
  95. }
  96. /* Puts the text into the given result line.
  97. */
  98. void TRANSLINE::setResult( int line, const char* text )
  99. {
  100. SetResultInDialog( line, text );
  101. }
  102. void TRANSLINE::setResult( int line, double value, const char* text )
  103. {
  104. SetResultInDialog( line, value, text );
  105. }
  106. /* Returns a property value. */
  107. double TRANSLINE::getProperty( enum PRMS_ID aPrmId )
  108. {
  109. return GetPropertyInDialog( aPrmId );
  110. }
  111. /** @function getProperties
  112. *
  113. * Get all properties from the UI. Computes some extra ones.
  114. **/
  115. void TRANSLINE::getProperties()
  116. {
  117. for( int i = 0; i < DUMMY_PRM; ++i )
  118. {
  119. m_parameters[i] = getProperty( (PRMS_ID) i );
  120. setErrorLevel( (PRMS_ID) i, TRANSLINE_OK );
  121. }
  122. m_parameters[SIGMA_PRM] = 1.0 / getProperty( RHO_PRM );
  123. m_parameters[EPSILON_EFF_PRM] = 1.0;
  124. m_parameters[SKIN_DEPTH_PRM] = skin_depth();
  125. }
  126. /** @function checkProperties
  127. *
  128. * Checks the input parameters (ie: negative length).
  129. * Does not check for incompatibility between values as this depends on the line shape.
  130. **/
  131. void TRANSLINE::checkProperties()
  132. {
  133. // Do not check for values that are results of analyzing / synthesizing
  134. // Do not check for transline specific incompatibilities ( like " conductor height should be lesser than dielectric height")
  135. if( !std::isfinite( m_parameters[EPSILONR_PRM] ) || m_parameters[EPSILONR_PRM] <= 0 )
  136. setErrorLevel( EPSILONR_PRM, TRANSLINE_WARNING );
  137. if( !std::isfinite( m_parameters[TAND_PRM] ) || m_parameters[TAND_PRM] < 0 )
  138. setErrorLevel( TAND_PRM, TRANSLINE_WARNING );
  139. if( !std::isfinite( m_parameters[RHO_PRM] ) || m_parameters[RHO_PRM] < 0 )
  140. setErrorLevel( RHO_PRM, TRANSLINE_WARNING );
  141. if( !std::isfinite( m_parameters[H_PRM] ) || m_parameters[H_PRM] < 0 )
  142. setErrorLevel( H_PRM, TRANSLINE_WARNING );
  143. if( !std::isfinite( m_parameters[TWISTEDPAIR_TWIST_PRM] )
  144. || m_parameters[TWISTEDPAIR_TWIST_PRM] < 0 )
  145. setErrorLevel( TWISTEDPAIR_TWIST_PRM, TRANSLINE_WARNING );
  146. if( !std::isfinite( m_parameters[STRIPLINE_A_PRM] ) || m_parameters[STRIPLINE_A_PRM] <= 0 )
  147. setErrorLevel( STRIPLINE_A_PRM, TRANSLINE_WARNING );
  148. if( !std::isfinite( m_parameters[H_T_PRM] ) || m_parameters[H_T_PRM] <= 0 )
  149. setErrorLevel( H_T_PRM, TRANSLINE_WARNING );
  150. // How can we check ROUGH_PRM ?
  151. if( !std::isfinite( m_parameters[MUR_PRM] ) || m_parameters[MUR_PRM] < 0 )
  152. setErrorLevel( MUR_PRM, TRANSLINE_WARNING );
  153. if( !std::isfinite( m_parameters[TWISTEDPAIR_EPSILONR_ENV_PRM] )
  154. || m_parameters[TWISTEDPAIR_EPSILONR_ENV_PRM] <= 0 )
  155. setErrorLevel( TWISTEDPAIR_EPSILONR_ENV_PRM, TRANSLINE_WARNING );
  156. if( !std::isfinite( m_parameters[MURC_PRM] ) || m_parameters[MURC_PRM] < 0 )
  157. setErrorLevel( MURC_PRM, TRANSLINE_WARNING );
  158. if( !std::isfinite( m_parameters[FREQUENCY_PRM] ) || m_parameters[FREQUENCY_PRM] <= 0 )
  159. setErrorLevel( FREQUENCY_PRM, TRANSLINE_WARNING );
  160. }
  161. void TRANSLINE::analyze()
  162. {
  163. getProperties();
  164. checkProperties();
  165. calcAnalyze();
  166. showAnalyze();
  167. show_results();
  168. }
  169. void TRANSLINE::synthesize()
  170. {
  171. getProperties();
  172. checkProperties();
  173. calcSynthesize();
  174. showSynthesize();
  175. show_results();
  176. }
  177. /**
  178. * @function skin_depth
  179. * calculate skin depth
  180. *
  181. * \f$ \frac{1}{\sqrt{ \pi \cdot f \cdot \mu \cdot \sigma }} \f$
  182. */
  183. #include <cstdio>
  184. double TRANSLINE::skin_depth()
  185. {
  186. double depth;
  187. depth = 1.0
  188. / sqrt( M_PI * m_parameters[FREQUENCY_PRM] * m_parameters[MURC_PRM] * MU0
  189. * m_parameters[SIGMA_PRM] );
  190. return depth;
  191. }
  192. /* *****************************************************************
  193. ********** **********
  194. ********** mathematical functions **********
  195. ********** **********
  196. ***************************************************************** */
  197. #define NR_EPSI 2.2204460492503131e-16
  198. /* The function computes the complete elliptic integral of first kind
  199. * K() and the second kind E() using the arithmetic-geometric mean
  200. * algorithm (AGM) by Abramowitz and Stegun.
  201. */
  202. void TRANSLINE::ellipke( double arg, double& k, double& e )
  203. {
  204. int iMax = 16;
  205. if( arg == 1.0 )
  206. {
  207. k = INFINITY; // infinite
  208. e = 0;
  209. }
  210. else if( std::isinf( arg ) && arg < 0 )
  211. {
  212. k = 0;
  213. e = INFINITY; // infinite
  214. }
  215. else
  216. {
  217. double a, b, c, fr, s, fk = 1, fe = 1, t, da = arg;
  218. int i;
  219. if( arg < 0 )
  220. {
  221. fk = 1 / sqrt( 1 - arg );
  222. fe = sqrt( 1 - arg );
  223. da = -arg / ( 1 - arg );
  224. }
  225. a = 1;
  226. b = sqrt( 1 - da );
  227. c = sqrt( da );
  228. fr = 0.5;
  229. s = fr * c * c;
  230. for( i = 0; i < iMax; i++ )
  231. {
  232. t = ( a + b ) / 2;
  233. c = ( a - b ) / 2;
  234. b = sqrt( a * b );
  235. a = t;
  236. fr *= 2;
  237. s += fr * c * c;
  238. if( c / a < NR_EPSI )
  239. break;
  240. }
  241. if( i >= iMax )
  242. {
  243. k = 0;
  244. e = 0;
  245. }
  246. else
  247. {
  248. k = M_PI_2 / a;
  249. e = M_PI_2 * ( 1 - s ) / a;
  250. if( arg < 0 )
  251. {
  252. k *= fk;
  253. e *= fe;
  254. }
  255. }
  256. }
  257. }
  258. /* We need to know only K(k), and if possible KISS. */
  259. double TRANSLINE::ellipk( double k )
  260. {
  261. double r, lost;
  262. ellipke( k, r, lost );
  263. return r;
  264. }
  265. #define MAX_ERROR 0.000001
  266. /**
  267. * @function minimizeZ0Error1D
  268. *
  269. * Tries to find a parameter that minimizes the error ( on Z0 ).
  270. * This function only works with a single parameter.
  271. * Calls @ref calcAnalyze several times until the error is acceptable.
  272. * While the error is unnacceptable, changes slightly the parameter.
  273. *
  274. * This function does not change Z0 / Angl_L.
  275. *
  276. * @param avar Parameter to synthesize
  277. * @return 'true' if error < MAX_ERROR, else 'false'
  278. */
  279. bool TRANSLINE::minimizeZ0Error1D( double* aVar )
  280. {
  281. double Z0_dest, Z0_current, Z0_result, angl_l_dest, increment, slope, error;
  282. int iteration;
  283. if( !std::isfinite( m_parameters[Z0_PRM] ) )
  284. {
  285. *aVar = NAN;
  286. return false;
  287. }
  288. if( ( !std::isfinite( *aVar ) ) || ( *aVar == 0 ) )
  289. *aVar = 0.001;
  290. /* required value of Z0 */
  291. Z0_dest = m_parameters[Z0_PRM];
  292. /* required value of angl_l */
  293. angl_l_dest = m_parameters[ANG_L_PRM];
  294. /* Newton's method */
  295. iteration = 0;
  296. /* compute parameters */
  297. calcAnalyze();
  298. Z0_current = m_parameters[Z0_PRM];
  299. error = fabs( Z0_dest - Z0_current );
  300. while( error > MAX_ERROR )
  301. {
  302. iteration++;
  303. increment = *aVar / 100.0;
  304. *aVar += increment;
  305. /* compute parameters */
  306. calcAnalyze();
  307. Z0_result = m_parameters[Z0_PRM];
  308. /* f(w(n)) = Z0 - Z0(w(n)) */
  309. /* f'(w(n)) = -f'(Z0(w(n))) */
  310. /* f'(Z0(w(n))) = (Z0(w(n)) - Z0(w(n+delw))/delw */
  311. /* w(n+1) = w(n) - f(w(n))/f'(w(n)) */
  312. slope = ( Z0_result - Z0_current ) / increment;
  313. slope = ( Z0_dest - Z0_current ) / slope - increment;
  314. *aVar += slope;
  315. if( *aVar <= 0.0 )
  316. *aVar = increment;
  317. /* find new error */
  318. /* compute parameters */
  319. calcAnalyze();
  320. Z0_current = m_parameters[Z0_PRM];
  321. error = fabs( Z0_dest - Z0_current );
  322. if( iteration > 100 )
  323. break;
  324. }
  325. /* Compute one last time, but with correct length */
  326. m_parameters[Z0_PRM] = Z0_dest;
  327. m_parameters[ANG_L_PRM] = angl_l_dest;
  328. m_parameters[PHYS_LEN_PRM] = C0 / m_parameters[FREQUENCY_PRM]
  329. / sqrt( m_parameters[EPSILON_EFF_PRM] ) * m_parameters[ANG_L_PRM]
  330. / 2.0 / M_PI; /* in m */
  331. calcAnalyze();
  332. /* Restore parameters */
  333. m_parameters[Z0_PRM] = Z0_dest;
  334. m_parameters[ANG_L_PRM] = angl_l_dest;
  335. m_parameters[PHYS_LEN_PRM] = C0 / m_parameters[FREQUENCY_PRM]
  336. / sqrt( m_parameters[EPSILON_EFF_PRM] ) * m_parameters[ANG_L_PRM]
  337. / 2.0 / M_PI; /* in m */
  338. return error <= MAX_ERROR;
  339. }
  340. /**
  341. * @function setErrorLevel
  342. *
  343. * set an error / warning level for a given parameter.
  344. *
  345. * @see TRANSLINE_OK
  346. * @see TRANSLINE_WARNING
  347. * @see TRANSLINE_ERROR
  348. *
  349. * @param aP parameter
  350. * @param aErrorLevel Error level
  351. */
  352. void TRANSLINE::setErrorLevel( PRMS_ID aP, char aErrorLevel )
  353. {
  354. switch( aErrorLevel )
  355. {
  356. case TRANSLINE_WARNING: SetPropertyBgColorInDialog( aP, &warnCol ); break;
  357. case TRANSLINE_ERROR: SetPropertyBgColorInDialog( aP, &errCol ); break;
  358. default: SetPropertyBgColorInDialog( aP, &okCol ); break;
  359. }
  360. }
  361. double TRANSLINE::calcUnitPropagationDelay( const double epsilonEff )
  362. {
  363. return std::sqrt( epsilonEff ) * ( 1.0e10 / 2.99e8 );
  364. }
  365. char TRANSLINE::convertParameterStatusCode( TRANSLINE_STATUS aStatus )
  366. {
  367. switch( aStatus )
  368. {
  369. case TRANSLINE_STATUS::OK: return TRANSLINE_OK;
  370. case TRANSLINE_STATUS::WARNING: return TRANSLINE_WARNING;
  371. case TRANSLINE_STATUS::TS_ERROR: return TRANSLINE_ERROR;
  372. }
  373. return TRANSLINE_OK;
  374. }