ADD: layer stack manager: support of multilayer dielectric between 2 copper layers

This feature is useful for advanced microwave applications.
This is the last missing feature in the board stackup management.
This commit is contained in:
jean-pierre charras 2019-11-14 16:26:05 +01:00
parent 39ef514ff9
commit 544867cf2a
8 changed files with 1092 additions and 757 deletions

View File

@ -25,6 +25,7 @@
# These are the keywords for the Pcbnew s-expression file format.
add_net
addsublayer
allowed
anchor
angle

View File

@ -42,58 +42,76 @@ wxString BuildStackupReport( BOARD_STACKUP& aStackup, EDA_UNITS_T aUnits )
wxString report;
wxString txt;
int row = 0;
LOCALE_IO toggle; // toggles on the C locale to write floating values, then off.
for( const auto item : aStackup.GetList() )
for( const BOARD_STACKUP_ITEM* item : aStackup.GetList() )
{
// Skip stackup items useless for the current board
if( !item->IsEnabled() )
{
row++;
continue;
}
txt.Printf( "layer \"%s\" type \"%s\"", item->GetLayerName(), item->GetTypeName() );
if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
{
wxString sublayer_text;
if( item->GetSublayersCount() )
sublayer_text.Printf( "\n sublayer \"1/%d\"", item->GetSublayersCount() );
txt.Printf( "layer \"%s\" type \"%s\"%s",
item->FormatDielectricLayerName(),
item->GetTypeName(), sublayer_text );
}
else
txt.Printf( "layer \"%s\" type \"%s\"", item->GetLayerName(),
item->GetTypeName() );
report << txt;
if( item->IsMaterialEditable() )
{
txt.Printf( " Material \"%s\"", item->GetMaterial() );
report << txt;
}
if( item->HasEpsilonRValue() )
{
txt.Printf( " EpsilonR %s", item->FormatEpsilonR() );
report << txt;
}
if( item->HasLossTangentValue() )
{
txt.Printf( " LossTg %s", item->FormatLossTangent() );
report << txt;
}
if( item->IsThicknessEditable() )
{
txt.Printf( " Thickness %s",
StringFromValue( aUnits, item->GetThickness(), true, true ) );
report << txt;
if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked() )
{
txt.Printf( " Locked" );
report << txt;
}
}
if( item->IsColorEditable() )
{
txt.Printf( " Color \"%s\"", item->GetColor() );
report << txt;
}
row++;
for( int idx = 0; idx < item->GetSublayersCount(); idx++ )
{
if( idx ) // not printed for the main (first) layer.
{
txt.Printf( "\n sublayer \"%d/%d\"", idx+1, item->GetSublayersCount() );
report << txt;
}
if( item->IsThicknessEditable() )
{
txt.Printf( " Thickness %s",
StringFromValue( aUnits, item->GetThickness( idx ), true, true ) );
report << txt;
if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked( idx ) )
{
txt.Printf( " Locked" );
report << txt;
}
}
if( item->IsMaterialEditable() )
{
txt.Printf( " Material \"%s\"", item->GetMaterial( idx ) );
report << txt;
}
if( item->HasEpsilonRValue() )
{
txt.Printf( " EpsilonR %s", item->FormatEpsilonR( idx ) );
report << txt;
}
if( item->HasLossTangentValue() )
{
txt.Printf( " LossTg %s", item->FormatLossTangent( idx ) );
report << txt;
}
}
report << '\n';
}

View File

