Hi, We needed to do some signal/power integrity simulations on one of our Kicad designs and in order to do that, we needed to convert a Kicad PCB to Hyperlynx format. Luckily, the format is simple, all text and well documented in [1], so here comes a patch that adds a Hyperlynx exporter.
Notes: - since Kicad doesn't have a concept of board stackup (permittivities, loss tangent, dielectric types, etc.), the exporter writes a dummy stackup. Edit it to match the PCB spec in Hyperlynx. - no support for offset pad holes, slotted pad holes, trapezoid/polygonal pads (it seems HL format doesn't support such features or I need to figure out how to emulate them). - no support for thermal pads (yet) - no error reporting. Looking forward to your feedback & wish you happy testing, Tom [1] http://www.ibis.org/birds/bird33.txt
>From e966c63a6f00959e359be36bfc0b8206e01ed4bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20W=C5=82ostowski?= <tomasz.wlostow...@cern.ch> Date: Thu, 4 Apr 2019 18:07:12 +0200 Subject: [PATCH] pcbnew: Initial support for Mentor Hyperlynx export --- pcbnew/CMakeLists.txt | 1 + pcbnew/dialogs/dialog_export_idf.cpp | 1 + pcbnew/exporters/export_hyperlynx.cpp | 547 ++++++++++++++++++ pcbnew/menubar_pcb_editor.cpp | 5 + pcbnew/pcb_edit_frame.cpp | 29 + pcbnew/pcb_edit_frame.h | 7 + pcbnew/pcbnew_id.h | 1 + qa/CMakeLists.txt | 1 + qa/hyperlynx_export/CMakeLists.txt | 64 ++ qa/hyperlynx_export/test_hyperlynx_export.cpp | 61 ++ 10 files changed, 717 insertions(+) create mode 100644 pcbnew/exporters/export_hyperlynx.cpp create mode 100644 qa/hyperlynx_export/CMakeLists.txt create mode 100644 qa/hyperlynx_export/test_hyperlynx_export.cpp diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 3bbdd8723..9f0f5eac1 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -193,6 +193,7 @@ set( PCBNEW_IMPORT_GFX ) set( PCBNEW_EXPORTERS + exporters/export_hyperlynx.cpp exporters/export_d356.cpp exporters/export_footprint_associations.cpp exporters/export_gencad.cpp diff --git a/pcbnew/dialogs/dialog_export_idf.cpp b/pcbnew/dialogs/dialog_export_idf.cpp index e9e16bfbc..882224b7c 100644 --- a/pcbnew/dialogs/dialog_export_idf.cpp +++ b/pcbnew/dialogs/dialog_export_idf.cpp @@ -229,3 +229,4 @@ void PCB_EDIT_FRAME::OnExportIDF3( wxCommandEvent& event ) return; } } + diff --git a/pcbnew/exporters/export_hyperlynx.cpp b/pcbnew/exporters/export_hyperlynx.cpp new file mode 100644 index 000000000..5f5c678dd --- /dev/null +++ b/pcbnew/exporters/export_hyperlynx.cpp @@ -0,0 +1,547 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 CERN + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <kiface_i.h> +#include <pcb_edit_frame.h> +#include <pcbnew.h> + +#include <class_board.h> +#include <class_board_item.h> +#include <class_drawsegment.h> +#include <class_edge_mod.h> +#include <class_module.h> +#include <class_track.h> +#include <class_zone.h> +#include <cstdio> +#include <vector> + +static double iu2hyp( double iu ) +{ + return iu / 1e9 / 0.0254; +} + +class PAD_STACK +{ +public: + PAD_STACK( BOARD* aBoard, const D_PAD* aPad ); + PAD_STACK( BOARD* aBoard, const VIA* aVia ); + ~PAD_STACK(){}; + + + bool isThrough() const + { + return m_type == PAD_ATTRIB_HOLE_NOT_PLATED || m_type == PAD_ATTRIB_STANDARD; + } + + bool operator==( const PAD_STACK& other ) const + { + if( m_shape != other.m_shape ) + return false; + + if( m_type != other.m_type ) + return false; + + if( isThrough() && other.isThrough() && m_drill != other.m_drill ) + return false; + + if( m_sx != other.m_sx ) + return false; + + if( m_sy != other.m_sy ) + return false; + + if( m_layers != other.m_layers ) + return false; + + if( m_angle != other.m_angle ) + return false; + + return true; + } + + bool isSMD() const + { + return m_type == PAD_ATTRIB_SMD; + } + + PCB_LAYER_ID getSMDLayer() const + { + for( auto l : LSET::AllCuMask().Seq() ) + if( m_layers[l] ) + return l; + return F_Cu; + } + + void SetId( int id ) + { + m_id = id; + } + + int GetId() const + { + return m_id; + } + + void FormatPadShape( + int indentLevel, std::shared_ptr<FILE_OUTPUTFORMATTER> fmt, const wxString& layerName ) + { + int shapeId; + + switch( m_shape ) + { + case PAD_SHAPE_CIRCLE: + case PAD_SHAPE_OVAL: shapeId = 0; break; + case PAD_SHAPE_ROUNDRECT: shapeId = 2; break; + case PAD_SHAPE_RECT: shapeId = 1; break; + default: wxLogDebug( wxT( "Unsupported pad shape %d\n" ), m_shape ); return; + } + + fmt->Print( indentLevel, "(\"%s\", %d, %.9f, %.9f, %.1f, M)\n", + (const char*) layerName.c_str(), shapeId, iu2hyp( m_sx ), iu2hyp( m_sy ), m_angle ); + } + + bool isEmpty() const + { + LSET layerMask = LSET::AllCuMask() & m_board->GetEnabledLayers(); + LSET outLayers = m_layers & layerMask; + + return outLayers.none(); + } + + void Format( int indentLevel, std::shared_ptr<FILE_OUTPUTFORMATTER> fmt ) + { + LSET layerMask = LSET::AllCuMask() & m_board->GetEnabledLayers(); + LSET outLayers = m_layers & layerMask; + + if( outLayers.none() ) + return; + + fmt->Print( 0, "{PADSTACK=%d, %.9f\n", m_id, iu2hyp( m_drill ) ); + + if( outLayers == layerMask ) + { + FormatPadShape( indentLevel + 1, fmt, wxT( "MDEF" ) ); + } + else + { + for( auto l : outLayers.Seq() ) + { + FormatPadShape( indentLevel + 1, fmt, m_board->GetLayerName( l ) ); + } + } + + + fmt->Print( 0, "}\n\n" ); + } + + +private: + BOARD* m_board; + int m_id; + int m_drill; + PAD_SHAPE_T m_shape; + int m_sx, m_sy; + double m_angle; + LSET m_layers; + PAD_ATTR_T m_type; +}; + +class HYPERLYNX_EXPORTER +{ +public: + HYPERLYNX_EXPORTER(){}; + ~HYPERLYNX_EXPORTER(){}; + + void SetOutputFilename( const wxFileName& aPath ) + { + m_outputFilePath = aPath; + } + + void SetBoard( BOARD* aBoard ) + { + m_board = aBoard; + } + + bool Run(); + + PAD_STACK* addPadStack( PAD_STACK stack ) + { + for( auto p : m_padStacks ) + { + if( *p == stack ) + return p; + } + + stack.SetId( m_padStacks.size() ); + m_padStacks.push_back( new PAD_STACK( stack ) ); + + return m_padStacks.back(); + } + + +private: + bool generateHeaders(); + bool writeBoardInfo(); + bool writeStackupInfo(); + bool writeDevices(); + bool writePadStacks(); + bool writeNets(); + bool writeNetObjects( const std::vector<BOARD_ITEM*>& aObjects ); + const std::vector<BOARD_ITEM*> collectNetObjects( int netcode ); + + std::vector<PAD_STACK*> m_padStacks; + std::map<BOARD_ITEM*, PAD_STACK*> m_padMap; + + + BOARD* m_board; + wxFileName m_outputFilePath; + std::shared_ptr<FILE_OUTPUTFORMATTER> m_out; + int m_polyId; +}; + + +PAD_STACK::PAD_STACK( BOARD* aBoard, const D_PAD* aPad ) +{ + m_board = aBoard; + m_sx = aPad->GetSize().x; + m_sy = aPad->GetSize().y; + //printf("sx %d sy %d\n", m_sx, m_sy ); + m_angle = 180.0 - ( aPad->GetOrientation() / 10.0 ); + if( m_angle < 0.0 ) + m_angle += 360.0; + m_layers = aPad->GetLayerSet(); + m_drill = aPad->GetDrillSize().x; + m_shape = aPad->GetShape(); + m_type = PAD_ATTRIB_STANDARD; +} + +PAD_STACK::PAD_STACK( BOARD* aBoard, const VIA* aVia ) +{ + m_board = aBoard; + m_sx = m_sy = aVia->GetWidth(); + //printf("sx %d sy %d\n", m_sx, m_sy ); + m_angle = 0; + m_layers = LSET::AllCuMask(); + m_drill = aVia->GetDrillValue(); + m_shape = PAD_SHAPE_CIRCLE; + m_type = PAD_ATTRIB_STANDARD; +} + +bool HYPERLYNX_EXPORTER::generateHeaders() +{ + m_out->Print( 0, "{VERSION=2.14}\n" ); + m_out->Print( 0, "{UNITS=ENGLISH LENGTH}\n\n" ); + return true; +} + + +bool HYPERLYNX_EXPORTER::writeBoardInfo() +{ + SHAPE_POLY_SET outlines; + wxString errText; + wxPoint errLoc; + + m_out->Print( 0, "{BOARD \"%s\"\n", (const char*) m_board->GetFileName().c_str() ); + + if( !m_board->GetBoardPolygonOutlines( outlines, &errText, &errLoc ) ) + { + return false; + } + + for( int o = 0; o < outlines.OutlineCount(); o++ ) + { + const auto& outl = outlines.COutline( o ); + for( int i = 0; i < outl.SegmentCount(); i++ ) + { + const auto& s = outl.CSegment( i ); + m_out->Print( 1, "(PERIMETER_SEGMENT X1=%.9f Y1=%.9f X2=%.9f Y2=%.9f)\n", + iu2hyp( s.A.x ), iu2hyp( s.A.y ), iu2hyp( s.B.x ), iu2hyp( s.B.y ) ); + } + } + m_out->Print( 0, "}\n\n" ); + + return true; +} + +bool HYPERLYNX_EXPORTER::writeStackupInfo() +{ + auto layers = m_board->GetDesignSettings().GetEnabledLayers().CuStack(); + + m_out->Print( 0, "{STACKUP\n" ); + + for( auto l : layers ) + { + const auto name = m_board->GetLayerName( l ); + m_out->Print( 1, "(SIGNAL T=0.002284 P=0.000000 C=1.724e-8 L=\"%s\" M=COPPER)\n", + (const char*) name.c_str() ); + if( l != B_Cu ) + { + m_out->Print( 1, "(DIELECTRIC T=0.007087 C=3.660000 L=\"DE_%s\" M=FR4)\n", + (const char*) name.c_str() ); + } + } + m_out->Print( 0, "}\n\n" ); + + return true; +} + +bool HYPERLYNX_EXPORTER::writeDevices() +{ + m_out->Print( 0, "{DEVICES\n" ); + + for( auto mod : m_board->Modules() ) + { + wxString ref = mod->GetReference(); + auto layerName = m_board->GetLayerName( mod->GetLayer() ); + + if( ref.IsEmpty() ) + ref = wxT( "EMPTY" ); + + m_out->Print( 1, "(? REF=\"%s\" L=\"%s\")\n", (const char*) ref.c_str(), + (const char*) layerName.c_str() ); + } + m_out->Print( 0, "}\n\n" ); + + return true; +} + +bool HYPERLYNX_EXPORTER::writePadStacks() +{ + for( auto mod : m_board->Modules() ) + { + for( auto pad : mod->Pads() ) + { + auto ps = addPadStack( PAD_STACK( m_board, pad ) ); + m_padMap[pad] = ps; + } + } + + for( auto trk : m_board->Tracks() ) + { + if( VIA* via = dyn_cast<VIA*>( trk ) ) + { + auto ps = addPadStack( PAD_STACK( m_board, via ) ); + m_padMap[via] = ps; + } + } + + for( auto pstack : m_padStacks ) + pstack->Format( 0, m_out ); + + return true; +} + +bool HYPERLYNX_EXPORTER::writeNetObjects( const std::vector<BOARD_ITEM*>& aObjects ) +{ + + for( auto item : aObjects ) + { + if( D_PAD* pad = dyn_cast<D_PAD*>( item ) ) + { + auto pstackIter = m_padMap.find( pad ); + if( pstackIter != m_padMap.end() ) + { + wxString ref = pad->GetParent()->GetReference(); + if( ref.IsEmpty() ) + ref = wxT( "EMPTY" ); + + auto padName = pad->GetName(); + + if( padName.IsEmpty() ) + padName = wxT( "1" ); + + + m_out->Print( 1, "(PIN X=%.10f Y=%.10f R=\"%s.%s\" P=%d)\n", + iu2hyp( pad->GetPosition().x ), iu2hyp( pad->GetPosition().y ), + (const char*) ref.c_str(), (const char*) padName.c_str(), + pstackIter->second->GetId() ); + } + } + else if( VIA* via = dyn_cast<VIA*>( item ) ) + { + auto pstackIter = m_padMap.find( via ); + if( pstackIter != m_padMap.end() ) + { + m_out->Print( 1, "(VIA X=%.10f Y=%.10f P=%d)\n", iu2hyp( via->GetPosition().x ), + iu2hyp( via->GetPosition().y ), pstackIter->second->GetId() ); + } + } + else if( TRACK* track = dyn_cast<TRACK*>( item ) ) + { + const auto layerName = m_board->GetLayerName( track->GetLayer() ); + m_out->Print( 1, "(SEG X1=%.10f Y1=%.10f X2=%.10f Y2=%.10f W=%.10f L=\"%s\")\n", + iu2hyp( track->GetStart().x ), iu2hyp( track->GetStart().y ), + iu2hyp( track->GetEnd().x ), iu2hyp( track->GetEnd().y ), + iu2hyp( track->GetWidth() ), (const char*) layerName.c_str() ); + } + else if( ZONE_CONTAINER* zone = dyn_cast<ZONE_CONTAINER*>( item ) ) + { + const auto layerName = m_board->GetLayerName( zone->GetLayer() ); + SHAPE_POLY_SET filledShape = zone->GetFilledPolysList(); + + filledShape.Simplify( SHAPE_POLY_SET::PM_FAST ); + + for( int i = 0; i < filledShape.OutlineCount(); i++ ) + { + const auto& outl = filledShape.COutline( i ); + + auto p0 = outl.CPoint( 0 ); + m_out->Print( 1, "{POLYGON T=POUR L=\"%s\" ID=%d X=%.10f Y=%.10f\n", + (const char*) layerName.c_str(), m_polyId, iu2hyp( p0.x ), iu2hyp( p0.y ) ); + + for( int v = 0; v < outl.PointCount(); v++ ) + { + m_out->Print( 2, "(LINE X=%.10f Y=%.10f)\n", iu2hyp( outl.CPoint( v ).x ), + iu2hyp( outl.CPoint( v ).y ) ); + } + + m_out->Print( 2, "(LINE X=%.10f Y=%.10f)\n", iu2hyp( p0.x ), iu2hyp( p0.y ) ); + + m_out->Print( 1, "}\n" ); + + for( int h = 0; h < filledShape.HoleCount( i ); h++ ) + { + const auto& holeShape = filledShape.CHole( i, h ); + auto ph0 = holeShape.CPoint( 0 ); + + m_out->Print( 1, "{POLYVOID ID=%d X=%.10f Y=%.10f\n", m_polyId, iu2hyp( ph0.x ), + iu2hyp( ph0.y ) ); + + for( int v = 0; v < holeShape.PointCount(); v++ ) + { + m_out->Print( 2, "(LINE X=%.10f Y=%.10f)\n", + iu2hyp( holeShape.CPoint( v ).x ), + iu2hyp( holeShape.CPoint( v ).y ) ); + } + m_out->Print( 2, "(LINE X=%.10f Y=%.10f)\n", iu2hyp( ph0.x ), iu2hyp( ph0.y ) ); + + m_out->Print( 1, "}\n" ); + } + + m_polyId++; + } + } + } + + return true; +} + +const std::vector<BOARD_ITEM*> HYPERLYNX_EXPORTER::collectNetObjects( int netcode ) +{ + std::vector<BOARD_ITEM*> rv; + + auto check = [&]( BOARD_CONNECTED_ITEM* item ) -> bool { + if( ( item->GetLayerSet() & LSET::AllCuMask() ).none() ) + return false; + if( item->GetNetCode() == netcode || ( netcode < 0 && item->GetNetCode() <= 0 ) ) + return true; + return false; + }; + + for( auto mod : m_board->Modules() ) + { + for( auto pad : mod->Pads() ) + { + if( check( pad ) ) + rv.push_back( pad ); + } + } + + for( auto item : m_board->Tracks() ) + if( check( item ) ) + rv.push_back( item ); + + for( int i = 0; i < m_board->GetAreaCount(); i++ ) + { + auto zone = m_board->GetArea( i ); + if( check( zone ) ) + rv.push_back( zone ); + } + return rv; +} + +bool HYPERLYNX_EXPORTER::writeNets() +{ + m_polyId = 1; + + for( const auto netInfo : m_board->GetNetInfo() ) + { + int netcode = netInfo->GetNet(); + + //printf( " netCode %d '%s' \n", netcode, (const char*) netInfo->GetNetname().c_str() ); + + bool isNullNet = netInfo->GetNet() <= 0 || netInfo->GetNetname().IsEmpty(); + if( isNullNet ) + continue; + + auto netObjects = collectNetObjects( netcode ); + if( netObjects.size() ) + { + + m_out->Print( 0, "{NET \"%s\"\n", (const char*) netInfo->GetNetname().c_str() ); + writeNetObjects( netObjects ); + m_out->Print( 0, "}\n\n" ); + } + } + + auto nullNetObjects = collectNetObjects( -1 ); + + //printf( "Null net objects: %d\n", nullNetObjects.size() ); + + int idx = 0; + + for( auto item : nullNetObjects ) + { + m_out->Print( 0, "{NET \"EmptyNet%d\"\n", idx ); + writeNetObjects( { item } ); + m_out->Print( 0, "}\n\n" ); + idx++; + } + + return true; +} + +bool HYPERLYNX_EXPORTER::Run() +{ + LOCALE_IO toggle; // toggles on, then off, the C locale. + + m_out.reset( new FILE_OUTPUTFORMATTER( m_outputFilePath.GetFullPath() ) ); + + generateHeaders(); + writeBoardInfo(); + writeStackupInfo(); + writeDevices(); + writePadStacks(); + writeNets(); + + return true; +} + + +bool ExportBoardToHyperlynx( BOARD* aBoard, const wxFileName& aPath ) +{ + HYPERLYNX_EXPORTER exporter; + exporter.SetBoard( aBoard ); + exporter.SetOutputFilename( aPath ); + return exporter.Run(); +} diff --git a/pcbnew/menubar_pcb_editor.cpp b/pcbnew/menubar_pcb_editor.cpp index 422456245..3e0711920 100644 --- a/pcbnew/menubar_pcb_editor.cpp +++ b/pcbnew/menubar_pcb_editor.cpp @@ -970,4 +970,9 @@ void prepareExportMenu( wxMenu* aParentMenu ) _( "&Footprint Association (.cmp) File..." ), _( "Export footprint association file (*.cmp) for schematic back annotation" ), KiBitmap( create_cmp_file_xpm ) ); + + AddMenuItem( aParentMenu, ID_GEN_EXPORT_FILE_HYPERLYNX, + _( "&Hyperlynx..." ), _( "Hyperlynx export" ), + KiBitmap( export_step_xpm ) ); + } diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index b4db36fb5..369e63a75 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -128,6 +128,7 @@ BEGIN_EVENT_TABLE( PCB_EDIT_FRAME, PCB_BASE_FRAME ) EVT_MENU( ID_GEN_EXPORT_FILE_VRML, PCB_EDIT_FRAME::OnExportVRML ) EVT_MENU( ID_GEN_EXPORT_FILE_IDF3, PCB_EDIT_FRAME::OnExportIDF3 ) EVT_MENU( ID_GEN_EXPORT_FILE_STEP, PCB_EDIT_FRAME::OnExportSTEP ) + EVT_MENU( ID_GEN_EXPORT_FILE_HYPERLYNX, PCB_EDIT_FRAME::OnExportHyperlynx ) EVT_MENU( ID_GEN_IMPORT_SPECCTRA_SESSION,PCB_EDIT_FRAME::ImportSpecctraSession ) EVT_MENU( ID_GEN_IMPORT_SPECCTRA_DESIGN, PCB_EDIT_FRAME::ImportSpecctraDesign ) @@ -1369,3 +1370,31 @@ void PCB_EDIT_FRAME::LockModule( MODULE* aModule, bool aLocked ) } } } + +bool ExportBoardToHyperlynx( BOARD* aBoard, const wxFileName& aPath ); + +void PCB_EDIT_FRAME::OnExportHyperlynx( wxCommandEvent& event ) +{ + wxString wildcard = wxT("*.hyp"); + wxFileName fn = GetBoard()->GetFileName(); + + fn.SetExt( wxT("hyp") ); + + wxFileDialog dlg( this, + _( "Export Hyperlynx Layout" ), + fn.GetPath(), + fn.GetFullName(), + wildcard, + wxFD_SAVE | wxFD_OVERWRITE_PROMPT + ); + + if( dlg.ShowModal() != wxID_OK ) + return; + + fn = dlg.GetPath(); + + // always enforce filename extension, user may not have entered it. + fn.SetExt( wxT("hyp") ); + + ExportBoardToHyperlynx( GetBoard(), fn ); +} diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h index a916893b2..cc6f5f89a 100644 --- a/pcbnew/pcb_edit_frame.h +++ b/pcbnew/pcb_edit_frame.h @@ -1044,6 +1044,13 @@ public: */ void OnExportIDF3( wxCommandEvent& event ); + /** + * Function OnExportHyperlynx + * will export the current BOARD to a Hyperlynx HYP file. + */ + void OnExportHyperlynx( wxCommandEvent& event ); + + /** * Function Export_IDF3 * Creates an IDF3 compliant BOARD (*.emn) and LIBRARY (*.emp) file. diff --git a/pcbnew/pcbnew_id.h b/pcbnew/pcbnew_id.h index dfe402af5..0d18e2e00 100644 --- a/pcbnew/pcbnew_id.h +++ b/pcbnew/pcbnew_id.h @@ -273,6 +273,7 @@ enum pcbnew_ids ID_GEN_EXPORT_FILE_IDF3, ID_GEN_EXPORT_FILE_VRML, ID_GEN_EXPORT_FILE_STEP, + ID_GEN_EXPORT_FILE_HYPERLYNX, ID_GEN_EXPORT_SPECCTRA, ID_GEN_EXPORT_FILE_GENCADFORMAT, ID_GEN_EXPORT_FILE_MODULE_REPORT, diff --git a/qa/CMakeLists.txt b/qa/CMakeLists.txt index 5822170df..eab06c313 100644 --- a/qa/CMakeLists.txt +++ b/qa/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory( unit_test_utils ) # Unit tests add_subdirectory( common ) +add_subdirectory( hyperlynx_export ) add_subdirectory( pcbnew ) add_subdirectory( eeschema ) diff --git a/qa/hyperlynx_export/CMakeLists.txt b/qa/hyperlynx_export/CMakeLists.txt new file mode 100644 index 000000000..0e279b370 --- /dev/null +++ b/qa/hyperlynx_export/CMakeLists.txt @@ -0,0 +1,64 @@ +# This program source code file is part of KiCad, a free EDA CAD application. +# +# Copyright (C) 2018 KiCad Developers, see CHANGELOG.TXT for contributors. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, you may find one here: +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# or you may search the http://www.gnu.org website for the version 2 license, +# or you may write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +if( BUILD_GITHUB_PLUGIN ) + set( GITHUB_PLUGIN_LIBRARIES github_plugin ) +endif() + +include_directories( ../../include + ../../common + ../../pcbnew + ../.. ) + +add_executable( test_hyperlynx_export + # stuff from common which is needed...why? + ../../common/colors.cpp + ../../common/observable.cpp + + # The main test entry points + test_hyperlynx_export.cpp + + # Older CMakes cannot link OBJECT libraries + # https://cmake.org/pipermail/cmake/2013-November/056263.html + $<TARGET_OBJECTS:pcbnew_kiface_objects> +) + +target_link_libraries( test_hyperlynx_export + 3d-viewer + connectivity + pcbcommon + pnsrouter + pcad2kicadpcb + legacy_wx + gal + common + qa_utils + lib_dxf + idf3 + unit_test_utils + ${wxWidgets_LIBRARIES} + ${GITHUB_PLUGIN_LIBRARIES} + ${GDI_PLUS_LIBRARIES} + ${PYTHON_LIBRARIES} + ${Boost_LIBRARIES} # must follow GITHUB + ${PCBNEW_EXTRA_LIBS} # -lrt must follow Boost +) + diff --git a/qa/hyperlynx_export/test_hyperlynx_export.cpp b/qa/hyperlynx_export/test_hyperlynx_export.cpp new file mode 100644 index 000000000..479c25446 --- /dev/null +++ b/qa/hyperlynx_export/test_hyperlynx_export.cpp @@ -0,0 +1,61 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2018 KiCad Developers, see CHANGELOG.TXT for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * Main file for the pcbnew tests to be compiled + */ + +#include <wx/wx.h> +#include <class_board.h> +#include <io_mgr.h> +#include <kicad_plugin.h> + +bool ExportBoardToHyperlynx( BOARD* aBoard, const wxFileName& aPath ); + +BOARD* loadBoard( const std::string& filename ) +{ + PLUGIN::RELEASER pi( new PCB_IO ); + BOARD* brd = nullptr; + + try + { + brd = pi->Load( wxString( filename.c_str() ), NULL, NULL ); + } + catch( const IO_ERROR& ioe ) + { + wxString msg = wxString::Format( _( "Error loading board.\n%s" ), + ioe.Problem() ); + + printf( "%s\n", (const char*) msg.mb_str() ); + return nullptr; + } + return brd; + +} + +int main( int argc, char* argv[] ) +{ + auto brd = loadBoard("test.kicad_pcb"); + ExportBoardToHyperlynx( brd, wxString( "test.hyp" ) ); + return 0; +} \ No newline at end of file -- 2.17.1
_______________________________________________ Mailing list: https://launchpad.net/~kicad-developers Post to : kicad-developers@lists.launchpad.net Unsubscribe : https://launchpad.net/~kicad-developers More help : https://help.launchpad.net/ListHelp