'도구/기타'에 해당되는 글 13건

  1. 2024.06.09 CSV Signal Analyzer 1
  2. 2024.04.21 Serial Data Generator
  3. 2023.12.27 Harness Wizard
  4. 2021.02.03 Harness Wizard 2
  5. 2020.04.05 사양 BOM 생성기 (매트릭스 버전)
  6. 2020.03.25 사양 BOM 생성기
  7. 2018.09.16 Eye Diagram Viewer - 파이썬 소스
  8. 2018.09.03 Wave 2 Table
  9. 2016.12.05 Spreadsheet Schematic Editor for Allegro
  10. 2016.11.21 패키지/커넥터 핀맵 작성기

CSV Signal Analyzer

도구/기타 2024. 6. 9. 21:45

오실로 스코프로 신호를 측정할 떄, 종종 측정 결과를 CSV 포맷으로 저장하는 경우가 있다. 이는 나중에 PC에서 후가공을 하거나 측정 결과 보고서를 작성할 때 첨부로 쓰기 위함일 것이다. 그런데, CSV로 저장된 파일은 PC에서 보기가 쉽지 않다. 스코프 제조사에서 제공해주는 도구를 사용하거나 엑셀을 사용해야 한다. 제조사 도구 같은 경우 제공되지 않거나 찾기 어려운 경우가 있고, 엑셀 같은 경우 CSV 파일이 크면 행수 제한으로 데이터까 짤리는 문제와 속도가 현저하게 느려지는 문제 등이 있다. 이에 쉽게 CSV 파일을 열어서 파형을 보고 쉽게 보고서에 Copy&Paste를 할 수 있는 도구를 만들어 보았다.

오실로스코프에서 제공되는 CSV파일은 파일 초반에 헤더가 표시되어 있다. 제조사마다 헤더에 포함되는 내용이 다르고 헤더로 사용된 줄 수도 다르다. 이 부분은 실제 데이터가 아니므로 건더 뛰어야 한다. 이렇게 건너 뛰어야 하는 행 수를 Header Row Count에 입력한다. 기본값은 20이다(데이터 값이 최소 수백개에서 백만개 이상 일 것이므로 20이라는 숫자를 구지 수정하지 않아도 문제가 될 것은 없다). 아래는 CSV 파일의 한 예이다. 헤더로 2줄이 사용되었다.

위 예제 CSV 파일은 3개의 열로 구성된다. 오실로스코프에서 저장되는 CSV 파일은 첫 열이 시간 데이터이다. 그리고 두번째 열부터 순서대로 신호 데이터열이 된다. 여기서는 2개의 신호 데이터 열이 있는 것을 볼 수 있다.

본 도구에서 Sample Step은  기본값으로 Auto로 설정되어 있는데, 이는 CSV의 첫 열에 시간 데이터가 있기 때문에 그 데이터로부터 자동으로 Sampling Time이 추출되기 때문이다. 만약 시간 데이터 없이 첫 열부터 데이터가 오는 CSV 파일이라면 각 데이터들의 sampling time을 Sampling Step 란에 적어주면 된다.

본 두구에서 오실로스코프처럼 가로와 세로 방향으로 각가 2개의 커서를 제공한다.

CSV에 신호 데이터가 몇 개 포함되었는지 자동 감지후 사용가능한 채널을 표기해 주며 각 채널을 on/off 할 수 있다. 화면에 보여지는 신호는 자동으로 max, min, median, average가 계산된다.

Frequency Domain을 선택하면 신호의 주파수 성분을 볼 수 있다.

csv_signal_analyzer.z01
10.00MB
csv_signal_analyzer.z02
10.00MB
csv_signal_analyzer.z03
10.00MB
csv_signal_analyzer.z04
10.00MB
csv_signal_analyzer.zip
6.55MB

'도구 > 기타' 카테고리의 다른 글

Serial Data Generator  (0) 2024.04.21
Harness Wizard  (0) 2023.12.27
Harness Wizard  (2) 2021.02.03
사양 BOM 생성기 (매트릭스 버전)  (0) 2020.04.05
사양 BOM 생성기  (0) 2020.03.25
:

Serial Data Generator

도구/기타 2024. 4. 21. 23:00

보드를 처음 제작하게 되면, HW가 정상적으로 동작하는지 검증하기 위해서, MCU나 FPGA 같은 제어 디바이스의 레지스터에 값을 쓰거나 읽는 것으로 평가 작업을 진행한다. 이런 검증 작업은 주로 디버깅용 시리얼 포트를 통해 진행하게 되고, 개발 초기에는 주로 시리얼 통신용 터미널 프로그램을 사용해 진행한다. 아직 전용 제어 UI 프로그램이 없기 때문이다.

범용 시리얼 통신 프로그램은 평가 작업을 자동화하기 어려워, 데이터 값 스윕이나 반복 작업 등을 할 때 많은 시간이 소요된다. 따라서 시리얼 통신을 통해 제어 디바이스와 주고 받을 데이터를 스크립트로 작성하여 자동실행을 시키면 검증 시간을 대폭 단축할 수 있다.

SDG(Serial Data Generator)는 시리얼 포트를 통해 제어 장치로 보낼 데이터를 자동으로 생성해 전송해주는 기능을 수행한다.

주요 기능:

  • 일반적인 시러얼 통신 기능. Hexademial 또는 ASCII로 데이터 송신 또는 수신
  • Delimiter 자동 추가
  • CRC 자동 생성
  • 전용 스크립트(텍스트 파일) 실행