@ -35,6 +35,7 @@ BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM_TYPE aType )
m_DielectricPrmsList.emplace_back( item_prms );
m_LayerId = UNDEFINED_LAYER;
m_Type = aType;
SetDielectricLayerId( 1 );
SetEnabled( true );
// Initialize parameters to a usual value for allowed types:
@ -48,7 +49,6 @@ BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM_TYPE aType )
case BS_ITEM_TYPE_DIELECTRIC:
m_TypeName = KEY_CORE; // or prepreg
SetMaterial( "FR4" ); // or other dielectric name
SetDielectricLayerId( 1 );
SetLossTangent( 0.02 ); // for FR4
SetEpsilonR( 4.5 ); // for FR4
break;
@ -81,6 +81,7 @@ BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM_TYPE aType )
BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM& aOther )
{
m_LayerId = aOther.m_LayerId;
m_DielectricLayerId = aOther.m_DielectricLayerId;
m_Type = aOther.m_Type;
m_enabled = aOther.m_enabled;
m_DielectricPrmsList = aOther.m_DielectricPrmsList;
@ -90,6 +91,28 @@ BOARD_STACKUP_ITEM::BOARD_STACKUP_ITEM( BOARD_STACKUP_ITEM& aOther )
}
void BOARD_STACKUP_ITEM::AddDielectricPrms( int aDielectricPrmsIdx )
{
// add a DIELECTRIC_PRMS item to m_DielectricPrmsList
DIELECTRIC_PRMS new_prms;
m_DielectricPrmsList.emplace( m_DielectricPrmsList.begin() + aDielectricPrmsIdx,
new_prms );
}
void BOARD_STACKUP_ITEM::RemoveDielectricPrms( int aDielectricPrmsIdx )
{
// Remove a DIELECTRIC_PRMS item from m_DielectricPrmsList if possible
if( GetSublayersCount() < 2 || aDielectricPrmsIdx < 0 || aDielectricPrmsIdx >= GetSublayersCount() )
return;
m_DielectricPrmsList.erase( m_DielectricPrmsList.begin() + aDielectricPrmsIdx );
}
int BOARD_STACKUP_ITEM::GetCopperDefaultThickness()
{
// A reasonable thickness for copper layers:
@ -104,38 +127,42 @@ int BOARD_STACKUP_ITEM::GetMaskDefaultThickness()
}
// Getters:
int BOARD_STACKUP_ITEM::GetThickness( int aDielectricSubLayer )
int BOARD_STACKUP_ITEM::GetThickness( int aDielectricSubLayer ) const
{
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
return m_DielectricPrmsList[aDielectricSubLayer].m_Thickness;
}
double BOARD_STACKUP_ITEM::GetLossTangent( int aDielectricSubLayer )
double BOARD_STACKUP_ITEM::GetLossTangent( int aDielectricSubLayer ) const
{
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
return m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent;
}
double BOARD_STACKUP_ITEM::GetEpsilonR( int aDielectricSubLayer )
double BOARD_STACKUP_ITEM::GetEpsilonR( int aDielectricSubLayer ) const
{
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
return m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR;
}
bool BOARD_STACKUP_ITEM::IsThicknessLocked( int aDielectricSubLayer )
bool BOARD_STACKUP_ITEM::IsThicknessLocked( int aDielectricSubLayer ) const
{
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
return m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked;
}
int BOARD_STACKUP_ITEM::GetDielectricLayerId( int aDielectricSubLayer )
wxString BOARD_STACKUP_ITEM::GetMaterial( int aDielectricSubLayer ) const
{
return m_DielectricPrmsList[aDielectricSubLayer].m_DielectricLayerId;
}
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
wxString BOARD_STACKUP_ITEM::GetMaterial( int aDielectricSubLayer )
{
return m_DielectricPrmsList[aDielectricSubLayer].m_Material;
}
@ -143,41 +170,50 @@ wxString BOARD_STACKUP_ITEM::GetMaterial( int aDielectricSubLayer )
// Setters:
void BOARD_STACKUP_ITEM::SetThickness( int aThickness, int aDielectricSubLayer )
{
m_DielectricPrmsList[aDielectricSubLayer].m_Thickness = aThickness;
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
m_DielectricPrmsList[aDielectricSubLayer].m_Thickness = aThickness;
}
void BOARD_STACKUP_ITEM::SetLossTangent( double aTg, int aDielectricSubLayer )
{
m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent = aTg;
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
m_DielectricPrmsList[aDielectricSubLayer].m_LossTangent = aTg;
}
void BOARD_STACKUP_ITEM::SetEpsilonR( double aEpsilon, int aDielectricSubLayer )
{
m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR = aEpsilon;
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
m_DielectricPrmsList[aDielectricSubLayer].m_EpsilonR = aEpsilon;
}
void BOARD_STACKUP_ITEM::SetThicknessLocked( bool aLocked, int aDielectricSubLayer )
{
m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked = aLocked;
}
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
void BOARD_STACKUP_ITEM::SetDielectricLayerId( int aLayerId, int aDielectricSubLayer )
{
m_DielectricPrmsList[aDielectricSubLayer].m_DielectricLayerId = aLayerId;
if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
m_DielectricPrmsList[aDielectricSubLayer].m_ThicknessLocked = aLocked;
}
void BOARD_STACKUP_ITEM::SetMaterial( const wxString& aName, int aDielectricSubLayer )
{
m_DielectricPrmsList[aDielectricSubLayer].m_Material = aName;
wxASSERT( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() );
if( aDielectricSubLayer >= 0 && aDielectricSubLayer < GetSublayersCount() )
m_DielectricPrmsList[aDielectricSubLayer].m_Material = aName;
}
bool BOARD_STACKUP_ITEM::HasEpsilonRValue()
bool BOARD_STACKUP_ITEM::HasEpsilonRValue() const
{
return m_Type == BS_ITEM_TYPE_DIELECTRIC
|| m_Type == BS_ITEM_TYPE_SOLDERMASK
@ -186,23 +222,21 @@ bool BOARD_STACKUP_ITEM::HasEpsilonRValue()
};
bool BOARD_STACKUP_ITEM::HasLossTangentValue()
bool BOARD_STACKUP_ITEM::HasLossTangentValue() const
{
return m_Type == BS_ITEM_TYPE_DIELECTRIC
|| m_Type == BS_ITEM_TYPE_SOLDERMASK
//|| m_Type == BS_ITEM_TYPE_SILKSCREEN
;
|| m_Type == BS_ITEM_TYPE_SOLDERMASK;
};
bool BOARD_STACKUP_ITEM::HasMaterialValue()
bool BOARD_STACKUP_ITEM::HasMaterialValue( int aDielectricSubLayer ) const
{
// return true if the material is specified
return IsMaterialEditable() && IsPrmSpecified( GetMaterial() );
return IsMaterialEditable() && IsPrmSpecified( GetMaterial( aDielectricSubLayer ) );
}
bool BOARD_STACKUP_ITEM::IsMaterialEditable()
bool BOARD_STACKUP_ITEM::IsMaterialEditable() const
{
// The material is editable only for dielectric
return m_Type == BS_ITEM_TYPE_DIELECTRIC ||
@ -211,13 +245,13 @@ bool BOARD_STACKUP_ITEM::IsMaterialEditable()
}
bool BOARD_STACKUP_ITEM::IsColorEditable()
bool BOARD_STACKUP_ITEM::IsColorEditable() const
{
return m_Type == BS_ITEM_TYPE_SOLDERMASK || m_Type == BS_ITEM_TYPE_SILKSCREEN;
}
bool BOARD_STACKUP_ITEM::IsThicknessEditable()
bool BOARD_STACKUP_ITEM::IsThicknessEditable() const
{
switch( m_Type )
{
@ -244,25 +278,25 @@ bool BOARD_STACKUP_ITEM::IsThicknessEditable()
}
wxString BOARD_STACKUP_ITEM::FormatEpsilonR()
wxString BOARD_STACKUP_ITEM::FormatEpsilonR( int aDielectricSubLayer ) const
{
// return a wxString to print/display Epsilon R
wxString txt;
txt.Printf( "%.1f", GetEpsilonR() );
txt.Printf( "%.1f", GetEpsilonR( aDielectricSubLayer ) );
return txt;
}
wxString BOARD_STACKUP_ITEM::FormatLossTangent()
wxString BOARD_STACKUP_ITEM::FormatLossTangent( int aDielectricSubLayer ) const
{
// return a wxString to print/display Loss Tangent
wxString txt;
txt.Printf( "%g", GetLossTangent() );
txt.Printf( "%g", GetLossTangent( aDielectricSubLayer ) );
return txt;
}
wxString BOARD_STACKUP_ITEM::FormatDielectricLayerName()
wxString BOARD_STACKUP_ITEM::FormatDielectricLayerName() const
{
// return a wxString to print/display a dielectriv name
wxString lname;
@ -364,17 +398,31 @@ bool BOARD_STACKUP::SynchronizeWithBoard( BOARD_DESIGN_SETTINGS* aSettings )
BOARD_STACKUP stackup;
stackup.BuildDefaultStackupList( aSettings );
// First test for removed layers:
for( BOARD_STACKUP_ITEM* old_item: m_list )
// First, find removed layers:
for( BOARD_STACKUP_ITEM* curr_item: m_list )
{
bool found = false;
for( BOARD_STACKUP_ITEM* item: stackup.GetList() )
{
if( item->GetBrdLayerId() == old_item->GetBrdLayerId() )
if( curr_item->GetBrdLayerId() != UNDEFINED_LAYER )
{
found = true;
break;
if( item->GetBrdLayerId() == curr_item->GetBrdLayerId() )
{
found = true;
break;
}
}
else // curr_item = dielectric layer
{
if( item->GetBrdLayerId() != UNDEFINED_LAYER )
continue;
if( item->GetDielectricLayerId() == curr_item->GetDielectricLayerId() )
{
found = true;
break;
}
}
}
@ -403,6 +451,10 @@ bool BOARD_STACKUP::SynchronizeWithBoard( BOARD_DESIGN_SETTINGS* aSettings )
}
else // dielectric layer: see m_DielectricLayerId for identification
{
// Compare dielectric layer with dielectric layer
if( initial_item->GetBrdLayerId() != UNDEFINED_LAYER )
continue;
if( item->GetDielectricLayerId() == initial_item->GetDielectricLayerId() )
{
*item = *initial_item;
@ -413,7 +465,9 @@ bool BOARD_STACKUP::SynchronizeWithBoard( BOARD_DESIGN_SETTINGS* aSettings )
}
if( !found )
{
change = true;
}
}
// Transfer other stackup settings from aSettings
@ -586,31 +640,40 @@ void BOARD_STACKUP::FormatBoardStackup( OUTPUTFORMATTER* aFormatter,
aFormatter->Quotew( layer_name ).c_str(),
aFormatter->Quotew( item->GetTypeName() ).c_str() );
if( item->IsThicknessEditable() )
{
if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked() )
aFormatter->Print( 0, " (thickness %s locked)",
FormatInternalUnits( item->GetThickness() ).c_str() );
else
aFormatter->Print( 0, " (thickness %s)",
FormatInternalUnits( item->GetThickness() ).c_str() );
}
if( item->HasMaterialValue() )
aFormatter->Print( 0, " (material %s)",
aFormatter->Quotew( item->GetMaterial() ).c_str() );
if( item->HasEpsilonRValue() && item->HasMaterialValue() )
aFormatter->Print( 0, " (epsilon_r %g)", item->GetEpsilonR() );
if( item->HasLossTangentValue() && item->HasMaterialValue() )
aFormatter->Print( 0, " (loss_tangent %s)",
Double2Str(item->GetLossTangent() ).c_str() );
if( item->IsColorEditable() && IsPrmSpecified( item->GetColor() ) )
aFormatter->Print( 0, " (color %s)",
aFormatter->Quotew( item->GetColor() ).c_str() );
for( int idx = 0; idx < item->GetSublayersCount(); idx++ )
{
if( idx ) // not for the main (first) layer.
{
aFormatter->Print( 0, "\n" );
aFormatter->Print( nest_level+1, "addsublayer" );
}
if( item->IsThicknessEditable() )
{
if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC && item->IsThicknessLocked( idx ) )
aFormatter->Print( 0, " (thickness %s locked)",
FormatInternalUnits( item->GetThickness( idx ) ).c_str() );
else
aFormatter->Print( 0, " (thickness %s)",
FormatInternalUnits( item->GetThickness( idx ) ).c_str() );
}
if( item->HasMaterialValue( idx ) )
aFormatter->Print( 0, " (material %s)",
aFormatter->Quotew( item->GetMaterial( idx ) ).c_str() );
if( item->HasEpsilonRValue() && item->HasMaterialValue( idx ) )
aFormatter->Print( 0, " (epsilon_r %g)", item->GetEpsilonR( idx ) );
if( item->HasLossTangentValue() && item->HasMaterialValue( idx ) )
aFormatter->Print( 0, " (loss_tangent %s)",
Double2Str(item->GetLossTangent( idx ) ).c_str() );
}
aFormatter->Print( 0, ")\n" );
}

