matplotlib 사용

개발/Note 2022. 11. 18. 13:44
반응형

다음 코드는 matplotlib를 사용한 그래프 출력 샘플이다.

import numpy as np
import matplotlib.pyplot as plt
'''
x = np.arange(0,5)
y = x ** 2 
'''

#x= np.array([1,2,3,4,5])
x = np.arange( -5, 5, 0.1)

y1 = np.exp(x)
y2 = pow(np.exp(x), 2)
y3 = pow((1+np.exp(x)), 2)
y4 = np.exp(x) / pow((1+np.exp(x)), 2)


plt.subplot(2, 2, 1)                # nrows=2, ncols=1, index=1
#포맷 문자열 ‘ro’는 빨간색 (‘red’)의 원형 (‘o’) 마커를 의미합니다. 
# '--'은 점선을 의미. (ex. ro, o-, .-, bs, g^, r--,)
plt.plot(x,y1, 'r-')#plt.plot(x1, y1, 'r--', x2, y2, 'bs', x3, y3, 'g^')
plt.title('np.exp(x)')
plt.axis([-5, 5, 0, 5])# xmin, xmax, ymin, ymax
plt.xlabel('x')
plt.ylabel('y')

plt.subplot(2, 2, 2)                # nrows=2, ncols=1, index=2
#포맷 문자열 ‘b-‘는 파란색 (‘blue’)의 실선 (‘-‘)을 의미합니다.
plt.plot(x,y2, 'b-')
plt.title('pow(np.exp(x), 2)')
plt.axis([-5, 5, 0, 5])# xmin, xmax, ymin, ymax
plt.xlabel('x')
plt.ylabel('y')

plt.subplot(2, 2, 3)                # nrows=2, ncols=1, index=2
#포맷 문자열 ‘b-‘는 파란색 (‘blue’)의 실선 (‘-‘)을 의미합니다.
plt.plot(x,y3, 'b-')
plt.title('pow((1+np.exp(x)), 2)')
plt.axis([-5, 5, 0, 5])# xmin, xmax, ymin, ymax
plt.xlabel('x')
plt.ylabel('y')

plt.subplot(2, 2, 4)                # nrows=2, ncols=1, index=2
#포맷 문자열 ‘b-‘는 파란색 (‘blue’)의 실선 (‘-‘)을 의미합니다.
plt.plot(x,y4, 'b-')
plt.title('np.exp(x) / pow((1+np.exp(x)), 2)')
plt.axis([-5, 5, 0, 5])# xmin, xmax, ymin, ymax
plt.xlabel('x')
plt.ylabel('y')




fig1 = plt.gcf()
plt.tight_layout()
plt.show()
plt.draw()
#dpi default=100
fig1.savefig('test.png', dpi=200, facecolor='#eeeeee', edgecolor='blue')

 

반응형

'개발 > Note' 카테고리의 다른 글

Unwind Segue 사용방법  (0) 2023.01.11
랜덤 seed 초기화  (0) 2022.11.18
git 소스코드 비교  (0) 2022.02.23
20진수 변환  (0) 2021.09.16
root-level 디렉토리에 폴더, symbolic link 생성 방법  (0) 2021.03.15
블로그 이미지

SKY STORY

,
반응형

python에서 신경망 xor 연산 학습 샘플을 만들어 보았다.

# neural network xor


import math
import random
from datetime import datetime

import matplotlib.pyplot as plt
import numpy as np

# 네트워크의 학습 속도
LEARNING_RATE = 1.414213562

MOMENTUM = 0.25

# 훈련 데이타
TRAINING_DATA = [
    [1.0, 0.0],
    [1.0, 1.0],
    [0.0, 1.0],
    [0.0, 0.0]
]

# 타겟 값
TARGET_DATA = [
    1.0, 
    0.0, 
    1.0, 
    0.0
]

MAX_LAYERS = 4
MAX_WEIGHTS = 9
MAX_EPOCHS = 10000 

# 가중치
weights = [0. for i in range(MAX_WEIGHTS)] # [0] * 9
gradients = [0. for i in range(MAX_WEIGHTS)] # [0] * 9
error = [0. for i in range(MAX_LAYERS)] # [0] * 4  #error = np.zeros(4, dtype='f')

update_weights = [0. for i in range(MAX_WEIGHTS)] # [0] * 9
prev_weight_update = [0. for i in range(MAX_WEIGHTS)] # [0] * 9
rmse_array_error = [0. for i in range(MAX_EPOCHS)] # [0] * 10000  # 각 세대별 RMSE 에러 값

bias1 = 1.0
bias2 = 1.0
bias3 = 1.0
h1 = 0.0
h2 = 0.0
output_neuron = 0.0
derivative_O1 = 0.0
derivative_h1 = 0.0
derivative_h2 = 0.0
sum_output = 0.0
sum_h1 = 0.0
sum_h2 = 0.0


# sum_output
graph_sum_output = [[0. for i in range(MAX_EPOCHS)] for j in range(MAX_LAYERS)]

# 그래프 출력을 위한 버퍼 
graph_derivative_O1 = [[0. for i in range(MAX_EPOCHS)] for j in range(MAX_LAYERS)]
graph_derivative_h1 = [[0. for i in range(MAX_EPOCHS)] for j in range(MAX_LAYERS)]
graph_derivative_h2 = [[0. for i in range(MAX_EPOCHS)] for j in range(MAX_LAYERS)]



'''
                bias1   bias3
                  |      |
                  w4     w8
                  |      |
 input1 --w0 --- h1      |
        \       /  \     |
         \     /    w6   |
          w1  /      \   |
            \/         output
            /\        /
          w2  \      w7
          /    \    /
         /      \  /
 input2 --w3----- h2
                  |
                  w5
                  |
                bias2
'''