스크립트 기능:

  • Case-insensitive
  • 주석 처리 및 모니터 창에 출력
  • 변수 정의, 변수에 값 할당, 변수 연산(사칙연산, 비트연산)
  • wait, pause
  • repeat
  • 수신된 데이터를 예상 값과 비교
  • 수신된 데이터를 변수에 할당
  • 변수를 포함한 수식 계산

SDG.zip
9.94MB
sample_script.sdg
0.00MB
sample_script2.sdg
0.00MB

sdg 파일은 일반 텍스트 파일이다.

명령어등 사용방법은 스크립트 파일 2개를 참조한다. sample_sciprt2를 먼저 전송하고 sample_script를 이어서 수행한 화면이 위 두 그림이다.

 

'도구 > 기타' 카테고리의 다른 글

CSV Signal Analyzer  (1) 2024.06.09
Harness Wizard  (0) 2023.12.27
Harness Wizard  (2) 2021.02.03
사양 BOM 생성기 (매트릭스 버전)  (0) 2020.04.05
사양 BOM 생성기  (0) 2020.03.25
:

Harness Wizard

도구/기타 2023. 12. 27. 21:41

'도구 > 기타' 카테고리의 다른 글

CSV Signal Analyzer  (1) 2024.06.09
Serial Data Generator  (0) 2024.04.21
Harness Wizard  (2) 2021.02.03
사양 BOM 생성기 (매트릭스 버전)  (0) 2020.04.05
사양 BOM 생성기  (0) 2020.03.25
:

Harness Wizard

도구/기타 2021. 2. 3. 22:00

1.     목적

Harness 도면의 경우, 접속도 또는 회로도와 달리, drawing 규칙이 정형화되어 있어 자동화가 비교적 용이하다. 따라서, Harness 도면 드로잉을 자동화하면, 도면 작성과 검도 시간을 절약할 수 있다. 이를 위해 Harness Wizard를 만들게 되었다.

 

2.     Harness Wizard 화면 구성

Harness Wizard 입력 시트 구성은 표제란(A), 자재란(B), Harness 구성(C), 그리고 기타(D) 네 부분으로 되어 있다.

 

3.     자재란

자재란은 도면에서 사용되는 자재인 케이블이나 커넥터 등을 정의한다. 여기서 정의된 자재는 각각 고유의 지정자(Material Designator)를 갖게 되며, 도면에서 이것으로 자재를 식별하게 된다.

자재란의 각 열에 대한 설명은 아래와 같다.

Mat. Des. 자재를 고유하게 식별하기 위한 지정자(반드시 숫자이어야 함)
Type Harness 구성 요소로 Cable, Connector는 필수 요소이고,
Connector 아래에 옵션(예들 들어 Terminal)을 추가할 수 있다.
Model(Or Specification) 모델 번호나 규격 등에 해당 자재에 대한 정보
Maker 해당 자재 제조사
Start Pin Type Cable인 경우, 와이어 번호이고,
Type Connector인 경우 핀 번
End Pin  "
추가 정보 Type Cable인 경우, 수축튜브 직경 값
Type Connector인 경우, 핀 번호 prefix(옵션). Prefix는 콤마로 구분하여 다중 사용

자재란의 정의는 위 규칙을 준수하는 범위 내에서 얼마든지 길게 작성 가능하다. 자재란 영역은 색으로 구분되므로 바탕색을 준수해야 한다.

아래는 3개의 자재를 정의한 예들 보여준다.

1개의 케이블과 2개의 커넥터가 정의되어 있다. 케이블은 8가닥의 선으로 되어 있으며 지정자(Mat. Des.)1 이다. 지정자 2로 정의된 커넥터는 별도의 터미널을 안에 가지고 있으며 A1~A4, B1~B4 8개의 핀을 갖고 있다. 지정자 3으로 정의된 커넥터는 내부 핀이 통합되어 있는 커넥터로 8개의 핀(1~8)을 갖고 있다. 아마도 Harness를 구성하려면 최소한 1개 이상의 Cable1개 이상의 Connector가 정의되어야 할 것이다.

 

4.     Harness 구성

Harness 구성은 자재란에 기술된 자재를 이용해서 Harness가 어떻게 구성되는지 정의한다. 기본적으로 Cable, Connector A, Connector B 3개 부분이 하나의 구성 세트를 이룬다. 하나의 케이블은 최소한 하나 이상의 Connector AConnector B가 필요하다. 하나의 도면에 여러 개 Cable을 드로잉하기 원할 경우 여러 개의 세트를 순서대로 기술하면 된다.

먼저 하나의 구성 세트 예를 살펴보자.

Harness는 하나의 Cable이 있으며 케이블 왼쪽(A) 1개의 커넥터가 있고, 오른쪽(B) 1개의 커넥터가 있다. Harness에서 사용되는 자제는 3장 자재란에서 정의된 자재이다. 이 구성 정의는 다음과 같다.

먼저 케이블을 정의하고 그 다음 Connector A 그리고 Connector B 순서로 한다. 하나의 케이블에 연결된 커넥터 수 많을 경우, A 또는 B 쪽에 원하는 만큼 계속 정의해주면 된다. 단 입력되는 순서는 Connector A(s)를 다 입력한 후에 Connector B(s)를 입력해야 한다. 입력된 자재들의 종류는 Materials 열에서 Cable, Connector A, Connector B 중 하나의 키워드를 사용해야 한다.

Cable 자재는 반드시 아래와 같은 속성(Properties)을 가져야 하며, 순서대로 입력되어야 한다.

