Skip to content

Commit

Permalink
Merge branch 'develop' into feature/iOS/GWL-407
Browse files Browse the repository at this point in the history
  • Loading branch information
MaraMincho committed Jan 13, 2024
2 parents 25226d1 + 9dd4665 commit 0e87e50
Show file tree
Hide file tree
Showing 24 changed files with 888 additions and 129 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/iOS_CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
jobs:
swift-format:
if: contains(github.event.pull_request.labels.*.name, '📱 iOS')
runs-on: macos-13
runs-on: macos-latest
env:
working-directory: ./iOS

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ProjectDescription
// MARK: - Feature

public enum Feature: String {
case writeBoard
case home
case splash
case profile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ public enum CoordinatorFlow {
case onboarding
case profile
case home
case writeBoard
}
1 change: 1 addition & 0 deletions iOS/Projects/Features/Profile/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ let project = Project.makeModule(
.commonNetworkingKeyManager,
.keychain,
.userInformationManager,
.feature(.writeBoard),
],
testDependencies: [],
resources: "Resources/**"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Keychain
import Log
import Trinet
import UIKit
import WriteBoardFeature

// MARK: - ProfileFinishFinishDelegate

Expand Down Expand Up @@ -90,4 +91,22 @@ extension ProfileCoordinator: ProfileCoordinating {
viewController.hidesBottomBarWhenPushed = true
navigationController.pushViewController(viewController, animated: true)
}

public func presentWriteBoard() {
let writeBoardCoordinator = WriteBoardCoordinator(
navigationController: navigationController,
delegate: self
)
childCoordinators.append(writeBoardCoordinator)
writeBoardCoordinator.start()
}
}

// MARK: CoordinatorFinishDelegate