def sigmoid_function(x) :
    sigmoid = 1.0 / ( 1.0 + np.exp(-x) )
    return sigmoid

# 변수 초기화 
def initialize():
    global prev_weight_update, update_weights, gradients, error, rmse_array_error
    for i in range(MAX_WEIGHTS):
        prev_weight_update[i] = 0.0
        update_weights[i] = 0.0
        gradients[i] = 0.0
    for i in range(MAX_LAYERS):
        error[i] = 0.0
    for i in range(MAX_EPOCHS):
        rmse_array_error[i] = 0.0

def generate_weights() :
    global weights
    for i in range(MAX_WEIGHTS):
        weights[i] = random.uniform(-1.0, 1.0)
        print("weight[",i,"] = ", weights[i])

def train_neural_net() :
    global rmse_array_error, error
    # 세대(학습 횟수)
    epoch = 0
    while epoch < MAX_EPOCHS :
        for i in range(MAX_LAYERS):
            calc_hidden_layers(i)
            calc_output_neuron()
            graph_sum_output[i][epoch] = sum_output
            calc_error(i)
            calc_derivatives(i)
            graph_derivative_O1[i][epoch] = derivative_O1
            graph_derivative_h1[i][epoch] = derivative_h1
            graph_derivative_h2[i][epoch] = derivative_h2
            calc_gradients(i)
            calc_updates()

        # RMSE 에러 값
        sum = math.pow(error[0], 2) + math.pow(error[1], 2) + math.pow(error[2], 2) + math.pow(error[3], 2)
        rmse_error = math.sqrt( sum / MAX_LAYERS )
        print("RMSE error: ", rmse_error)
        rmse_array_error[epoch] = rmse_error

        # 세대        
        epoch = epoch + 1
        print("epoch:", epoch)

        
        # 예외 처리 - 처음부터 다시 
        if epoch > 4000 and rmse_error > 0.3:
            epoch = 0
            initialize()
            generate_weights()


def calc_hidden_layers(x):
    global sum_h1, sum_h2, h1, h2, weights, bias1, bias2
    sum_h1 = (TRAINING_DATA[x][0] * weights[0]) + (TRAINING_DATA[x][1] * weights[2]) + (bias1 * weights[4])
    sum_h2 = (TRAINING_DATA[x][0] * weights[1]) + (TRAINING_DATA[x][1] * weights[3]) + (bias2 * weights[5])
    h1 = sigmoid_function(sum_h1)
    h2 = sigmoid_function(sum_h2)

def calc_output_neuron():
    global sum_output, h1, h2, weights, bias3, output_neuron
    sum_output = (h1 * weights[6]) + (h2 * weights[7]) + (bias3 * weights[8])
    output_neuron = sigmoid_function(sum_output)

def calc_error(x):
    global error, output_neuron
    error[x] = output_neuron - TARGET_DATA[x]

def calc_derivatives(x):
    global derivative_O1, derivative_h1, derivative_h2, sum_h1, sum_h2, weights, sum_output, error
    derivative_O1 = -error[x] * ( np.exp(sum_output) / math.pow((1 + np.exp(sum_output)), 2) )
    derivative_h1 = ( np.exp(sum_h1) / math.pow((1 + np.exp(sum_h1)), 2) ) * weights[6] * derivative_O1
    derivative_h2 = ( np.exp(sum_h2) / math.pow((1 + np.exp(sum_h2)), 2) ) * weights[7] * derivative_O1

def calc_gradients(x):
    global gradients, derivative_O1, derivative_h1, derivative_h2, h1, h2, bias1, bias2,bias3
    gradients[0] = sigmoid_function(TRAINING_DATA[x][0]) * derivative_h1
    gradients[1] = sigmoid_function(TRAINING_DATA[x][0]) * derivative_h2
    gradients[2] = sigmoid_function(TRAINING_DATA[x][1]) * derivative_h1
    gradients[3] = sigmoid_function(TRAINING_DATA[x][1]) * derivative_h2
    gradients[4] = sigmoid_function(bias1) * derivative_h1
    gradients[5] = sigmoid_function(bias2) * derivative_h2
    gradients[6] = h1 * derivative_O1
    gradients[7] = h2 * derivative_O1
    gradients[8] = sigmoid_function(bias3) * derivative_O1

def calc_updates():
    global update_weights, gradients, prev_weight_update
    for i in range(MAX_WEIGHTS):
        update_weights[i] = (LEARNING_RATE * gradients[i]) + (MOMENTUM * prev_weight_update[i])
        prev_weight_update[i] = update_weights[i]
        weights[i] = weights[i] + update_weights[i]


def save_data():
    global rmse_array_error, weights
    with open('errorData1.txt', 'w', encoding='utf-8') as f:
        for i in range(MAX_EPOCHS):
            line = '%d   %s' % (i, rmse_array_error[i])
            f.write(line) # f.write('\n'.join(lines))
            f.write('\n') # f.writelines('\n')
    
    with open('weight_data1.txt', 'w', encoding='utf-8') as f:
        for i in range(MAX_WEIGHTS):
            line = '%d   %s' % (i, weights[i])
            f.write(line)
            f.write('\n')