Material Designator 자재란에 정의된 자재 Mat. Des. 중 하나
Length Cable 길이 (mm)
Shield 실드 케이블 사용 유무.
A, B 
또는 빈칸
Label A A쪽 라벨명
Tag A A쪽 태그명(옵션)
Heat Shrink Tube Color A A쪽 수축튜브 색상
Label B B쪽 라벨명
Tag B B쪽 태그명(옵션)
Heat Shrink Tube Color B B쪽 수축튜브 색상

Connector A 또는 Connector B 자재는 아래와 같은 속성을 반드시 가져야 하며, 순서대로 입력되어야 한다.

Material Designator 자재란에 정의된 자재 Mat. Des. 중 하나
Length Cable 탈피 길이 (mm)
Pin Assignment 케이블 와이어 번호.
커넥터 핀 수만큼 정의하며, 할당은 핀 번호 순서대로 콤마로 구분하여 기술한다.
Tag Cable 자재에서 태그를 사용하지 않을 경우, 커넥터 쪽에서 태그를 정의할 수 있다.
태그는 하나만 정의해서 커넥터 전체를 대표하거나, 콤마로 구분해서 핀 수만큼 정의한다.

ConnectorPin Assignment 속성은 커넥터 핀과 케이블 와이어 간의 연결 관계를 정의한다. 앞예에서, A쪽 커넥터는 Mat. Des. 2 이다. 자재란에서 2번 자재를 보면 시작 핀이 A1이고 마지막 핀이 B4이다. 8개의 핀을 갖고 있다. 따라서, Pin Assignment에서 8개의 와이어에 연결을 해야 하고, 여기서는 핀번호 순서대로 각각 1, 2, 3, 4, 5, 6, 7, 8번 와이어를 연결했다. 커넥터의 핀에 와이어 할당을 하지 않으려면, 해당 위치는 빈칸으로 둔다. 만약 A2번 핀에 와이어를 할당하지 않으려면, “1, ,3,4,5,6,7,8”처럼 하면 된다.

 

5.     도면 시트 크기

도면 크기는 A4, A3, AUTO 중 하나를 고를 수 있다.

6.     Shield 처리

Shield된 케이블을 사용하는 경우, Cable 자재의 Shield Property 값으로 A 또는(그리고) B를 사용하면 된다. 이 부분이 비이 있으면 Unshielded(Normal) 케이블을 의미한다.

Shield는 커넥터 하우징과 Bonding 되거나 커넥터의 한 핀에 Drain Wire가 연결되는 형태를 취한다. 커넥터 하우징에 Bonding하기 원할 경우, Connector A(B) 자재 정의 부분에서 따로 정의할 것은 없다. 그러나, Drain Wire를 커넥터 핀에 할당하고자 할 경우, 해당 핀 번호 자리에 “shield”를 입력한다.

다음은 이전 예에서 Connector A의 하우징이 아닌 A4번 핀에 shield를 연결한 경우이다.

Connector A 커넥터에서 A4번 핀에 wire 4번이 생략되며 shied가 연결된다. Connector B 커넥터에서도 6번째 핀 자리가 빈칸이므로 wire 연결을 하지 않는다.

 

7.     점퍼 처리

케이블에서 나온 하나의 wire를 커넥터 2개 이상의 핀에 연결하기 원할 경우, Pin Assignment 속성에서 해당 핀위치에 wire 번호를 동일하게 입력하면 된다.

다음 구성 예는 AB쪽 모두에서 5번과 7번 와이어를 2개의 핀에 할당한 경우를 보여준다.

아래는 구현된 도면이다.

 

8.     커넥터 공유

위 도면처럼 하나의 커넥터가 2개의 케이블에서 공유되는 경우, 아래처럼 Share Des. 열에서 공유되는 커넥터에 동일한 지정자를 임의로 지정해 주면 된다.

 

9.     다중 라벨

동일 Harness에 라벨(또는 길이)만 다르게 적용하고 싶은 경우, 도면에 라벨 테이블을 추가할 수 있다. 이 기능은 테이블 시트 이름항목에 테이블 정보가 있는 엑셀 시트 이름을 입력해주고, 해당 엑셀 시트에서 테이블을 만들어 주면 된다.

테이블 엑셀 시트의 A1 셀부터 도면에 표시될 표를 작성한다. 이때 헤더 부분의 각 항목은 도면 생성시트의 Label A(B), Tag (A/B), 또는 길이와 이름을 같게 해주는 것이 바람직하다.

구성은 예는 아래와 같다.

생성된 도면은 다음과 같다.

 

10.  핀과 태그 할당 편의 기능

커넥터에 핀 수가 많은 경우 와이어 할당에 어려움이 있을 수 있다. 와이어 번호와 핀 번호가 제대로 맵핑 되었는지 확인이 어렵기 때문이다(태그도 마찬가지). 이를 쉽게 하기 위해서 핀 할당 적용기능과 핀 할당 펼치기기능이 있다.

핀 할당 펼치기는 현재 커넥터에 할당된 핀과 태그가 4개 이상일 경우 오른쪽에 그것을 펼친다. 아래는 핀 할당을 펼치기 전이다.

아래는 핀 할당을 펼친 후이다.

핀 할당 펼치기의 반대가 핀 할당 적용이다. 커넥터 핀에 대한 와이어 번호 할당을 쉽게 할 수 있게 해준다. 아래는 핀 할당 적용 전이다.