View File

@ -58,15 +58,13 @@ enum BS_EDGE_CONNECTOR_CONSTRAINTS
/**
* A helper class to manage a dielectric parameters
* A helper class to manage a dielectric layer set of parameters
*/
class DIELECTRIC_PRMS
{
friend class BOARD_STACKUP_ITEM;
private:
int m_DielectricLayerId;/// the "layer" id for dielectric layers,
/// from 1 (top) to 31 (bottom)
wxString m_Material; /// type of material (for dielectric and solder mask)
int m_Thickness; /// the physical layer thickness in internal units
bool m_ThicknessLocked; /// true for dielectric layers with a fixed thickness
@ -76,7 +74,6 @@ private:
public:
DIELECTRIC_PRMS() :
m_DielectricLayerId(-1),
m_Thickness(0), m_ThicknessLocked( false ),
m_EpsilonR( 1.0 ), m_LossTangent( 0.0 )
{}
@ -102,6 +99,9 @@ private:
PCB_LAYER_ID m_LayerId; /// the layer id (F.Cu to B.Cu, F.Silk, B.silk, F.Mask, B.Mask)
/// and UNDEFINED_LAYER (-1) for dielectic layers that are not
/// really layers for the board editor
int m_DielectricLayerId;/// the "layer" id for dielectric layers,
/// from 1 (top) to 31 (bottom)
/// (only 31 dielectric layers for 32 copper layers)
/// List of dielectric parameters
/// usually only one item, but in complex (microwave) boards, one can have
/// more than one dielectic layer between 2 copper layers, and therfore
@ -112,25 +112,40 @@ private:
/// false to ignore it. Mainly used in dialog stackup editor.
public:
/**
* add (insert) a DIELECTRIC_PRMS item to m_DielectricPrmsList
* all values are set to default
* @param aDielectricPrmsIdx is a index in m_DielectricPrmsList
* the new item will be inserted at this position
*/
void AddDielectricPrms( int aDielectricPrmsIdx );
/**
* Remove a DIELECTRIC_PRMS item from m_DielectricPrmsList
* @param aDielectricPrmsIdx is the index of the parameters set
* to remove in m_DielectricPrmsList
*/
void RemoveDielectricPrms( int aDielectricPrmsIdx );
/// @return true if the layer has a meaningfull Epsilon R parameter
/// namely dielectric layers: dielectric and solder mask
bool HasEpsilonRValue();
bool HasEpsilonRValue() const;
/// @return true if the layer has a meaningfull Dielectric Loss parameter
/// namely dielectric layers: dielectric and solder mask
bool HasLossTangentValue();
bool HasLossTangentValue() const;
/// @return true if the material is specified
bool HasMaterialValue();
bool HasMaterialValue( int aDielectricSubLayer = 0 ) const;
/// @return true if the material is editable
bool IsMaterialEditable();
bool IsMaterialEditable() const;
/// @return true if the color is editable
bool IsColorEditable();
bool IsColorEditable() const;
/// @return true if Thickness is editable
bool IsThicknessEditable();
bool IsThicknessEditable() const;
/// @return a reasonable default value for a copper layer thickness
static int GetCopperDefaultThickness();
@ -138,30 +153,34 @@ public:
/// @return a reasonable default value for a solder mask layer thickness
static int GetMaskDefaultThickness();
/// @return a the number of sublayers in a dielectric layer.
/// the count is >= 1 (there is at least one layer)
int GetSublayersCount() const { return m_DielectricPrmsList.size(); }
/// @return a wxString to print/display Epsilon R
wxString FormatEpsilonR();
wxString FormatEpsilonR( int aDielectricSubLayer = 0 ) const;
/// @return a wxString to print/display Loss Tangent
wxString FormatLossTangent();
wxString FormatLossTangent( int aDielectricSubLayer = 0 ) const;
/// @return a wxString to print/display a dielectric name
wxString FormatDielectricLayerName();
wxString FormatDielectricLayerName() const;
// Getters:
bool IsEnabled() {return m_enabled; }
bool IsEnabled() const {return m_enabled; }
BOARD_STACKUP_ITEM_TYPE GetType() { return m_Type; }
PCB_LAYER_ID GetBrdLayerId() { return m_LayerId; }
wxString GetColor(){ return m_Color; }
wxString GetLayerName() { return m_LayerName; }
wxString GetTypeName() { return m_TypeName; }
BOARD_STACKUP_ITEM_TYPE GetType() const { return m_Type; }
PCB_LAYER_ID GetBrdLayerId() const { return m_LayerId; }
wxString GetColor() const { return m_Color; }
wxString GetLayerName() const { return m_LayerName; }
wxString GetTypeName() const { return m_TypeName; }
int GetDielectricLayerId() const { return m_DielectricLayerId; }
int GetThickness( int aDielectricSubLayer = 0 );
bool IsThicknessLocked( int aDielectricSubLayer = 0 );
double GetEpsilonR( int aDielectricSubLayer = 0 );
double GetLossTangent( int aDielectricSubLayer = 0 );
int GetDielectricLayerId( int aDielectricSubLayer = 0 );
wxString GetMaterial( int aDielectricSubLayer = 0 );
int GetThickness( int aDielectricSubLayer = 0 ) const;
bool IsThicknessLocked( int aDielectricSubLayer = 0 ) const;
double GetEpsilonR( int aDielectricSubLayer = 0 ) const;
double GetLossTangent( int aDielectricSubLayer = 0 ) const;
wxString GetMaterial( int aDielectricSubLayer = 0 ) const;
// Setters:
void SetEnabled( bool aEnable) { m_enabled = aEnable; }
@ -169,12 +188,12 @@ public:
void SetColor( const wxString& aColorName ){ m_Color = aColorName; }
void SetLayerName( const wxString& aName ) { m_LayerName = aName; }
void SetTypeName( const wxString& aName ) { m_TypeName = aName; }
void SetDielectricLayerId( int aLayerId ) { m_DielectricLayerId = aLayerId; }
void SetThickness( int aThickness, int aDielectricSubLayer = 0 );
void SetThicknessLocked( bool aLocked, int aDielectricSubLayer = 0 );
void SetEpsilonR( double aEpsilon, int aDielectricSubLayer = 0 );
void SetLossTangent( double aTg, int aDielectricSubLayer = 0 );
void SetDielectricLayerId( int aLayerId, int aDielectricSubLayer = 0 );
void SetMaterial( const wxString& aName, int aDielectricSubLayer = 0 );
};