def start_input():
    global weights, sum_h1, sum_h2, h1, h2, bias1, bias2, bias3, sum_output, output_neuron

    choice = 'y'
    while True:
        if choice == 'Y' or choice == 'y':
            data1 = input("enter data 1: ")
            if data1.isnumeric() == False :
                continue
            data2 = input("enter data 2: ")
            if data2.isnumeric() == False :
                continue

            sum_h1 = (float(data1) * weights[0]) + (float(data2) * weights[2]) + (bias1 * weights[4])
            sum_h2 = (float(data1) * weights[1]) + (float(data2) * weights[3]) + (bias2 * weights[5])
            h1 = sigmoid_function(sum_h1)
            h2 = sigmoid_function(sum_h2)
            
            sum_output = (h1 * weights[6]) + (h2 * weights[7]) + (bias3 * weights[8])
            output_neuron = sigmoid_function(sum_output)
            print('output = ', output_neuron)

            choice = input('Again? (y/n) : ')
        else:
            print('exit')
            break




initialize()
generate_weights()
train_neural_net()

save_data()
start_input()







# ===============================================================

# sigmoid
x = np.arange( -10.0, 10.0, 0.1)
y = 1.0 / (1.0 + np.exp(-x))
plt.subplot(2, 3, 1)                
plt.plot(x,y, 'r-')#plt.plot(x1, y1, 'r--', x2, y2, 'bs', x3, y3, 'g^')
plt.title('sigmoid')# 1.0 / (1.0 + np.exp(-x))
plt.axis([-10.0, 10.0, 0, 1.0])# xmin, xmax, ymin, ymax
plt.xlabel('x')
plt.ylabel('y')

# sum_output
x2 = np.arange( 0.0, 10000.0, 1.0)
y2 = graph_sum_output[0]
plt.subplot(2, 3, 2)               
plt.plot(x2,y2, 'r-')#plt.plot(x1, y1, 'r--', x2, y2, 'bs', x3, y3, 'g^')
plt.title('sum_output')
plt.axis([0.0, 10000.0, -0.2, 5.0])# xmin, xmax, ymin, ymax
plt.xlabel('epoch')
plt.ylabel('sum_output')

# rmse_array_error
x2 = np.arange( 0.0, 10000.0, 1.0)
y2 = rmse_array_error
plt.subplot(2, 3, 3)               
plt.plot(x2,y2, 'r-')#plt.plot(x1, y1, 'r--', x2, y2, 'bs', x3, y3, 'g^')
plt.title('RMSE Error')
plt.axis([0.0, 10000.0, 0.0, 1.0])# xmin, xmax, ymin, ymax
plt.xlabel('epoch')
plt.ylabel('RMSE error')


# derivative_O1
x3 = np.arange( 0.0, 10000.0, 1.0)
y3 = graph_derivative_O1[0] 
plt.subplot(2, 3, 4)              
plt.plot(x3,y3, 'r-')#plt.plot(x1, y1, 'r--', x2, y2, 'bs', x3, y3, 'g^')
plt.title('derivative_O1')
plt.axis([0.0, 10000.0, -0.05, 0.15])# xmin, xmax, ymin, ymax
plt.xlabel('epoch')
plt.ylabel('derivative_O1')

# derivative_h1
x4 = np.arange( 0.0, 10000.0, 1.0)
y4 = graph_derivative_h1[0]
plt.subplot(2, 3, 5)                
plt.plot(x4,y4, 'r-')#plt.plot(x1, y1, 'r--', x2, y2, 'bs', x3, y3, 'g^')
plt.title('derivative_h1')
plt.axis([0.0, 10000.0, -0.05, 0.15])# xmin, xmax, ymin, ymax
plt.xlabel('epoch')
plt.ylabel('derivative_h1')

# derivative_h2
x5 = np.arange( 0.0, 10000.0, 1.0)
y5 = graph_derivative_h2[0]
plt.subplot(2, 3, 6)                
plt.plot(x5,y5, 'r-')#plt.plot(x1, y1, 'r--', x2, y2, 'bs', x3, y3, 'g^')
plt.title('derivative_h2')
plt.axis([0.0, 10000.0, -0.05,0.15])# xmin, xmax, ymin, ymax
plt.xlabel('epoch')
plt.ylabel('derivative_h2')


fig1 = plt.gcf()
plt.tight_layout()
plt.show()
plt.draw()
fig1.savefig('nn_xor_derivative.png', dpi=200, facecolor='#eeeeee', edgecolor='blue')


결과 그래프는 다음과 같다.


#----------------------------------------------------------------
weight_data1.txt

0 -4.749828824761969
1 -3.5287652884908254
2 -4.747508485076842
3 -3.5276048615858935
4 1.816578868741784
5 5.375092217625854
6 -13.742230835984616
7 13.712746773928748
8 -6.530714643755955



#----------------------------------------------------------------
errorData1.txt

0 0.5424391889837473
1 0.5331220459189032
2 0.5304319567906218

10 0.5274519675428608

100 0.5198658296518869

200 0.5155776076419263

300 0.5046587144670703

400 0.4791696883760997

1000 0.09849463249517668

2000 0.03296842320700676

3000 0.02307589090605272

9999 0.010292985691909057



반응형

'개발 > AI,ML,ALGORITHM' 카테고리의 다른 글

MNIST 데이터셋을 이미지 파일로 복원  (0) 2023.07.19
MNIST 데이터셋 다운로드  (0) 2023.07.19
2D 충돌처리  (0) 2020.12.12
Generic algorithm  (0) 2020.05.19
neural network  (0) 2020.05.19
블로그 이미지

SKY STORY

,
반응형

NSURLSessionTask 를 사용하여 파일을 로딩할 경우
최초 1회 이후 캐싱되어 웹에서 json파일을 변경해도 반영되지 않는다.
그래서 다음과같이 캐싱을 비활성화 하여 파일 변경사항이 즉시 반영되도록 하였다.