아래는 핀 할당 적용 후이다.

 

별표(*) 부분은 단순히 식별을 용이하게 하기 위한 목적이다.

 

11.  Harness 도면 만들기

a.     Harness Wizard 엑셀 파일에서 Harness 관련 정보 입력 후 생성 버튼을 클릭한다.

b.     파일 메뉴에서 내보내기를 선태하고 PDF파일을 생성한다.

c.     AutoCAD에서 삽입 메뉴 아래 PDF 가져오기를 실행한다.

d.     Import된 도면을 DWG 파일로 저장한다.

  

12.  문의

버그 보고, 기능 수정 및 추가, 기타 문의는 specctra@gmail.com으로 해주세요.

 

Harness Wizard 1.14R.xlsm
0.22MB

'도구 > 기타' 카테고리의 다른 글

Serial Data Generator  (0) 2024.04.21
Harness Wizard  (0) 2023.12.27
사양 BOM 생성기 (매트릭스 버전)  (0) 2020.04.05
사양 BOM 생성기  (0) 2020.03.25
Eye Diagram Viewer - 파이썬 소스  (0) 2018.09.16
:

사양 BOM 생성기 (매트릭스 버전)

도구/기타 2020. 4. 5. 20:23

엑셀로 두번째 사양 기본 BOM 생성기를 만들어 보았다.

지난 번에 처음 만들었던 버전에 사용상 불편함이 많아 새로 단순하게 사용할 수 있도록 했다.

기본적인 기능은 처음 공개한 것과 같다.

기능 1. 모델 선택에 따른 선택 옵션 항목 자동 변경

기능 2. 코드 수정 없이 사용자가 임의로 옵션 추가 및 수정

기능 3. 쉬운 마스터 BOM 옵션 적용

기능 4. 사양 옵션에 기반한 BOM 자동 생성

개선된 점:

  기능 1에서 모델 변경에 따라 사용할 수 없던 사양 항목이 표시되던 버그 수정

세단 모델에서 선택 가능한 하이브리드 엔진 사양이 SUV 모델에서는 선택할 수 없다.

  기능 2에서  사용자가 콤보 박스 속성 수정하는 등 불편한 점 해결. 

사양을 추가하고 싶은 곳에 추가하고 아무 콤보박스나 추가된 사양 옆에 복사하면 끝 

  기능 3에서 매트릭스 테이블 적용으로 더 쉬운 옵션 적용


사양 BOM 생성기 (매트릭스버전) v1.24R.xlsm





'도구 > 기타' 카테고리의 다른 글

Harness Wizard  (0) 2023.12.27
Harness Wizard  (2) 2021.02.03
사양 BOM 생성기  (0) 2020.03.25
Eye Diagram Viewer - 파이썬 소스  (0) 2018.09.16
Wave 2 Table  (0) 2018.09.03
:

사양 BOM 생성기

도구/기타 2020. 3. 25. 22:15

엑셀로 사양 기반 BOM 생성기를 만들어 보았다. 여기서 예로 든 BOM(간략화한 자동차 구성 BOM)은 그냥 구현 과정 설명을 위한 것으로 실제와는 매우 크게 다를 것이다.  사용자가 자신의 실상에 맞게 수정하여 사용하면 된다.

기능 1. 모델 선택에 따른 선택 사양 항목 자동 변경

기능 2. 코드 수정 없이 사용자가 임의로 사양 추가 및 수정

기능 3. 쉬운 마스터 BOM 사양 적용

기능 4. 사양에 기반한 BOM 자동 생성


사양 BOM 생성기 v1.34R.xlsm






'도구 > 기타' 카테고리의 다른 글

Harness Wizard  (2) 2021.02.03
사양 BOM 생성기 (매트릭스 버전)  (0) 2020.04.05
Eye Diagram Viewer - 파이썬 소스  (0) 2018.09.16
Wave 2 Table  (0) 2018.09.03
Spreadsheet Schematic Editor for Allegro  (0) 2016.12.05
:

Eye Diagram Viewer - 파이썬 소스

도구/기타 2018. 9. 16. 22:00



XML 디자인:

XML 소스:

eyediagram_viewer.ui


Python 소스:

import sys

from PyQt5.QtWidgets import *

from PyQt5 import uic

import csv

import matplotlib.pyplot as plt

import numpy as np

from scipy.interpolate import interp1d

from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5

if is_pyqt5():

    from matplotlib.backends.backend_qt5agg import (

            FigureCanvas, NavigationToolbar2QT as NavigationToolbar)

else:

    from matplotlib.backends.backend_qt4agg import (

            FigureCanvas, NavigationToolbar2QT as NavigationToolbar)



def bres_segment_count(x0, y0, x1, y1, grid, endpoint):

    """

    Bresenham's algorithm.

    See http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm

    """


    nrows = grid.shape[0]

    ncols = grid.shape[1]


    if x1 > x0:

        dx = x1 - x0

    else:

        dx = x0 - x1

    if y1 > y0:

        dy = y1 - y0

    else:

        dy = y0 - y1


    sx = 0

    if x0 < x1:

        sx = 1

    else:

        sx = -1

    sy = 0

    if y0 < y1:

        sy = 1

    else:

        sy = -1


    err = dx - dy


    while True:

        # When endpoint is 0, this test occurs before we increment the

        # grid value, so we don't count the last point.

        if endpoint == 0 and x0 == x1 and y0 == y1:

            break


        if (0 <= x0 < nrows) and (0 <= y0 < ncols):

            grid[x0, y0] += 1


        if x0 == x1 and y0 == y1:

            break


        e2 = 2 * err

        if e2 > -dy:

            err -= dy

            x0 += sx

        if e2 < dx:

            err += dx

            y0 += sy


    return 0