File diff suppressed because it is too large Load Diff

View File

@ -49,6 +49,9 @@ class PANEL_SETUP_LAYERS;
struct BOARD_STACKUP_ROW_UI_ITEM
{
BOARD_STACKUP_ITEM* m_Item; // The BOARD_STACKUP_ITEM managed by this BOARD_STACKUP_ROW_UI_ITEM
int m_SubItem; // For multilayer dielectric, the index in sublayer list.
// Must be >= 0 and < m_Item sublayer count. Used only for dielectic
// 0 is the base list of parameters (always existing)
bool m_isEnabled; // True if the row is in board
// false if not (this row is not shown on the panel)
wxStaticBitmap* m_Icon; // Color icon in first column (column 1)
@ -62,8 +65,8 @@ struct BOARD_STACKUP_ROW_UI_ITEM
wxControl* m_EpsilonCtrl; // control shown in column 8
wxControl* m_LossTgCtrl; // control shown in column 9
BOARD_STACKUP_ROW_UI_ITEM( BOARD_STACKUP_ITEM* aItem ) :
m_Item( aItem ),
BOARD_STACKUP_ROW_UI_ITEM( BOARD_STACKUP_ITEM* aItem, int aSubItem = 1 ) :
m_Item( aItem ), m_SubItem( aSubItem ),
m_isEnabled( true ), m_Icon( nullptr ), m_LayerName( nullptr ),
m_LayerTypeCtrl( nullptr ),
m_MaterialCtrl( nullptr ),m_MaterialButt( nullptr ),
@ -92,13 +95,16 @@ public:
*/
void OnLayersOptionsChanged( LSET aNewLayerSet );
BOARD_STACKUP_ITEM* GetStackupItem( int aIndex );
/// @return the BOARD_STACKUP_ITEM managed by the row aRow
BOARD_STACKUP_ITEM* GetStackupItem( int aRow );
/// @return the BOARD_STACKUP_ITEM sublayermanaged by the row aRow
int GetSublayerId( int aRow );
/// Return the color currently selected for the row aRow
wxColor GetSelectedColor( int aRow ) const;
BOARD_STACKUP& GetStackup() { return m_stackup; }
int GetPcbTickness();
int GetPcbThickness();
// Called by wxWidgets: transfer current settings stored in m_stackup to the board
bool TransferDataFromWindow() override;
@ -106,6 +112,18 @@ public:
std::vector<wxColor> m_UserColors; // the list of user colors for each grid row
// other colors are defined colors, and are not stored
private:
/** Creates a BOARD_STACKUP_ROW_UI_ITEM relative to the aStackupItem.
* @return a BOARD_STACKUP_ROW_UI_ITEM filled with corresponding widgets
* @param aRow is the row index in the row list
* @param aStackupItem is the stackup item controlled by the created
* BOARD_STACKUP_ROW_UI_ITEM.
* @param aSublayerIdx is used only for BS_ITEM_TYPE_DIELECTRIC stackup items.
* this is the index of the sublayer to used inside aStackupItem
* (from 0 to sub layer count - 1)
*/
BOARD_STACKUP_ROW_UI_ITEM createRowData( int aRow, BOARD_STACKUP_ITEM* aStackupItem,
int aSublayerIdx );
/** add a Spacer in m_fgGridSizer when a empty cell is needed
*/
wxControl* addSpacer();
@ -124,20 +142,31 @@ private:
* all copper layers and all tech layers that are supported by the stackup
* items not in the current board stackup will be not shown, but they are
* existing in list
* @param aCreatedInitialStackup = true to create a initial stackup list for the dialog
* false to build the stackup panel from the existing stackup list.
*/
void buildLayerStackPanel();
void buildLayerStackPanel( bool aCreatedInitialStackup );
/** Show or do not show items in m_fgGridSizer according to the stackup of the
/** Synchronize the full stackup shown in m_fgGridSizer according to the stackup of the
* current board and optionally update the stackup params (thickness, color ... )
* @param aFullSync = true to update stackup params, false to only update the list
* of shown items
*/
void synchronizeWithBoard( bool aFullSync );
/** Show or do not show items in m_fgGridSizer according to the stackup of the
* current board.
* The panel stackup stores all posible layers (because the number of layers is set
* from an other panel), but only some of them must be actually shown on screen
*/
void showOnlyActiveLayers();
/** Populate m_fgGridSizer with items to handle stackup parameters
* If previous items are in list, remove old items
* New prms are added
* must be called after adding or deleting a dielectric parameter set
*/
void RebuildLayerStackPanel();
void rebuildLayerStackPanel();
/** Transfer current UI settings to m_stackup but not to the board
*/

View File

@ -614,150 +614,173 @@ void GERBER_JOBFILE_WRITER::addJSONMaterialStackup()
for( int ii = 0; ii < brd_stackup.GetCount(); ++ii )
{
BOARD_STACKUP_ITEM* item = brd_stackup.GetStackupLayer( ii );
double thickness = item->GetThickness()*m_conversionUnits; // layer thickness is always in mm
wxString layer_type;
std::string layer_name; // for comment
switch( item->GetType() )
int sub_layer_count = item->GetType() == BS_ITEM_TYPE_DIELECTRIC
? item->GetSublayersCount() : 1;
for( int sub_idx = 0; sub_idx < sub_layer_count; sub_idx++ )
{
case BS_ITEM_TYPE_COPPER:
layer_type = "Copper";
layer_name = formatStringFromUTF32( m_pcb->GetLayerName( item->GetBrdLayerId() ) );
last_copper_layer = item->GetBrdLayerId();
break;
// layer thickness is always in mm
double thickness = item->GetThickness( sub_idx )*m_conversionUnits;
wxString layer_type;
std::string layer_name; // for comment
case BS_ITEM_TYPE_SILKSCREEN:
layer_type = "Legend";
layer_name = formatStringFromUTF32( item->GetTypeName() );
break;
case BS_ITEM_TYPE_SOLDERMASK:
layer_type = "SolderMask";
layer_name = formatStringFromUTF32( item->GetTypeName() );
break;
case BS_ITEM_TYPE_SOLDERPASTE:
layer_type = "SolderPaste";
layer_name = formatStringFromUTF32( item->GetTypeName() );
break;
case BS_ITEM_TYPE_DIELECTRIC:
layer_type = "Dielectric";
// The option core or prepreg is not added here, as it creates constraints
// in build process, not necessary wanted.
layer_name = formatStringFromUTF32( wxString::Format( "dielectric layer %d",
item->GetDielectricLayerId() ) );
break;
default:
break;
}
openBlock();
addJSONObject( wxString::Format( "\"Type\": \"%s\",\n", layer_type ) );
if( item->IsColorEditable() && uptodate )
{
if( IsPrmSpecified( item->GetColor() ) )
switch( item->GetType() )
{
wxString colorName = item->GetColor();
case BS_ITEM_TYPE_COPPER:
layer_type = "Copper";
layer_name = formatStringFromUTF32( m_pcb->GetLayerName( item->GetBrdLayerId() ) );
last_copper_layer = item->GetBrdLayerId();
break;
if( colorName.StartsWith( "#" ) ) // This is a user defined color.
case BS_ITEM_TYPE_SILKSCREEN:
layer_type = "Legend";
layer_name = formatStringFromUTF32( item->GetTypeName() );
break;
case BS_ITEM_TYPE_SOLDERMASK:
layer_type = "SolderMask";
layer_name = formatStringFromUTF32( item->GetTypeName() );
break;
case BS_ITEM_TYPE_SOLDERPASTE:
layer_type = "SolderPaste";
layer_name = formatStringFromUTF32( item->GetTypeName() );
break;
case BS_ITEM_TYPE_DIELECTRIC:
layer_type = "Dielectric";
// The option core or prepreg is not added here, as it creates constraints
// in build process, not necessary wanted.
if( sub_layer_count > 1 )
{
// In job file a color can be given by its RGB values (0...255)
wxColor color( colorName );
colorName.Printf( "R%dG%dB%d", color.Red(), color.Green(), color.Blue() );
layer_name = formatStringFromUTF32(
wxString::Format( "dielectric layer %d - %d/%d",
item->GetDielectricLayerId(),
sub_idx+1, sub_layer_count ) );
}
else
layer_name = formatStringFromUTF32(
wxString::Format( "dielectric layer %d",
item->GetDielectricLayerId() ) );
break;
addJSONObject( wxString::Format( "\"Color\": \"%s\",\n", colorName ) );
default:
break;
}
}
if( item->IsThicknessEditable() && uptodate )
addJSONObject( wxString::Format( "\"Thickness\": %.3f,\n", thickness ) );
openBlock();
addJSONObject( wxString::Format( "\"Type\": \"%s\",\n", layer_type ) );
if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
{
if( item->HasMaterialValue() )
if( item->IsColorEditable() && uptodate )
{
addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", item->GetMaterial() ) );
// These constrains are only written if the board has impedance controlled tracks.
// If the board is not impedance controlled, they are useless.
// Do not add constrains that create more expensive boards.
if( brd_stackup.m_HasDielectricConstrains )
if( IsPrmSpecified( item->GetColor() ) )
{
// Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not
// a possible value
if( item->GetEpsilonR() > 1.0 )
addJSONObject( wxString::Format( "\"DielectricConstant\": %s,\n",
item->FormatEpsilonR() ) );
wxString colorName = item->GetColor();
// Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not
// a possible value
if( item->GetLossTangent() > 0.0 )
addJSONObject( wxString::Format( "\"LossTangent\": %s,\n",
item->FormatLossTangent() ) );
if( colorName.StartsWith( "#" ) ) // This is a user defined color.
{
// In job file a color can be given by its RGB values (0...255)
wxColor color( colorName );
colorName.Printf( "R%dG%dB%d", color.Red(), color.Green(), color.Blue() );
}
addJSONObject( wxString::Format( "\"Color\": \"%s\",\n", colorName ) );
}
}
PCB_LAYER_ID next_copper_layer = (PCB_LAYER_ID) (last_copper_layer+1);
if( item->IsThicknessEditable() && uptodate )
addJSONObject( wxString::Format( "\"Thickness\": %.3f,\n", thickness ) );
// If the next_copper_layer is the last copper layer, the next layer id is B_Cu
if( next_copper_layer >= m_pcb->GetCopperLayerCount()-1 )
next_copper_layer = B_Cu;
if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
{
if( item->HasMaterialValue() )
{
addJSONObject( wxString::Format( "\"Material\": \"%s\",\n",
item->GetMaterial( sub_idx ) ) );
// These constrains are only written if the board has impedance controlled tracks.
// If the board is not impedance controlled, they are useless.
// Do not add constrains that create more expensive boards.
if( brd_stackup.m_HasDielectricConstrains )
{
// Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not
// a possible value
if( item->GetEpsilonR() > 1.0 )
addJSONObject( wxString::Format( "\"DielectricConstant\": %s,\n",
item->FormatEpsilonR( sub_idx ) ) );
addJSONObject( wxString::Format( "\"Name\": \"%s/%s\",\n",
// Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not
// a possible value
if( item->GetLossTangent() > 0.0 )
addJSONObject( wxString::Format( "\"LossTangent\": %s,\n",
item->FormatLossTangent( sub_idx ) ) );
}
}
PCB_LAYER_ID next_copper_layer = (PCB_LAYER_ID) (last_copper_layer+1);
// If the next_copper_layer is the last copper layer, the next layer id is B_Cu
if( next_copper_layer >= m_pcb->GetCopperLayerCount()-1 )
next_copper_layer = B_Cu;
wxString subLayerName;
if( sub_layer_count > 1 )
subLayerName.Printf( " (%d/%d)",sub_idx+1, sub_layer_count );
addJSONObject( wxString::Format( "\"Name\": \"%s/%s%s\",\n",
formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ),
formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ),
subLayerName )
);
// Add a comment ("Notes"):
wxString note = "\"Notes\": ";
note << wxString::Format( " \"Type: %s", layer_name.c_str() );
note << wxString::Format( " (from %s to %s)\"\n",
formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ),
formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ) )
);
formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ) );
// Add a comment ("Notes"):
wxString note = "\"Notes\": ";
note << wxString::Format( " \"Type: %s", layer_name.c_str() );
note << wxString::Format( " (from %s to %s)\"\n",
formatStringFromUTF32( m_pcb->GetLayerName( last_copper_layer ) ),
formatStringFromUTF32( m_pcb->GetLayerName( next_copper_layer ) ) );
addJSONObject( note );
}
else if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK || item->GetType() == BS_ITEM_TYPE_SILKSCREEN )
{
if( item->HasMaterialValue() )
addJSONObject( note );
}
else if( item->GetType() == BS_ITEM_TYPE_SOLDERMASK || item->GetType() == BS_ITEM_TYPE_SILKSCREEN )
{
addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", item->GetMaterial() ) );
// These constrains are only written if the board has impedance controlled tracks.
// If the board is not impedance controlled, they are useless.
// Do not add constrains that create more expensive boards.
if( brd_stackup.m_HasDielectricConstrains )
if( item->HasMaterialValue() )
{
// Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not
// a possible value
if( item->GetEpsilonR() > 1.0 )
addJSONObject( wxString::Format( "\"DielectricConstant\": %s,\n",
item->FormatEpsilonR() ) );
addJSONObject( wxString::Format( "\"Material\": \"%s\",\n", item->GetMaterial() ) );
// Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not
// a possible value
if( item->GetLossTangent() > 0.0 )
addJSONObject( wxString::Format( "\"LossTangent\": %s,\n",
item->FormatLossTangent() ) );
// These constrains are only written if the board has impedance controlled tracks.
// If the board is not impedance controlled, they are useless.
// Do not add constrains that create more expensive boards.
if( brd_stackup.m_HasDielectricConstrains )
{
// Generate Epsilon R if > 1.0 (value <= 1.0 means not specified: it is not
// a possible value
if( item->GetEpsilonR() > 1.0 )
addJSONObject( wxString::Format( "\"DielectricConstant\": %s,\n",
item->FormatEpsilonR() ) );
// Generate LossTangent > 0.0 (value <= 0.0 means not specified: it is not
// a possible value
if( item->GetLossTangent() > 0.0 )
addJSONObject( wxString::Format( "\"LossTangent\": %s,\n",
item->FormatLossTangent() ) );
}
}
addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", layer_name.c_str() ) );
}
else
{
addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", layer_name.c_str() ) );
}
addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", layer_name.c_str() ) );
removeJSONSepararator();
closeBlockWithSep();
}
else
{
addJSONObject( wxString::Format( "\"Name\": \"%s\",\n", layer_name.c_str() ) );
}
removeJSONSepararator();
closeBlockWithSep();
}
removeJSONSepararator();