// 테스트용 json파일 다운로드
let urlString = "https://www.test.com/data/info.json"

// NSURLSessionTask 캐싱 비활성화
let config = URLSessionConfiguration.default
config.requestCachePolicy = .reloadIgnoringLocalCacheData
config.urlCache = nil
let session = URLSession(configuration: config)

session.dataTask(with: URL(string: urlString)!) { (data, response, error) -> Void in
    if error == nil && data != nil {
        do {
            let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String: Any]
            print("\(json)")
	
        } catch {
            print("Something went wrong")
        }
    }
}.resume()

 

반응형
블로그 이미지

SKY STORY

,
반응형

네이티브 UIScrollView 화면에서 스크린샷 구하는 방법에 대해 알아보자.

일반적으로 스크린샷을 구해보면 화면에 보이는 부분만

구해지며 숨겨진 영역은 흰색화면으로 출력된다. (iOS 13.0이상 에서 생기는 버그)

extension UIScrollView {
    func screenshot() -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(contentSize, false, 0.0)
        let savedContentOffset = contentOffset
        let savedFrame = frame
        defer {
            UIGraphicsEndImageContext()
            contentOffset = savedContentOffset
            frame = savedFrame
        }
        contentOffset = .zero
        frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
        guard let ctx = UIGraphicsGetCurrentContext() else {
            return nil
        }
        layer.render(in: ctx)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        return image
    }
}

이를 위해 UIScrollView를 부모로 부터 Layout 컨텐츠를 비활성하고 스크린샷 이미지를 구하고 다시

원래대로 활성화 시켜주는 방법으로 해결 가능하다.

extension UIScrollView {
    func screenshot() -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(contentSize, false, 0.0)
        let savedContentOffset = contentOffset
        let actualConstraints = relatedConstraints()
        NSLayoutConstraint.deactivate(actualConstraints)
        translatesAutoresizingMaskIntoConstraints = true
        frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
        contentOffset = .zero
        defer {
            UIGraphicsEndImageContext()
            translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate(actualConstraints)
            superview?.layoutIfNeeded()
            contentOffset = savedContentOffset
        }
        guard let ctx = UIGraphicsGetCurrentContext() else {
            return nil
        }
        layer.render(in: ctx)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        return image
    }
}


extension UIView {
    func relatedConstraints() -> [NSLayoutConstraint] {
        var constraints = self.constraints
        var parent = superview
        while parent != nil {
            constraints.append(contentsOf: parent!.constraints.filter { $0.firstItem === self || $0.secondItem === self })
            parent = parent!.superview
        }
        return constraints
    }
    
    class func getAllSubviews<T: UIView>(from parenView: UIView) -> [T] {
        return parenView.subviews.flatMap { subView -> [T] in
            var result = getAllSubviews(from: subView) as [T]
            if let view = subView as? T { result.append(view) }
            return result
        }
    }
	
    class func getAllSubviews(from parenView: UIView, types: [UIView.Type]) -> [UIView] {
        return parenView.subviews.flatMap { subView -> [UIView] in
            var result = getAllSubviews(from: subView) as [UIView]
            for type in types {
                if subView.classForCoder == type {
                    result.append(subView)
                    return result
                }
            }
            return result
        }
    }
	
    func getAllSubviews<T: UIView>() -> [T] { return UIView.getAllSubviews(from: self) as [T] }
    func get<T: UIView>(all type: T.Type) -> [T] { return UIView.getAllSubviews(from: self) as [T] }
    func get(all types: [UIView.Type]) -> [UIView] { return UIView.getAllSubviews(from: self, types: types) }
}


/*
// 현재 뷰의 하위뷰 중 UIScrollView를 구한다.
if let scrollView = self.view.get(all: UIScrollView.self).first {
    // 스크롤뷰의 모든 컨텐츠가 나오도록 스크린샷 이미지로 반환한다.
    let image = scrollView.screenshot()
    
    // 주어진 이미지를 공유
    DispatchQueue.main.async {
        let imageToShare = [ image ]
        let activityVC = UIActivityViewController(activityItems: imageToShare as [Any], applicationActivities: nil)
        self.present(activityVC, animated: true, completion: nil)
    }
}
*/

 

반응형
블로그 이미지

SKY STORY

,
반응형

문자열 숫자에 3자리마다 콤마를 넣을 때 사용할 함수를 알아보자

extension String {
    var commas: String {
        guard self.isEmpty == false else { return "" }
        // 이미 ','가 존재하면 제거 
        let str = replacingOccurrences(of: ",", with: "", options: .literal, range: nil)
        
        let numFmt = NumberFormatter()
        numFmt.numberStyle = .decimal
        // 포멧 적용 소수점 이하 자릿수
        // 소수점 이하 최대 7자리까지만 적용되며 반올림 된다.
        numFmt.maximumFractionDigits = 7
        // 현재 문자열 숫자를 NSNumber객체로 변환
        let num = numFmt.number(from: str)
        // 3자릿수마다 콤마 삽입
        return numFmt.string(for: num) ?? str
    }
}

/*
let s = "1234567890.123456789".commas
print("\(s)")

let s2 = "1234567890".commas
print("\(s2)")

결과 : 
1,234,567,890.1234567
1,234,567,890
*/

소수점 이하 7자리까지만 지원된다는 문제가 있지만 일반적으로 사용하는데 문제없을 듯하다. 

maximumFractionDigits 참조 :

https://developer.apple.com/documentation/foundation/numberformatter/1415364-maximumfractiondigits

 

만약 소수점 이하 자릿수 제한 없이 구현해야 한다면 다음 함수를 이용하자.