def bres_curve_count(x, y, grid):

    for k in range(len(x)-1):

        x0 = x[k]

        y0 = y[k]

        x1 = x[k+1]

        y1 = y[k+1]

        bres_segment_count(x0, y0, x1, y1, grid, 0)


    if 0 <= x1 < grid.shape[0] and 0 <= y1 < grid.shape[1]:

        # Count the last point in the curve.

        grid[x1, y1] += 1



def grid_count(y, window_size, offset, bounds, size=None, fuzz=True):

    """

    파라미터

    ----------

    `y` - 1차원 신호 샘플 배열

    `window_size` - eye diagram에서 수평으로 보여질 샘플 수. 통상 심볼(비트) 샘플의 2배

    `offset` - eye diagram을 계산하기 전에 건너 뛸 초기 샘플 수. eye의 전반적 위상 조정

    `bounds` - 두 부동소수 튜 플이어야 함. 돌려질 배열의 y 범위(ymin, ymax) 설정.

    `size` - 두 정수 튜플이어야만 함. 배열의 크기(height, width) 설정. 기본값은(800,600)

    `fuzz` - True일 경우, y 값이 랜덤 퍼즈 요소로 강제 인터폴레이션 됨. 알리아싱 감소

    반환 값 - 정수 numpy 배열

    """

    if size is None:

        size = (800, 640)

    width, height = size

    dt = width / window_size

    counts = np.zeros((width, height), dtype=np.int32)


    start = offset

    while start + window_size < len(y):

        end = start + window_size

        yy = y[start:end+1]

        k = np.arange(len(yy))

        xx = dt*k

        if fuzz:

            f = interp1d(xx, yy, kind='cubic')

            jiggle = dt*(np.random.beta(a=3, b=3, size=len(xx)-2) - 0.5)

            xx[1:-1] += jiggle

            yd = f(xx)

        else:

            yd = yy

        iyd = (height * (yd - bounds[0])/(bounds[1] - bounds[0])).astype(np.int32)

        bres_curve_count(xx.astype(np.int32), iyd, counts)


        start = end

    return counts