View File

@ -1075,72 +1075,90 @@ void PCB_PARSER::parseBoardStackup()
else
Expecting( "layer_name" );
// Dielectric thickness can be locked (for impedance controled layers)
bool thickness_locked = false;
bool has_next_sublayer = true;
int sublayer_idx = 0; // the index of dielectric sub layers
// sublayer 0 is always existing (main sublayer)
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
while( has_next_sublayer )
{
if( token == T_LEFT )
has_next_sublayer = false;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
token = NextTok();
switch( token )
if( token == T_addsublayer )
{
case T_type:
NeedSYMBOL();
item->SetTypeName( FromUTF8() );
NeedRIGHT();
has_next_sublayer = true;
break;
}
case T_thickness:
item->SetThickness( parseBoardUnits( T_thickness ) );
if( token == T_LEFT )
{
token = NextTok();
if( token == T_LEFT )
switch( token )
{
case T_type:
NeedSYMBOL();
item->SetTypeName( FromUTF8() );
NeedRIGHT();
break;
if( token == T_locked )
{
thickness_locked = true;
case T_thickness:
item->SetThickness( parseBoardUnits( T_thickness ), sublayer_idx );
token = NextTok();
if( token == T_LEFT )
break;
if( token == T_locked )
{
// Dielectric thickness can be locked (for impedance controled layers)
if( type == BS_ITEM_TYPE_DIELECTRIC )
item->SetThicknessLocked( true, sublayer_idx );
NeedRIGHT();
}
break;
case T_material:
NeedSYMBOL();
item->SetMaterial( FromUTF8(), sublayer_idx );
NeedRIGHT();
break;
case T_epsilon_r:
NextTok();
item->SetEpsilonR( parseDouble(), sublayer_idx );
NeedRIGHT();
break;
case T_loss_tangent:
NextTok();
item->SetLossTangent( parseDouble(), sublayer_idx );
NeedRIGHT();
break;
case T_color:
NeedSYMBOL();
item->SetColor( FromUTF8() );
NeedRIGHT();
break;
default:
// Currently, skip this item if not defined, because the stackup def
// is a moving target
//Expecting( "type, thickness, material, epsilon_r, loss_tangent, color" );
skipCurrent();
}
break;
case T_material:
NeedSYMBOL();
item->SetMaterial( FromUTF8() );
NeedRIGHT();
break;
case T_epsilon_r:
NextTok();
item->SetEpsilonR( parseDouble() );
NeedRIGHT();
break;
case T_loss_tangent:
NextTok();
item->SetLossTangent( parseDouble() );
NeedRIGHT();
break;
case T_color:
NeedSYMBOL();
item->SetColor( FromUTF8() );
NeedRIGHT();
break;
default:
// Currently, skip this item if not defined, because the stackup def
// is a moving target
//Expecting( "type, thickness, material, epsilon_r, loss_tangent, color" );
skipCurrent();
}
}
}
if( type == BS_ITEM_TYPE_DIELECTRIC && thickness_locked )
item->SetThicknessLocked( true );
if( has_next_sublayer ) // Prepare reading the next sublayer description
{
sublayer_idx++;
item->AddDielectricPrms( sublayer_idx );
}
}
}
if( token != T_RIGHT )