extension String {
	var commas : String {
        guard self.isEmpty == false else { return "" }
        let str = replacingOccurrences(of: ",", with: "", options: .literal, range: nil)
        // 소수점을 기준으로 토큰 분리
        let strArr = str.components(separatedBy: ".")
        guard strArr.count > 0 else { return self }
        guard strArr[0].isNumber else { return self }
        let numFmt = NumberFormatter()
        numFmt.numberStyle = .decimal
        let num = numFmt.number(from: strArr[0])
        var numStr = numFmt.string(from: num) ?? strArr[0]
        if strArr[1].isEmpty == false {
            numStr.append(".\(strArr[1])")
        }
        return numStr
    }
}

/*
let s = "1234567890.123456789".commas
print("\(s)")

결과 : 
1,234,567,890.123456789
*/

소수점 이하 자릿수 제한 없이 사용 가능하다.

 

반응형
블로그 이미지

SKY STORY

,
반응형

유니버셜링크 적용 시 도메인 루트에 AASA(apple-app-site-association)파일을 생성하여
관련정보를 설정해야 한다. 개발 중에 주의할 점은 이 파일을 다운받게 되면 캐싱되어
그 이후 계속적으로 캐싱된 파일이 사용되므로 중간에 내용을 바꾸어도 적용되지 않는다.
이 상황을 모르고 작업하다보면 원인파악에 많은 시간을 낭비할 수 있으므로 다음 내용을 숙지하기 바란다.

AASA파일을 다운받아 캐싱되는 경우
1. 앱 최초 설치 시 AASA파일 인스톨할 경우 (앱스토어 다운로드)
2. 앱 스토어 업데이트로 인한 인스톨 (앱 업데이트)

위 두가지 이외는 더 이상 다운로드 되지 않을 수 있으므로 중간에 해당 파일을 수정했을 경우
정상동작하지 않고 이전 파일(캐싱된 파일)로 적용된다.
또한 AASA파일을 다운로드가 안될경우(방화벽 또는 도메인 문제 등.) 역시 정상 동작되지 않으므로
해당 파일 다운로드가 가능한지 체크한다.
개발중 AASA파일 갱신으로 인하여 캐싱된 파일을 강제 업데이트 할 경우 다음과정을 통해 업데이트하길 바란다.

1. 디바이스에서 앱 제거
2. 디바이스 재시작
3. 앱 재설치
4. 1~3번을 2~3번 재시도
5. 앱 내에서 AASA파일이 다운로드 성공인지 확인.

AASA 파일 다운로드 성공 확인 방법 :
- 휴대폰의 노트앱 실행
- 링크를 노트에 붙여넣기
- 해당 링크를 2초이상 누르고 있으면 드롭 다운 메뉴가 뜨는데 
  메뉴리스트에 '링크 열기' 표시되지 않고 '[앱이름] 으로 열기'가 
  표시되면 성공이다.
  
  ex) AASA 파일 링크
  https://myapp.page.link/apple-app-site-association

  ex) AASA 파일 내용 
  {"applinks":{"apps":[],"details":[{"appID":"ABCDEF2TH3.com.company.myapp","paths":["NOT /_/*","/*"]}]}}



반응형

'개발 > iOS' 카테고리의 다른 글

UIScrollView 스크린샷 만들기  (0) 2022.10.24
문자열 숫자에 콤마 넣기  (0) 2022.10.14
웹 저장 데이터 (Cookie, Cache) 삭제  (0) 2021.09.24
버전분기 샘플  (0) 2021.08.02
시뮬레이터 분기  (0) 2021.08.02
블로그 이미지

SKY STORY

,
반응형

현재 작업중인 소스코드가 git에 마지막으로 commit된 소스코드와

비교를 해야할 경우가 종종 발생한다.

이럴경우 이전 commit코드와 비교하여 추가,삭제 및 수정된 부분을

알 수 있도록 보여주는 기능(Code Review)을 사용하도록 하자. 

 

View / Show Code Review

Shift + Option + Command + Enter

 

'Code Review' 가 활성화되면 Xcode 우상단 아이콘(1번)이 아래 그림과 같이 변경된다.

또한 우측 목록 아이콘을 클릭하여 Inline, Side By Side 비교방법(2번)을 선택할 수 있다.

 

Inline Comparision - 현재 소스코드에서 저장된 코드와의 비교

다음과 같이 변경된 부분은 좌측 라인번호에 파란색 마크가 생긴다.

 

Side By Side Comparision - 현재 소스코드와 저장된 코드를 화면분할 출력하여 비교

다음과 같이 현재 코드와 저장된 코드를 둘다 출력하여 변경된 부분을 비교해 준다.

 

 

반응형

'개발 > Note' 카테고리의 다른 글

랜덤 seed 초기화  (0) 2022.11.18
matplotlib 사용  (0) 2022.11.18
20진수 변환  (0) 2021.09.16
root-level 디렉토리에 폴더, symbolic link 생성 방법  (0) 2021.03.15
RSA 암복호화 테스트  (0) 2021.02.05
블로그 이미지

SKY STORY

,
반응형

정치, 종교 얘기를 하지 말아야 하는 이유  

by netcanis

 

누군가 삼각형을 원이라고 주장하면 어떻게 반응할까?

분명 삼각형인데, 어딜 봐도 삼각형인데 말이다…

당연히 논란이 생길 수 있다.

 

대부분의 사람들은 2차원적으로 사고하며 자신이 알고 있는 것에 대한 강한 믿음을 가지고 있다.

그들은 그것이 틀릴 수 있다는 생각을 하지 않는다.

즉, 삼각형으로 보이면 삼각형이고, 원으로 보이면 원일 뿐이다.

 

예를 들어, 아래의 원뿔(Circular Cone) 그림을 살펴보자.

원뿔

위 도형을 앞이나 뒤에서 바라보면 삼각형으로 보이고, 위나 아래에서 바라보면 원으로 보인다.

2차원적으로 사고하는 사람들에게는 두 가지 모양만 인식된다.

 

따라서 삼각형으로 보는 사람에게 원이라고 말해도, 원으로 보는 사람에게 삼각형이라고 말해도 그들은 절대 믿지 않을 것이다.

왜냐하면 그들이 보는 시각에서는 삼각형이나 원으로만 인식되기 때문이다.

 

만약 당신이 원뿔을 볼 수 있다면, 침묵하는 것이 좋다. 그렇지 않으면 미친 사람으로 몰릴 수 있으니 말이다.

 

반응형
블로그 이미지

SKY STORY

,
반응형

하이브리드 앱을 제작하다 보면 웹페이지에서 저장한 데이터(쿠키, 캐시)를

삭제해야 하는 경우가 생긴다.

하지만 모든 저장 데이터를 삭제하다 보면 다른 웹 페이지에 저장된 데이터까지

모두 삭제되어 예외사항이 발생할 수 있다.

 

특정 페이지에서 저장된 데이터만 삭제하는 코드를 알아보자.

// 다음과 같이 displayName을 이용하여 특정 데이터만 삭제.
WKWebsiteDataStore.default().fetchDataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes()) { records in
	WKWebsiteDataStore.default().removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(),
                                            for: records.filter { $0.displayName.contains("mytest.com") },
                                            completionHandler: {
                                                print("\("delete website data")")
                                            })
}

 

다음은 모든 캐시데이터 및 특정 도메인 쿠키 삭제

// 모든 캐시데이터 삭제
let cacheDataTypes : Set<String> = [WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache]
let date = Date(timeIntervalSince1970: 0)
WKWebsiteDataStore.default().removeData(ofTypes: cacheDataTypes,
                                        modifiedSince: date ,
                                        completionHandler: {
                                            print("delete cache data")
                                        })
                                        
                                        
                                        
// 특정 도메인 쿠키만 삭제
let cookieDataType : Set<String> = [WKWebsiteDataTypeCookies]
WKWebsiteDataStore.default().fetchDataRecords(ofTypes: cookieDataType) { records in
    WKWebsiteDataStore.default().removeData(ofTypes: cookieDataType,
                                            for: records.filter { $0.displayName.contains("mytest.com") },
                                            completionHandler: {
                                                print("delete cookie data")
                                            })
}

 

웹 저장 데이터 로그 출력 

// 쿠키 정보 보기 
WKWebsiteDataStore.default().httpCookieStore.getAllCookies { cookies in
    for cookie in cookies {
        print("cookie : \(cookie)")
    }
}



/* 
출력 로그 :
cookie : <NSHTTPCookie
	version:1
	name:JSESSIONID
	value:43C2101193D26FDE7F2DB61D86E15B0E
	expiresDate:'(null)'
	created:'2021-09-17 00:06:31 +0000'
	sessionOnly:TRUE
	domain:www.mytest.com
	partition:none
	sameSite:none
	path:/
	isSecure:FALSE
	isHTTPOnly: YES
 path:"/" isSecure:FALSE isHTTPOnly: YES>
 */

 

 

 

 

반응형

'개발 > iOS' 카테고리의 다른 글

문자열 숫자에 콤마 넣기  (0) 2022.10.14
유니버셜 링크(Universal Links) 문제점 해결  (0) 2022.07.21
버전분기 샘플  (0) 2021.08.02
시뮬레이터 분기  (0) 2021.08.02
Foreground 이벤트 받기  (0) 2021.08.02
블로그 이미지

SKY STORY

,

20진수 변환

개발/Note 2021. 9. 16. 11:44
반응형

7세그먼트 LED는 아래와 같이 숫자 및 일부 영문자를 표시해 주는 디지털 표시 모듈이다.

7 세그먼트 LED

 

이 모듈에 숫자 이외에 출력 가능한 영문자는 다음과 같다.

A, b, C, d, E, F, H, L, P, U

이 모듈에 표현 가능한 최대 숫자는 10진수로 표현할 경우 0~9 10개의 

숫자(10진수)만 가능하지만 영문까지 사용할 경우 0~9, A~U

총 20개의 숫자/문자(20진수) 표현이 가능해 진다.

펌웨어 프로그래밍 작업을 하다보면 10진수와 20진수 변환해야

하는 경우가 생길 수 있는데 다음과 같이 변환하여 처리해 보았다.

 

// 20진수 문자열을 10진수 자료형 변수로 변환
/*
 좌측부터 10, .... 19
 A, b, C, d, E, F, H, L, P, U
 
 20진수 아스키코드 
 A:65(0x41)
 b:98(0x62)
 C:67(0x43)
 d:100(0x64)
 E:69(0x45)
 F:70(0x46)
 H:72(0x48)
 L:76(0x4C)
 P:80(0x50)
 U:85(0x55)
*/
unsigned long long convert20to10(char* str) {
    unsigned long long result = 0;
    unsigned long len = strlen(str);
    for (unsigned long i = 0; i < len; i++ ) {
        if ( str[i] >= 'A' && str[i] <= 'F' ) {         // A(65) ~ F(70)
            result = result * 20 + str[i] - 'A' + 10;
        } else if ( str[i] >= 'a' && str[i] <= 'f' ) {  // a(97) ~ f(102)
            result = result * 20 + str[i] - 'a' + 10;
        } else if ( str[i] >= '0' && str[i] <= '9' ) {  // 0(48) ~ 9(57)
            result = result * 20 + str[i] - '0';
        }
        else if (str[i] == 'H' || str[i] == 'h' ) { result = result * 20 + 16; }// H(72),h(104)
        else if (str[i] == 'L' || str[i] == 'l' ) { result = result * 20 + 17; }// L(76),l(108)
        else if (str[i] == 'P' || str[i] == 'p' ) { result = result * 20 + 18; }// P(80),p(112)
        else if (str[i] == 'U' || str[i] == 'u' ) { result = result * 20 + 19; }// U(85),u(117)
    }
    return result;
}


// 10진수 자료형 변수 값을 20진수 문자열로 변환
void convert10to20(unsigned long long decimal, char *result) {
    char szTemp[256] = {0,};
    unsigned long pos = 0;
    while(true) {
        unsigned long mod = decimal % 20;
        if (mod < 10) { // 0 ~ 9
            szTemp[pos] = 48 + mod;
        } else { //  A, b, C, d, E, F, H, L, P, U
            switch (mod) {
                case 10: szTemp[pos] = 'A'; break;
                case 11: szTemp[pos] = 'b'; break;
                case 12: szTemp[pos] = 'C'; break;
                case 13: szTemp[pos] = 'd'; break;
                case 14: szTemp[pos] = 'E'; break;
                case 15: szTemp[pos] = 'F'; break;
                case 16: szTemp[pos] = 'H'; break;
                case 17: szTemp[pos] = 'L'; break;
                case 18: szTemp[pos] = 'P'; break;
                case 19: szTemp[pos] = 'U'; break;
                default: break;
            }
        }
        decimal = decimal / 20;
        pos += 1;
        if (decimal == 0)
            break;
    }
    for (unsigned long i=0; i<pos; ++i) {
        result[i] = szTemp[pos-1-i];
    }
}

 

 

반응형

'개발 > Note' 카테고리의 다른 글

matplotlib 사용  (0) 2022.11.18
git 소스코드 비교  (0) 2022.02.23
root-level 디렉토리에 폴더, symbolic link 생성 방법  (0) 2021.03.15
RSA 암복호화 테스트  (0) 2021.02.05
RSA key 파일 생성  (0) 2021.02.05
블로그 이미지

SKY STORY

,
반응형

성장 성공하고 싶다면 남탓 환경탓 대신 내탓하라

by 조지 버나드 쇼

 

사람은 흔히 자기가 처해 있는 상태를

환경의 탓이라고 불평한다.

나는 환경이라는 것을 믿지 않는다.

성공하는 사람들이란

자기가 바라는 환경을 찾아내는 사람들이다.

그리고 발견하지 못하면 자기가 만들면 된다.

 

반응형
블로그 이미지

SKY STORY

,
반응형

1.Teslausb 다운로드

https://github.com/marcone/teslausb

현재 최신버전 Teslausb-20201211.zip파일 다운로드

https://github.com/marcone/teslausb/releases/tag/v2.5

 

 

2.Etcher 다운로드 및 설치

https://www.balena.io/etcher/

 

3.SMB teslausb conf 파일(teslausb_setup_variables.conf) 다운로드 

https://github.com/dasonwong/teslausb_variables

 

4.1번 파일(teslausb)을 더블클릭하여 마운트 시킨다. boot 드라이브에 3번파일을 복사한다.

 

5.SMB teslausb conf 파일에서 와이파이 접속 정보를 설정한다.

라즈베리파이 제로 W는 2.4Ghz 와이파이만 지원된다.

export SSID='netcanis'

export WIFIPASS='123456'

 

6.SMB teslausb conf 파일에서 와이파이 AP 접속 정보를 설정하고 저장.

비밀번호는 8자 이상이어야 한다.

export AP_SSID='teslausb'

export AP_PASS='12345678'

 

 

7.micro sd카드를 아답터에 삽입하여 컴퓨터에 연결한다.

 

8.balenaEtcher 실행하여 1번파일을 선택한다.

 

9.usb메모리 드라이브 선택하고 'Flash'버튼을 선택하여 라이팅한다.

 

10.완료되면 usb드라이브 제거후 microsd카드를 빼서 라즈베리파이에 넣는다.

11.iPhone 설정에서 핫스팟을 켜준다. 

 

 

2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 1

2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 2

2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 3

2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 4

2021.08.10 - [Tesla] - Rclone 설정 (Google Drive에 TeslaCam 파일 업로드) - Step 1

2021.08.10 - [Tesla] - Rclone 설정 (Google Drive에 TeslaCam 파일 업로드) - Step 2

2021.08.10 - [Tesla] - Rclone 설정 (Google Drive에 TeslaCam 파일 업로드) - Step 3

 

 

 

반응형
블로그 이미지

SKY STORY

,
반응형

15.패스워드를 변경해 준다.

pi@teslausb:~ $ passwd

Changing password for pi.

Current password: [현재 패스워드 입력]

New password: [새로운 패스워드 입력]

Retype new password: [새로운 패스워드 재입력]

#passwd: password updated successfully

 

16.설정이 완료(약 10분)되면 자동 리부팅되며 이재부터는 부팅시 화면에 출력되는 내용은 없다.

17.라즈베리파이가 정상동작되는지 확인하려면 전원을 컴퓨터 USB단자에 연결했을 때 

라즈베리파이가 USB드라이브로 연결되고 'TeslaCam'폴더가 생성되어 있다면 성공이다.

 

17.재부팅 이후 휴대폰으로 와이파이 ‘teslausb’에 접속한다. (설정한 WiFi AP SSID로 접속)

18.앱스토어에서 'Cam Viewer for Tesla'앱을 다운로드 받아 실행하여 저장된 영상을 확인한다.

 

Cam Viewer for Tesla

https://apps.apple.com/us/app/cam-viewer-for-tesla/id1476197999

 

‎Cam Viewer for Tesla

‎Introducing Cam Viewer App with Tesla Software v10 Support You can now view your Tesla captured video directly on your iOS device. Note: to access a USB drive on your device requires iOS 13 AND the Lightning to USB 3 Camera Adapter. Plug in power to the

apps.apple.com

 

Documents:Files, PDF, Browser  (SMB 폴더 브라우징)

https://apps.apple.com/us/app/documents-files-pdf-browser/id364901807

 

‎Documents: Files, PDF, Browser

‎A central hub for all your files. Read, listen, view, annotate almost anything you want on your iPad and iPhone. Files, documents, books, any content is at home in Documents by Readdle. It’s the Finder for iOS you’ve always wanted. App Store Editors

apps.apple.com

 

https://apps.apple.com/us/app/sentry-dashcam-for-teslas/id1514151653

 

‎Sentry DashCam for Teslas

‎Introducing the simplest, and most hassle-free way to download, watch, and share your Tesla Sentry and DashCam Videos. Now with wireless support for Raspberry Pi 4 and Raspberry Pi Zero W(beta), Tesla Sentry DashCam is the simplest, and most hassle-free

apps.apple.com

 

 

2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 1

2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 2

2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 3

2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 4

2021.08.10 - [Tesla] - Rclone 설정 (Google Drive에 TeslaCam 파일 업로드) - Step 1

2021.08.10 - [Tesla] - Rclone 설정 (Google Drive에 TeslaCam 파일 업로드) - Step 2

2021.08.10 - [Tesla] - Rclone 설정 (Google Drive에 TeslaCam 파일 업로드) - Step 3

 

 

 

반응형
블로그 이미지

SKY STORY

,
반응형

[ 라즈베리 파이에 사용할 모니터, 키보드, 마우스가 있을 경우 ]

12.라즈베리파이에 모니터, 키보드, 마우스를 연결한 후 전원을 연결한다.

13.휴대폰의 개인용 핫스팟에 라즈베리 파이가 연결되는지 확인한다. (좌상단 시계가 파란색으로 변하면 연결완료)

14.첫로그인  계정은 pi / raspberry 이다.

 

 

[ 라즈베리 파이에 사용할 모니터, 키보드, 마우스가  없을 경우 ]

12.사용자 컴퓨터도 휴대폰 핫스팟에 연결한다. 

13.mac컴퓨터 콘솔을 실행하여 ssh접속한다.

ssh pi@teslausb.local

yes입력

 

14.첫로그인 시 계정은 pi / raspberry 이다.

 

14-1.파일목록 보기

 

14-2.설정 로그 보기

 

14-3.설치가 완료되면 업그레이드를 한다.

sudo -I

bin/setup-teslausb upgrade

 

 

2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 1

2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 2

2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 3

2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 4

2021.08.10 - [Tesla] - Rclone 설정 (Google Drive에 TeslaCam 파일 업로드) - Step 1

2021.08.10 - [Tesla] - Rclone 설정 (Google Drive에 TeslaCam 파일 업로드) - Step 2

2021.08.10 - [Tesla] - Rclone 설정 (Google Drive에 TeslaCam 파일 업로드) - Step 3

 

 

 

반응형
블로그 이미지

SKY STORY

,
반응형

 
TeslaCam 비디오를 보려면 차량에 탑승하여 뷰어를 통해서  볼 수 있다.
개발자(들)에 의해 Raspberry pi를 이용하여 가상 드라이브를 생성하고 해당 폴더를 WiFi를 통해 공유하는 방법을 Github를 통해 공개하였고 많은 테슬라 오너들이 애용하고 있다.
 
해당 방법은 컴퓨터를 잘 모르는 사람들의 경우 굉장히 어렵게 
느껴질 수 있음으로 쉽게 따라 할 수 있도록 자세히 방법을 소개하도록 하겠다.
 
준비물은 다음과 같다.
 
준비물 :

  • Raspberry pi Zero W (라즈베리 파이 제로 W) https://amzn.to/3A8Awa3
  • micro sd card 128G 이상
  • iPhone
  • Mac컴퓨터
  • 라즈베리파이 사용을 위한 모니터,키보드,마우스 

 
 
teslausb의 작동 방법 이해 :
라즈베리파이가 구동되면 사용자가 설정한 핫스팟 WiFi에 접속을 한다. 라즈베리 파이가
USB 드라이브처럼 가상 드라이브를 생성하여 Tesla는 접속된 라즈베리파이를 
USB 드라이브로 인식하게 되며 영상을 저장하게 된다. 또한 WIFI AP가 활성화되어 
설정된 와이파이 네트워크(SMB)에 접속하면 저장된 영상에 접근할 수 있게 된다.
 
 
2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 1
2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 2
2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 3
2021.08.09 - [Tesla] - Teslausb를 사용하여 TeslaCam 비디오를 무선으로 보기 - Step 4
2021.08.10 - [Tesla] - Rclone 설정 (Google Drive에 TeslaCam 파일 업로드) - Step 1
2021.08.10 - [Tesla] - Rclone 설정 (Google Drive에 TeslaCam 파일 업로드) - Step 2
2021.08.10 - [Tesla] - Rclone 설정 (Google Drive에 TeslaCam 파일 업로드) - Step 3
 
 

반응형
블로그 이미지

SKY STORY

,