def read_and_plot(appWin):

    # 소수점 출력 자리수 설정

    unitx = '.' + str(appWin.unitX) + 'f'                                      

    unity = '.' + str(appWin.unitY) + 'f'

    

    samples_per_symbol = 20

    

    npx = np.array(appWin.x)    # numpy 배열로 변환

    number_of_samples = int((npx[len(npx)-1]/appWin.ui)*samples_per_symbol)  # 총샘플 수

    ipx = np.linspace(0, npx[len(npx)-1], number_of_samples)  # 총 샘플수에 맞게 x 샘플 수 조정

    

    npy= np.array(appWin.y)

    ipy = np.interp(ipx, npx, npy)  # *** x 를 참조하여 y 샘플수를 조정

    

    # ------ high level과 low level을 histogram을 사용하여 찾기 ------------------

    n, bins, patches = plt.hist(ipy, bins=100)

    peak = 0                                 # 히스토그램 최대 분포

    high = 0                                 # high와 low 두 부분으로 나누어 진행

    for i in range(50,99):

        if n[i] > peak:

            high = i

    peak = 0

    low = 0

    for i in range(50,49):

        if n[i] > peak:

            low = i

    high = (bins[high]+bins[high+1])/2

    low = (bins[low]+bins[low+1])/2

    mid = (high+low)/2

    amp = high - low

    appWin.lineEditHigh.setText(format(high, unity))

    appWin.lineEditLow.setText(format(low, unity))

    appWin.lineEditMid.setText(format(mid, unity))

    

    # ------- 상승 및 하강 시간 찾기 ---------------------------------------------

    p10 = amp*0.1 + low

    p90 = amp*0.9 + low

    tran = False

    step = 0                 # 누적 상승 구간 스텝 수

    count = 0                # 상승 구간 개수

    for i in range(0, len(ipy)-1):

        if ipy[i] <= p10 and ipy[i+1] >= p10 and tran == False:      # 10%를 지날 때 step 카운트 시작

            tran = True

            start = i

        elif ipy[i] <= p90 and ipy[i+1] >= p90 and tran == True:     # 90%를 지날 때 step 카운트 종료

            tran = False

            step = step + i - start

            count = count + 1

    appWin.lineEditRise.setText(format(appWin.ui*(step/count)/samples_per_symbol, unitx))

    tran = False                                                     # 하강 구간에 대해서 동일하게

    step = 0

    count = 0

    for i in range(0, len(ipy)-1):

        if ipy[i] >= p90 and ipy[i+1] <= p90 and tran == False:

            tran = True

            start = i

        elif ipy[i] >= p10 and ipy[i+1] <= p10 and tran == True:

            tran = False

            step = step + i - start

            count = count + 1

    appWin.lineEditFall.setText(format(appWin.ui*(step/count)/samples_per_symbol, unitx))

    

    # eye diagram을 shift 시키기 위한 offset값. 반대방향으로 적용

    offset = appWin.offset

    while offset < 0:                 # grid count에서 offset은 +만 되므로 -로 변환

        offset = offset + appWin.ui

    offset = int(samples_per_symbol*offset/appWin.ui)   # 시간 단위 옵셋을 샘플 수 단위로 변환

    if offset > samples_per_symbol:                     # 옵셋이 UI를 넘으면 보정

        offset = offset % samples_per_symbol

    skip = samples_per_symbol - offset

    if appWin.ampMinMax[0] == appWin.ampMinMax[1]:

        ymax = ipy.max()

        ymin = ipy.min()

        yamp = ymax - ymin

        min_y = ymin - 0.05*yamp

        max_y = ymax + 0.05*yamp

    else:

        min_y = appWin.ampMinMax[0]

        max_y = appWin.ampMinMax[1]

    bounds = (min_y, max_y)

    span = max_y - min_y

    """

    plot an eye diagram using matplotlib by creating an image 

    and calling the 'imshow' function.

    """

    counts = grid_count(ipy, 2*samples_per_symbol, skip, bounds)

    counts = counts.astype(np.float32)

    counts[counts==0] = np.nan

    

    appWin.fig.clf()

    appWin.verticalLayout.removeWidget(appWin.canvas)

    appWin.canvas = FigureCanvas(appWin.fig)

    appWin.verticalLayout.addWidget(appWin.canvas)

    

    plt.imshow(counts.T[::-1, :],aspect='auto',

                extent=[0, 2*appWin.ui, min_y, max_y],

                cmap=plt.cm.RdYlGn)

    plt.grid(color='#929591', linestyle=':')

    #if colorbar:

    #   cb = plt.colorbar()

    #   cb.set_ticks([])                  # tick 제거

    

    plt.xlabel(appWin.matrix[0][0])

    plt.ylabel(appWin.matrix[0][appWin.index])

    #file_name = matrix[0][j] + '.png'

    #plt.savefig(file_name)

    #plt.show()

    

    # ------------ eye width 찾기 ----------------------------------------------

    if appWin.vh == None:

        vh = mid

        appWin.vh =mid

        appWin.lineEditVH.setText(format(mid, unity))

    else:

        vh = appWin.vh

    if appWin.vl == None:

        vl = mid

        appWin.vl =mid

        appWin.lineEditVL.setText(format(mid, unity))

    else:

        vl = appWin.vl

    vhi = int(639 * (vh - min_y)/span)

    row_data_h = np.nan_to_num(counts[0:800, vhi])

    vli = int(639 * (vl - min_y)/span)

    row_data_l = np.nan_to_num(counts[0:800, vli])

    zeros_h = 0

    for i in range(400, 800):

        if row_data_h[i] == 0:

            zeros_h = zeros_h + 1

        else:

            break

    zeros_l = 0

    for i in range(400, 800):

        if row_data_l[i] == 0:

            zeros_l = zeros_l + 1

        else:

            break

    if zeros_h > zeros_l:

        zeros_right = zeros_l

    else:

        zeros_right = zeros_h

    zeros_h = 0

    for i in range(400, 0, -1):

        if row_data_h[i] == 0:

            zeros_h = zeros_h + 1

        else:

            break

    zeros_l = 0

    for i in range(400, 0, -1):

        if row_data_l[i] == 0:

            zeros_l = zeros_l + 1

        else:

            break

    if zeros_h > zeros_l:

        zeros_left = zeros_l

    else:

        zeros_left = zeros_h    

    zeros = zeros_right + zeros_left

    

    eye_width = zeros * (appWin.ui * 2) / 800

    appWin.lineEditWidth.setText(format(eye_width, unitx))

    appWin.lineEditWidth2.setText(format(100*eye_width/appWin.ui, '.1f')+'%')


    # -------------- eye height 찾기 -------------------------------------------

    col_data = np.nan_to_num(counts[400+int((zeros_right-zeros_left)/2)])

    sp = int(639 * (low-min_y)/span)

    ep = int(639 * (high-min_y)/span)

    if ep > 639:

        ep = 639

    cp = int((sp+ep)/2)

    zeros = 0

    for i in range(cp, ep):

        if col_data[i] == 0:

            zeros = zeros + 1

        else:

            break

    for i in range(cp, sp, -1):

        if col_data[i] == 0:

            zeros = zeros + 1

        else:

            break

    eye_height =zeros * span / 640

    appWin.lineEditHeight.setText(format(eye_height, unity))

    appWin.lineEditHeight2.setText(format(100*eye_height/amp, '.1f') + '%')

    

    # -------------- VH, VL 활성화 결정

    if eye_height > 0:

        appWin.vh = vh

        appWin.vl = vl

        appWin.lineEditVH.setText(format(vh, unity))

        appWin.lineEditVL.setText(format(vl, unity))

        appWin.lineEditVH.setEnabled(True)

        appWin.lineEditVL.setEnabled(True)

    else:

        appWin.vh = None

        appWin.vl = None

        appWin.lineEditVH.setText('')

        appWin.lineEditVL.setText('')

        appWin.lineEditVH.setEnabled(False)

        appWin.lineEditVL.setEnabled(False)



def is_number(num):

    try:

        float(num)

        return True                         # num을 float으로 변환할 수 있는 경우

    except ValueError:

        return False                        # num을 float으로 변환할 수 없는 경우

    


form_class = uic.loadUiType("eyediagram_viewer.ui")[0]