extension ProfileCoordinator: CoordinatorFinishDelegate {
public func flowDidFinished(childCoordinator: Coordinating) {
childCoordinators = childCoordinators.filter { $0.flow != childCoordinator.flow }
childCoordinator.childCoordinators.removeAll()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ public protocol ProfileCoordinating: Coordinating {

/// 프로필 설정 화면으로 넘어갑니다.
func moveToProfileSettings()

/// 글쓰기 화면으로 넘어갑니다.
func presentWriteBoard()
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public final class ProfileViewController: UICollectionViewController {

private let viewDidLoadSubject: PassthroughSubject<Void, Never> = .init()
private let didTapSettingButtonSubject: PassthroughSubject<Void, Never> = .init()
private let didTapWriteBarButtonSubject: PassthroughSubject<Void, Never> = .init()
private let paginationEventSubject: PassthroughSubject<ProfileItem, Never> = .init()

private var subscriptions: Set<AnyCancellable> = []
Expand Down Expand Up @@ -61,13 +62,25 @@ public final class ProfileViewController: UICollectionViewController {
private func setupStyles() {
collectionView.backgroundColor = DesignSystemColor.primaryBackground
navigationItem.backButtonDisplayMode = .minimal
navigationItem.rightBarButtonItem = .init(

let settingBarButtonItem = UIBarButtonItem(
image: .init(systemName: "gearshape"),
style: .plain,
target: self,
action: #selector(didTapSettingButton)
action: #selector(didTapSettingBarButton)
)

let writeBoardBarButtonItem = UIBarButtonItem(
image: .init(systemName: "plus.square"),
style: .plain,
target: self,
action: #selector(didTapWriteBarButton)
)
navigationItem.rightBarButtonItem?.tintColor = DesignSystemColor.primaryText

let rightBarButtonItems = [writeBoardBarButtonItem, settingBarButtonItem]
rightBarButtonItems.forEach { $0.tintColor = DesignSystemColor.primaryText }

navigationItem.rightBarButtonItems = rightBarButtonItems
}

private func bind() {
Expand All @@ -76,7 +89,8 @@ public final class ProfileViewController: UICollectionViewController {
viewDidLoadPublisher: viewDidLoadSubject.eraseToAnyPublisher(),
didTapSettingButtonPublisher: didTapSettingButtonSubject.eraseToAnyPublisher(),
paginationEventPublisher: paginationEventSubject.eraseToAnyPublisher(),
refreshPostsPublisher: refreshControl.publisher(.valueChanged).map { _ in () }.eraseToAnyPublisher()
refreshPostsPublisher: refreshControl.publisher(.valueChanged).map { _ in () }.eraseToAnyPublisher(),
writeBoardPublisher: didTapWriteBarButtonSubject.eraseToAnyPublisher()
)
)
.receive(on: RunLoop.main)
Expand All @@ -100,8 +114,13 @@ public final class ProfileViewController: UICollectionViewController {
// MARK: - Custom Methods

@objc
private func didTapSettingButton() {
didTapSettingButtonSubject.send(())
private func didTapSettingBarButton() {
didTapSettingButtonSubject.send()
}

@objc
private func didTapWriteBarButton() {
didTapWriteBarButtonSubject.send()
}

/// 에러 알림 문구를 보여줍니다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public struct ProfileViewModelInput {
let didTapSettingButtonPublisher: AnyPublisher<Void, Never>
let paginationEventPublisher: AnyPublisher<ProfileItem, Never>
let refreshPostsPublisher: AnyPublisher<Void, Never>
let writeBoardPublisher: AnyPublisher<Void, Never>
}

public typealias ProfileViewModelOutput = AnyPublisher<ProfileViewModelState, Never>
Expand Down Expand Up @@ -59,6 +60,12 @@ extension ProfileViewModel: ProfileViewModelRepresentable {
}
.store(in: &subscriptions)

input.writeBoardPublisher
.sink { [weak self] _ in
self?.coordinating?.presentWriteBoard()
}
.store(in: &subscriptions)

let profileInfoPublisher = input.viewDidLoadPublisher
.flatMap(useCase.fetchProfile)
.map(ProfileViewModelState.setupProfile)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
// Copyright © 2023 kr.codesquad.boostcamp8. All rights reserved.
//

import CoreLocation
import Foundation

// MARK: - KalmanFilterUpdateRequireElement

struct KalmanFilterUpdateRequireElement {
let longitude: Double
let latitude: Double
let prevSpeedAtLongitude: Double
let prevSpeedAtLatitude: Double
let prevCLLocation: CLLocation
let currentCLLocation: CLLocation
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,26 @@
// Copyright © 2023 kr.codesquad.boostcamp8. All rights reserved.
//

import CoreLocation
import Foundation
import Log
import simd

struct KalmanFilter {
/// 새로운 값을 입력하게 된다면, 에측을 통해서 값을 작성하게 되는 변수 입니다.
var x = MatrixOfTwoDimension([[]])
var x = simd_double4()

/// 초기 오차 공분산 입니다.
/// 초기 값은 에러가 많기 떄문에 다음과 같이 크게 가져갔습니다.
private var p = MatrixOfTwoDimension([
[500, 0, 0, 0],
private var p = simd_double4x4([
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 500, 0],
[0, 0, 1, 0],
[0, 0, 0, 1],
])

// 사용자 경험을 통해 얻어진 값 입니다. (일단 대한민국 GPS환경이 좋다고 가정하여,
// 애플 오차의 1/2로 가져갔습니다.)

private var q = MatrixOfTwoDimension([
[0.000455, 0, 0, 0],
private var q = simd_double4x4([
[0.00082, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0.000059, 0],
[0, 0, 0.00082, 0],
[0, 0, 0, 0],
])

Expand All @@ -45,77 +42,86 @@ struct KalmanFilter {
/// 1도 일 때 몇 km? = 지구 반지름 6371 * cos(37) * 1 (pi/180) = 85.18km
/// 1º : 85180m = y : 10m
/// y = 0.00011739 ~= 0.000117
private var r = MatrixOfTwoDimension([
[0.000899, 0],
[0, 0.000117],
private var r = simd_double2x2([
[0.00082, 0],
[0, 0.00082],
])

var prevHeadingValue: Double
var prevSpeedAtLatitude: Double = 0
var prevSpeedAtLongitude: Double = 0

/// 관계 식 입니다.
lazy var A = MatrixOfTwoDimension([
[1, cos(prevHeadingValue) * prevSpeedAtLatitude, 0, 0],
lazy var A = simd_double4x4([
[1, timeInterval * prevVelocity.lat, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, sin(prevHeadingValue) * prevSpeedAtLongitude],
[0, 0, 1, timeInterval * prevVelocity.long],
[0, 0, 0, 1],
])

/// 우리가 궁금한건 위도와 경도이기 때문에 필요한 부분만 기재했습니다.
private var H = MatrixOfTwoDimension([
let H = simd_double4x2([
[1, 0],
[0, 0],
[0, 1],
[0, 0],
])

// 우리가 궁금한건 위도와 경도이기 때문에 필요한 부분만 기재했습니다.

var prevTime: Date

init(initLocation: CLLocation) {
x = .init(initLocation.coordinate.latitude, 0, initLocation.coordinate.longitude, 0)
prevTime = initLocation.timestamp
}

var timeInterval: Double = 0.0

var prevLocation = CLLocation()

let pIdentity = simd_double4x4([
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1],
])

init(initLongitude: Double, initLatitude: Double, headingValue: Double) {
x = .init([
[initLatitude],
[0],
[initLongitude],
[0],
])
prevHeadingValue = headingValue
}
var prevVelocity: (lat: Double, long: Double) = (0, 0)

/// 사용자가 가르키는 방향을 업데이트 합니다.
mutating func update(heading: Double) {
prevHeadingValue = heading
}
mutating func update(currentLocation: CLLocation) {
let currentTime = currentLocation.timestamp

let prevTimeInterval = prevTime.timeIntervalSince1970
let currentTimeInterval = currentTime.timeIntervalSince1970

timeInterval = currentTimeInterval - prevTimeInterval

let velocityLatitude = (prevLocation.coordinate.latitude - currentLocation.coordinate.latitude)
let velocityLongitude = (prevLocation.coordinate.longitude - currentLocation.coordinate.longitude)

/// Update합니다.
mutating func update(initLongitude: Double, initLatitude: Double, prevSpeedAtLatitude: Double, prevSpeedAtLongitude: Double) {
let mesure = MatrixOfTwoDimension(
prevVelocity = (velocityLatitude, velocityLongitude)

prevLocation = currentLocation
prevTime = currentTime

let mesure = simd_double2(
[
[initLatitude],
[initLongitude],
currentLocation.coordinate.latitude,
currentLocation.coordinate.longitude,
]
)
self.prevSpeedAtLatitude = prevSpeedAtLatitude
self.prevSpeedAtLongitude = prevSpeedAtLongitude
guard
let prediction = A.multiply(x),
let predictionErrorCovariance = A.multiply(p)?.multiply(A.transPose())?.add(q),

let notInversed = H.multiply(predictionErrorCovariance)?.multiply(H.transPose())?.add(r),
let prevKalman = notInversed.invert(),
let kalman = predictionErrorCovariance.multiply(H.transPose())?.multiply(prevKalman),

let tempValue = H.multiply(prediction),
let subTractedValue = mesure.sub(tempValue),
let multiedKalmanValue = kalman.multiply(subTractedValue),
let currentX = prediction.add(multiedKalmanValue),

let tempPredictionErrorCovariance = kalman.multiply(H)?.multiply(predictionErrorCovariance),
let currentPredictionErrorCovariance = predictionErrorCovariance.sub(tempPredictionErrorCovariance)
else {
return
}

let xp = A * x
let pp = A * p * A.transpose + q

let temp = pp * H.transpose
let invert = (H * pp * H.transpose + r).inverse
let kalman = temp * invert

let currentX = xp + kalman * (mesure - H * xp)

let currentP = pp - kalman * H * pp.inverse
x = currentX
p = currentPredictionErrorCovariance
p = currentP
}

var latestCensoredPosition: KalmanFilterCensored {
return .init(longitude: x.value[2][0], latitude: x.value[0][0])
return .init(longitude: x[2], latitude: x[0])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import Foundation

protocol KalmanUseCaseRepresentable {
func updateFilter(_ element: KalmanFilterUpdateRequireElement) -> KalmanFilterCensored?
func updateHeading(_ heading: Double)
}

// MARK: - KalmanUseCase
Expand All @@ -27,21 +26,13 @@ final class KalmanUseCase {
// MARK: KalmanUseCaseRepresentable

extension KalmanUseCase: KalmanUseCaseRepresentable {
func updateHeading(_ heading: Double) {
filter?.update(heading: heading)
}

func updateFilter(_ element: KalmanFilterUpdateRequireElement) -> KalmanFilterCensored? {
if filter == nil {
filter = .init(initLongitude: element.latitude, initLatitude: element.longitude, headingValue: 0)
let currentLocation = element.currentCLLocation
filter = .init(initLocation: currentLocation)
return nil
}
filter?.update(
initLongitude: element.longitude,
initLatitude: element.latitude,
prevSpeedAtLatitude: element.prevSpeedAtLatitude,
prevSpeedAtLongitude: element.prevSpeedAtLongitude
)
filter?.update(currentLocation: element.currentCLLocation)

return filter?.latestCensoredPosition
}
Expand Down
Loading

0 comments on commit 0e87e50

Please sign in to comment.