class ApplicationWindow(QMainWindow, form_class):

    def __init__(self):

        super().__init__()

        self.setupUi(self)

        

        self.fig = plt.figure(figsize=(10, 7.5), dpi=80)

        self.canvas = FigureCanvas(self.fig)

        self.verticalLayout.addWidget(self.canvas)

        #self.addToolBar(NavigationToolbar(self.canvas, self))

        

        self.offset = 0.0

        self.ampMinMax = (0,0)

        self.ui = 1.0

        self.matrix = []

        self.x = []

        self.y = []

        self.index = 1

        self.vh = None

        self.vl = None

        self.unitX = 2

        self.unitY = 2

        

        self.actionOpen.triggered.connect(self.menu_open_checked)

        self.actionExport_to_PNG.triggered.connect(self.menu_export_checked)

        self.comboBox.currentIndexChanged.connect(self.combo_choose)

        self.radioButtonAuto.clicked.connect(self.radio_auto)

        self.radioButtonManual.clicked.connect(self.radio_manual)

        self.lineEditMax.returnPressed.connect(self.edit_max)

        self.lineEditMin.returnPressed.connect(self.edit_min)

        self.lineEditUI.returnPressed.connect(self.edit_ui)

        self.lineEditOffset.returnPressed.connect(self.edit_offset)

        self.lineEditVH.returnPressed.connect(self.edit_vh)

        self.lineEditVL.returnPressed.connect(self.edit_vl)

        self.lineEditUnitX.returnPressed.connect(self.edit_unit_x)

        self.lineEditUnitY.returnPressed.connect(self.edit_unit_y)

        

    def menu_open_checked(self):

        fname = QFileDialog.getOpenFileName(self)

        if fname[0] == '':

            return


        self.matrix = []

        with open(fname[0], 'r') as file:

            reader = csv.reader(file)

            for line in reader:

                self.matrix.append(line)

                

        self.x = []

        try:

            for i in range(1, len(self.matrix), 1):

                newx = float(self.matrix[i][0])

                self.x.append(newx)

                

            self.comboBox.clear()

            for j in range(1, len(self.matrix[0]), 1):

                self.comboBox.addItem(self.matrix[0][j])

                

            self.comboBox.setEnabled(True)          # 파일을 읽었으므로 기능 활성화

            self.radioButtonAuto.setEnabled(True)

            self.radioButtonManual.setEnabled(True)

            self.lineEditUI.setEnabled(True)

            self.lineEditOffset.setEnabled(True)

            self.actionExport_to_PNG.setEnabled(True)

        except ValueError:

            self.statusbar.showMessage('The file is not a eye diagram CSV.')

            

    def menu_export_checked(self):

        file_name = self.matrix[0][self.index] + '.png'

        plt.savefig(file_name)


    def radio_auto(self):

        self.lineEditMin.setEnabled(False)

        self.lineEditMax.setEnabled(False)

        self.ampMinMax = (0, 0)

        read_and_plot(self)

        

    def radio_manual(self):

        self.lineEditMin.setEnabled(True)

        self.lineEditMax.setEnabled(True)

        max = self.lineEditMax.text()

        min = self.lineEditMin.text()        

        self.ampMinMax = (float(min), float(max))

        read_and_plot(self)


    def edit_max(self):

        if is_number(self.lineEditMax.text()):

            self.ampMinMax = (self.ampMinMax[0], float(self.lineEditMax.text()))

            read_and_plot(self)

        else:

            self.lineEditMax.setText(str(self.ampMinMax[1]))

    

    def edit_min(self):

        if is_number(self.lineEditMin.text()):

            self.ampMinMax = (float(self.lineEditMin.text()), self.ampMinMax[1])

            read_and_plot(self)

        else:

            self.lineEditMin.setText(str(self.ampMinMax[0]))

   

    def edit_ui(self):

        if is_number(self.lineEditUI.text()):

            self.ui = float(self.lineEditUI.text())

            read_and_plot(self)

        else:

            self.lineEditUI.setText(str(self.ui))

    

    def edit_offset(self):

        if is_number(self.lineEditOffset.text()):

            self.offset = float(self.lineEditOffset.text())

            read_and_plot(self)

        else:

            self.lineEditOffset.setText(str(self.offset))

    

    def edit_vh(self):

        if is_number(self.lineEditVH.text()):

            self.vh = float(self.lineEditVH.text())

            read_and_plot(self)

        else:

            self.vh = None

            self.lineEditVH.setText('')

            

    def edit_vl(self):

        if is_number(self.lineEditVL.text()):

            self.vl = float(self.lineEditVL.text())

            read_and_plot(self)

        else:

            self.vl = None

            self.lineEditVL.setText('')

            

    def edit_unit_x(self):

        if is_number(self.lineEditUnitX.text()) and int(self.lineEditUnitX.text()) >= 0:

            self.unitX = int(self.lineEditUnitX.text())

            read_and_plot(self)

        else:

            self.lineEditUnitX.setText(str(self.unitX))

            

    def edit_unit_y(self):

        if is_number(self.lineEditUnitY.text()) and int(self.lineEditUnitY.text()) >= 0:

            self.unitY = int(self.lineEditUnitY.text())

            read_and_plot(self)

        else:

            self.lineEditUnitX.setText(str(self.unitY))

        

    def combo_choose(self):

        self.index = self.comboBox.currentIndex() + 1

        if self.index > 0:

            self.y = []

            for i in range(1, len(self.matrix), 1):

                newy = float(self.matrix[i][self.index])

                self.y.append(newy)

            read_and_plot(self)

            


if __name__ == "__main__":

    app = QApplication(sys.argv)

    myWindow = ApplicationWindow()

    myWindow.show()

    app.exec_()


샘플 transient simulation 데이터:

4000mbps_prbs.csv






'도구 > 기타' 카테고리의 다른 글

사양 BOM 생성기 (매트릭스 버전)  (0) 2020.04.05
사양 BOM 생성기  (0) 2020.03.25
Wave 2 Table  (0) 2018.09.03
Spreadsheet Schematic Editor for Allegro  (0) 2016.12.05
패키지/커넥터 핀맵 작성기  (0) 2016.11.21
:

Wave 2 Table

도구/기타 2018. 9. 3. 22:00

웨이브폼을 테이블(CSV) 형태의 파일로 만들거나 PWL(Piece Wise Linear) 형태의 파일로 만들어주는 도구를 만들어 보았다. PWL 파일은 SPICE에서 소스 입력 파일로 사용될 수 있다.


Wave2Table.exe

(요구사항: JRE)




'도구 > 기타' 카테고리의 다른 글

사양 BOM 생성기  (0) 2020.03.25
Eye Diagram Viewer - 파이썬 소스  (0) 2018.09.16
Spreadsheet Schematic Editor for Allegro  (0) 2016.12.05
패키지/커넥터 핀맵 작성기  (0) 2016.11.21
누적 공차 계산기  (0) 2016.07.03
:

Spreadsheet Schematic Editor for Allegro

도구/기타 2016. 12. 5. 21:44

Allegro PCB Editor에서 PCB 디자인을 하려면 Orcad나 Allegro HDL 같은 Schematic Editor가  필요하다. Allegro는 회로를 물리적으로 만들어 주는 툴이고 자체적으로는 회로를 만들지 못하므로 회로 작성 툴에서 만든 회로를 import 받아야만 한다. 앞 서 언급한 툴들을 이용해서 회로도를 그린 후에 netlist를 packing하여 export한다. 그 다음, Allegro PCB Editor에서 패키징된 넷리스트를 import하여 회로를 입력 받는다. 그러면 이제 물리적으로 PCB를 디자인할 준비가 된 것이다.

간단한 회로나 테이블화된 값을 자주 사용하는 회로의 경우, 앞 서 언급한 회로도 작성 툴을 사용하지 않고 스프레트시트에서 간단하게 작성 후 바로 알레그로에서 사용할 수 있도록 넷리스트를 작성할 수 있는 기능을 엑셀에서 구현해 보았다.

알레그로는 넷리스트를 import할 때 넷리스트 패지징 정보가 담긴 폴더를 입력으로 받는다. ALLegro HDL에서는 기본적으로 packaged라는 폴더를 사용하고 OrCAD는 allegro라는 폴더 명을 사용한다. 폴더 명은 사용자가 임의로 수정할 수 있다. 여기서는 기본 값으로 allegro 폴더를 사용한다.

엑셀은 크게 3개의 시트 범주를 갖는다. libarry로 시작하는 시트, design으로 시작하는 시트, export 시트 3개 이다. 각각의 입력 포맷은 해당 시트에 주석으로 표시해 놓았다. 주석(회색)은 삭제하면 안되며, 포맷은 반드시 준수해야 한다.

Export 버튼을 누르면 allegro 폴더가 생성되면서 넷리스트가 만들어진다. 에러가 있을 경우, 넷리스트 작성에 실패하며, 어떤 부분에 에러가 있는지 결과를 보고 수정한다.

에러 없이 넷리스트가 만들어지면 allegro 폴더 아래 3개의 파일이 생성된다.

폴더 경로를 기억해 두고, Allegro PCB Editor를 수행해서 넷리스트를 import한다. import할 때 위 폴더를 선택한다.

넷리스트 import가 끝나면 정상적으로 회로가 올라온 것을 확인할 수 있다.

 

TableSchematic_V1.0.xlsm

 

'도구 > 기타' 카테고리의 다른 글

Eye Diagram Viewer - 파이썬 소스  (0) 2018.09.16
Wave 2 Table  (0) 2018.09.03
패키지/커넥터 핀맵 작성기  (0) 2016.11.21
누적 공차 계산기  (0) 2016.07.03
몬테카를로 시뮬레이션  (0) 2016.07.02
:

패키지/커넥터 핀맵 작성기

도구/기타 2016. 11. 21. 22:00

다수의 핀을 갖고 있는 패키지나 커넥터의 핀 정보가 아래와 같은 형태로 제공되는 경우가 많다. 아래 예는 LPDDR4 패키지 샘플이다.

그런데, 심볼을 제작하려면, 핀 번호 -핀 이름 짝이 2 열의 형태로 정렬되어 있어야 편리하다.

패키지·커넥터 핀맵 작성기는 이런 작업을 빠르게 자동으로 수행해 준다.


패키지&middot;커넥터 핀맵 작성기.xlsm


아래는 자동으로 작성된 핀맵의 예이다. 왼쪽은 행 번호 0, 번호 순서 ColRow로 추출한 것이고, 가운데는 행 번호 00, 번호 순서 ColRow로 추출한 것이고, 오른쪽은 행 번호 00, 번호순서 RowCol로 추출한 것이다.

아래에서 왼쪽은 빈 셀 처리 Reserve를 선택한 것이고, 오른쪽은  빈 셀 처리 Skip을 선택한 것이다.


'도구 > 기타' 카테고리의 다른 글

Wave 2 Table  (0) 2018.09.03
Spreadsheet Schematic Editor for Allegro  (0) 2016.12.05
누적 공차 계산기  (0) 2016.07.03
몬테카를로 시뮬레이션  (0) 2016.07.02
Package parasitic SPICE model extractor from IBIS file  (0) 2015.08.05
: