06. Ambient Lighting — 하나의 조명이 여섯 가지를 말해야 할 때
출처: SR §6.4.3 (1. 일반 Ambient Lighting / 2. Goodbye Lighting / 3. Smart Access 연계 시각 피드백 / 4. Warning, Fault 및 Emergency 표시 / 5. 표시 우선순위 / 6. 조도 기반 밝기 조절 / 7. Ambient Lighting 동작 / 8. 오류 및 안전 동작) 관련 TBD: TBD-011(조도 기준 Auto Light/Ambient 밝기 임계값 및 Hysteresis), TBD-019(Ambient Lighting 의 일반/Warning/Fault/Emergency 표시 Pattern·밝기 및 일시적 표시 지속시간), TBD-034(엔진룸 대상 동물 진입 상태 Ambient Lighting 우선순위 등급) 담당 노드: S32K144 Function Controller(Ambient Lighting 직접 제어 — §7.2)
0. 이 절의 본질 — 출력 채널이 하나뿐인 표시 시스템
05편의 Predictive Climate가 "하나의 Fan을 세 기능이 나눠 쓰는" 문제였다면, 이 절은 더 심하다. 하나의 조명 시스템이 여섯 종류의 정보를 표현해야 한다.
| 표시 종류 | 무엇을 말하는가 | 성격 |
|---|---|---|
| 일반 Ambient Lighting | 사용자가 설정한 실내 분위기 | 설정 |
| Goodbye Lighting | 차량 전원 종료 | 일시적 이벤트 |
| Smart Access 시각 피드백 | 접근·잠금·잠금 해제 | 일시적 이벤트 |
| Warning | 경고 상태 | 안전 |
| Fault | 고장 상태 | 안전 |
| Emergency | 비상 상태 | 안전 |
화면이라면 여러 개를 동시에 그릴 수 있다. 조명은 한 번에 하나의 색·패턴만 낼 수 있다. 그래서 §6.4.3에는 **"5. 표시 우선순위"**라는 소절이 통째로 있고, 이 절 전체 요구사항의 4분의 1을 차지한다.
1. 일반 Ambient Lighting
- 사용자는 차량 내 HMI 를 통해 일반 Ambient Lighting 의 사용 여부를 설정할 수 있어야 한다.
- 사용자는 차량 내 HMI 를 통해 허용된 범위에서 일반 Ambient Lighting 의 밝기를 변경할 수 있어야 한다.
- 시스템은 사용자의 유효한 Ambient Lighting 설정을 일반 조명 상태에 반영해야 한다.
- 시스템은 일반 Ambient Lighting 이 비활성화된 경우 일반 조명 출력을 중단해야 한다.
- 시스템은 일반 Ambient Lighting 의 사용 설정과 관계없이 Goodbye Lighting, Smart Access 시각 피드백 및 안전 관련 조명 표시는 수행해야 한다.
- 시스템은 일반 Ambient Lighting 의 설정 상태와 현재 밝기 수준을 차량 내 HMI 에서 사용자가 확인할 수 있도록 해야 한다.
1-1. 다섯 번째 문장이 이 절 전체의 열쇠다
"시스템은 일반 Ambient Lighting 의 사용 설정과 관계없이 Goodbye Lighting, Smart Access 시각 피드백 및 안전 관련 조명 표시는 수행해야 한다."
사용자가 무드등을 껐다고 안전 경고까지 꺼지면 안 된다.
01편의 VSS 무음 설정과 정확히 같은 구조다.
| 사용자 설정으로 끌 수 있는 것 | 끌 수 없는 것 | |
|---|---|---|
| VSS(§6.1.3) | 피드백·모드 전환 음향 | 경고음(Anti-pinch, ROA 등) |
| Ambient(§6.4.3) | 일반 Ambient Lighting | Goodbye, Smart Access 피드백, Warning/Fault/Emergency |
두 절에서 같은 원칙이 반복된다: 사용자 편의 설정은 안전 기능을 무력화할 수 없다.
구현상 이건 "설정 체크를 출력 함수 최상단에 두지 마라"는 뜻이다.
/* ✗ 이렇게 하면 요구사항 위반 */
void ambient_output(amb_pattern_t p) {
if (!g_user_ambient_enabled) return; /* 안전 표시까지 막힌다 */
led_apply(p);
}
/* ✓ 표시 종류별로 게이트 */
typedef enum {
AMB_KIND_NORMAL, /* 사용자 설정에 종속 */
AMB_KIND_GOODBYE, /* 설정과 무관 */
AMB_KIND_ACCESS_FB, /* 설정과 무관 */
AMB_KIND_WARNING, /* 설정과 무관 — 안전 */
AMB_KIND_FAULT, /* 설정과 무관 — 안전 */
AMB_KIND_EMERGENCY, /* 설정과 무관 — 안전 */
} amb_kind_t;
static bool ambient_gated_by_user(amb_kind_t k) {
return (k == AMB_KIND_NORMAL) && !g_user_ambient_enabled;
}
2. Goodbye Lighting
- 시스템은 차량 전원이 종료되는 경우 Goodbye Lighting 을 제공해야 한다.
- 시스템은 Goodbye Lighting 을 일시적으로 제공하고, 정의된 표시가 완료된 후 해당 출력을 종료해야 한다.
- 시스템은 Goodbye Lighting 이 종료된 후 최신 차량 상태와 일반 Ambient Lighting 설정에 따라 이후 조명 상태를 결정해야 한다.
2-1. "일시적"이 요구사항으로 명시돼 있다
Goodbye Lighting은 자기 종료(self-terminating) 표시다. 시작 조건은 있지만 종료 조건이 외부에 없다 — 정의된 시간이 지나면 스스로 끝난다.
TBD-019가 이 시간을 확정한다: "Ambient Lighting 의 일반/Warning/Fault/Emergency 표시 Pattern, 밝기 및 일시적 표시 지속시간 — 기능별 시각적 피드백과 안전 표시 우선순위 정책 통합 후 확정".
2-2. 종료 후의 처리가 "복구"가 아니다
"시스템은 Goodbye Lighting 이 종료된 후 최신 차량 상태와 일반 Ambient Lighting 설정에 따라 이후 조명 상태를 결정해야 한다."
00편 패턴 C의 정확한 적용이다. "Goodbye 전 상태로 되돌린다"가 아니라 **"지금 조건으로 새로 결정한다"**이다.
이 차이가 실제로 다른 결과를 낳는다.
Goodbye 시작 전: 일반 Ambient ON (파란색)
Goodbye 진행 중: 사용자가 모바일 앱으로 일반 Ambient 를 OFF 로 변경
Goodbye 종료:
✗ 복구 방식 → 파란색 다시 켜짐 (사용자 의사 무시)
✓ 재결정 방식 → 꺼진 상태 유지 (최신 설정 반영)
/* §6.4.3 — 일시적 표시 종료 후 상태 재결정 */
static void ambient_on_temporary_end(void)
{
/* 저장해 둔 이전 상태를 복원하지 않는다 */
amb_state_t next = ambient_decide_from_current(); /* 최신 조건으로 새로 계산 */
ambient_apply(next);
}
3. Smart Access 연계 시각 피드백
- 시스템은 등록된 Digital Key 사용자의 유효한 차량 접근 상태가 확인된 경우 해당 상태를 Ambient Lighting 으로 표시해야 한다.
- 시스템은 Door Unlock 동작이 정상적으로 완료된 경우 해당 결과를 Ambient Lighting 으로 표시해야 한다.
- 시스템은 Door Lock 동작이 정상적으로 완료된 경우 해당 결과를 Ambient Lighting 으로 표시해야 한다.
- 시스템은 Door Lock 또는 Door Unlock 동작이 정상적으로 완료되지 않은 경우 정상 완료 상태와 구분되는 Ambient Lighting 피드백을 제공해야 한다.
- 시스템은 Smart Access 와 연계된 접근, 잠금 및 잠금 해제 피드백을 일시적으로 제공해야 한다.
- 시스템은 Smart Access 의 일시적 시각 피드백이 종료된 경우 최신 차량 상태와 일반 Ambient Lighting 설정에 따라 이후 조명 상태를 결정해야 한다.
3-1. 04편과의 연결 — "정상적으로 완료된 경우"
04편에서 본 폐루프 확인 요구사항이 여기서도 반복된다.
§6.4.1 도어 잠금 또는 잠금 해제 동작 후 실제 목표 상태가 확인되지 않는 경우
해당 요청을 정상 완료로 처리하지 않아야 한다.
↓
§6.1.1 정상적으로 완료된 경우 → 차임 / 정상 잠금에 실패한 경우 → 경적
§6.4.3 정상적으로 완료된 경우 → 표시 / 정상 완료되지 않은 경우 → 구분되는 피드백
같은 판정 결과가 소리(VSS)와 빛(Ambient) 두 채널로 동시에 나간다. 이건 우연이 아니라 다중 감각 피드백(multimodal feedback) 설계다.
| 상황 | 청각 | 시각 |
|---|---|---|
| 잠금 성공 | 차임 1회 | 성공 패턴 |
| 잠금 해제 성공 | 차임 2회 | 성공 패턴 |
| 잠금 실패 | 경적 | 성공과 구분되는 패턴 |
주차장이 시끄러워 소리를 못 들어도 빛으로 알 수 있고, 차에서 멀어져 조명을 못 봐도 소리로 알 수 있다. 이게 이중화의 실질적 가치다.
3-2. "구분되는" 피드백이라고만 하고 색을 지정하지 않는다
"정상 완료 상태와 구분되는 Ambient Lighting 피드백을 제공해야 한다."
빨간색이라고 쓰지 않았다. §9의 원칙 때문이다 — SR은 사용자 관점에서 관찰 가능한 것만 정의하고, 구체적 패턴은 TBD-019와 설계 문서로 미룬다.
"구분 가능하다"가 검증 가능한 요구사항이라는 게 중요하다. 시험에서 "성공 시 표시와 실패 시 표시가 사용자에게 구분되는가"를 확인하면 된다. 색을 지정하면 색이 바뀔 때마다 SR을 고쳐야 한다.
4. Warning, Fault 및 Emergency 표시
- 시스템은 Warning 상태를 일반 조명 및 Smart Access 시각 피드백과 구분하여 Ambient Lighting 으로 표시해야 한다.
- 시스템은 Fault 상태를 일반 조명 및 Smart Access 시각 피드백과 구분하여 Ambient Lighting 으로 표시해야 한다.
- 시스템은 Emergency 상태를 일반 조명 및 Smart Access 시각 피드백과 구분하여 Ambient Lighting 으로 표시해야 한다.
- 시스템은 Warning 상태가 유효한 동안 해당 안전 표시를 유지해야 한다.
- 시스템은 Fault 상태가 유효한 동안 해당 안전 표시를 유지해야 한다.
- 시스템은 Emergency 상태가 유효한 동안 해당 안전 표시를 유지해야 한다.
- 시스템은 Warning, Fault 및 Emergency 표시가 일반 Ambient Lighting 의 ON/OFF 또는 밝기 설정에 의해 비활성화되지 않도록 해야 한다.
- 시스템은 Warning, Fault 또는 Emergency 상태가 해제된 경우 최신 차량 상태와 사용자 설정에 따라 이후 Ambient Lighting 상태를 결정해야 한다.
4-1. 세 등급의 차이
SR은 Warning / Fault / Emergency를 정의하지 않고 사용한다. 자동차 도메인의 관례로 해석하면:
| 등급 | 의미 | 이 시스템의 예 |
|---|---|---|
| Warning | 주의가 필요한 상태. 기능은 동작하지만 조건이 나쁨 | 후방 근접 위험, 엔진룸 동물 진입(?), 도어 열림 |
| Fault | 기능 고장. 해당 기능을 못 쓴다 | 센서 오류, 통신 오류, Fan 요구-실제 불일치, VSS 오디오 경로 이상 |
| Emergency | 즉각적 인명 위험 | Anti-pinch 끼임, ROA 잔류 탑승자 |
TBD-034가 "엔진룸 대상 동물 진입 상태 Ambient Lighting 우선순위 등급 — Warning/Fault/Emergency 중 적용 등급을 별도 티어 여부 확정"이라고 하는 게 이 분류의 어려움을 보여준다.
고양이 진입이 Warning인가 Emergency인가? 논거가 갈린다.
- Warning 주장: 사람이 다치는 게 아니고, 사용자가 보닛을 열어 확인하면 된다
- Emergency 주장: 시동을 걸면 동물이 즉사한다. 시간이 촉박하다
- 별도 티어 주장: 셋 중 어느 것도 아닌 성격이다 (긴급하지만 차량 고장이 아니고, 인명 위험도 아님)
SR이 답을 강요하지 않고 TBD로 남긴 게 옳다. 이건 정책 결정이지 기술 결정이 아니다.
4-2. "유효한 동안 유지"— 일시적 표시와의 대비
Goodbye Lighting : 일시적 — 정의된 표시 완료 후 종료
Smart Access 피드백 : 일시적 — 종료 후 상태 재결정
Warning / Fault / Emergency : 유효한 동안 유지 — 상태가 해제되어야 종료
표시의 수명 모델이 두 가지다.
typedef enum {
AMB_LIFE_TRANSIENT, /* 정의된 시간 후 자동 종료 (TBD-019) */
AMB_LIFE_STATEFUL, /* 원인 상태가 유효한 동안 유지 */
} amb_life_t;
typedef struct {
amb_kind_t kind;
amb_life_t life;
uint32_t started_ms;
uint32_t duration_ms; /* TRANSIENT 전용 (TBD-019) */
bool source_active; /* STATEFUL 전용 — 원인 상태의 유효성 */
} amb_item_t;
static bool amb_item_alive(const amb_item_t *it)
{
if (it->life == AMB_LIFE_TRANSIENT)
return (hal_now_ms() - it->started_ms) < it->duration_ms;
return it->source_active; /* §6.4.3 — 유효한 동안 유지 */
}
두 수명 모델을 한 변수로 관리하려 하면 반드시 버그가 난다. Warning에 타임아웃을 걸면 위험이 계속되는데 표시가 사라지고, Goodbye에 상태 기반 종료를 쓰면 영원히 안 꺼진다.
4-3. 일곱 번째 문장 = 1절의 다섯 번째 문장의 재확인
"시스템은 Warning, Fault 및 Emergency 표시가 일반 Ambient Lighting 의 ON/OFF 또는 밝기 설정에 의해 비활성화되지 않도록 해야 한다."
1절에서 "사용 설정과 관계없이"라고 했는데 여기서 밝기 설정까지 명시적으로 추가했다.
왜 따로 썼을까? ON/OFF는 명백하지만 밝기는 교묘하기 때문이다.
사용자가 일반 Ambient 밝기를 5 %로 설정
→ 같은 밝기 스케일을 Warning 에도 적용하면?
→ Warning 이 5 % 밝기로 표시됨 = 사실상 안 보임 = 비활성화된 것과 같음
**"꺼지지 않았지만 보이지 않는다"**는 상태를 막는 요구사항이다. 6절이 이걸 한 번 더 강화한다.
/* §6.4.3 — 안전 표시의 밝기 하한 */
#define AMB_SAFETY_MIN_DUTY 60 /* % — TBD-019 로 확정 */
static uint8_t ambient_final_duty(amb_kind_t k, uint8_t requested_duty)
{
uint8_t d = requested_duty;
if (g_lux_valid) d = apply_auto_dim(d, g_lux); /* 6절 조도 기반 조절 */
/* 안전 표시는 하한 아래로 내려가지 않는다 */
if (k >= AMB_KIND_WARNING && d < AMB_SAFETY_MIN_DUTY) d = AMB_SAFETY_MIN_DUTY;
return d;
}
01편의 VSS VOL_SAFETY_FLOOR와 완전히 같은 패턴이다. 소리에는 음량 하한, 빛에는 밝기 하한.
5. 표시 우선순위 — 이 절의 핵심
- 시스템은 동시에 둘 이상의 Ambient Lighting 표시 조건이 발생한 경우 정의된 우선순위에 따라 우선도가 높은 표시를 적용해야 한다.
- 시스템은 Emergency 표시를 Fault, Warning, Smart Access 시각 피드백, Goodbye Lighting 및 일반 Ambient Lighting 보다 우선해야 한다.
- 시스템은 Fault 표시를 Warning, Smart Access 시각 피드백, Goodbye Lighting 및 일반 Ambient Lighting 보다 우선해야 한다.
- 시스템은 Warning 표시를 Smart Access 시각 피드백, Goodbye Lighting 및 일반 Ambient Lighting 보다 우선해야 한다.
- 시스템은 Smart Access 의 일시적 시각 피드백을 Goodbye Lighting 및 일반 Ambient Lighting 보다 우선해야 한다.
- 시스템은 Goodbye Lighting 을 일반 Ambient Lighting 보다 우선해야 한다.
- 시스템은 안전 관련 표시가 종료된 이후 이전에 중단된 일시적 Smart Access 시각 피드백 또는 Goodbye Lighting 을 자동으로 재개하지 않아야 한다.
- 시스템은 우선순위가 높은 표시가 종료된 경우 최신 차량 상태와 사용자 설정을 기준으로 다음 Ambient Lighting 상태를 결정해야 한다.
5-1. 완전한 전순서가 5개 문장으로 정의된다
Emergency > Fault > Warning > Smart Access 피드백 > Goodbye > 일반 Ambient
02편의 윈도우 우선순위와 같은 서술 방식이다 — 각 등급이 자기보다 낮은 것들을 전부 열거한다. 중복되게 쓰는 이유는 §9의 검증 가능성이다. "Emergency > Fault"만 쓰고 추이성(transitivity)에 기대면, 시험에서 "Emergency vs 일반 Ambient"를 직접 검증하는 근거 요구사항이 없어진다.
5-2. 왜 안전 3종이 일시적 표시보다 위인가
일시적 표시는 정보이고, 안전 표시는 경고다. 문이 잠겼다는 사실은 나중에 확인해도 되지만, 손가락이 끼었다는 사실은 지금 알아야 한다.
그리고 Smart Access 피드백 > Goodbye의 순서도 근거가 있다. 둘 다 일시적인데, Smart Access는 사용자의 행동에 대한 응답이고 Goodbye는 시스템의 자발적 표시다. 사용자가 방금 한 행동의 결과를 알려주는 게 더 급하다.
5-3. 선점된 일시적 표시는 되살아나지 않는다
"시스템은 안전 관련 표시가 종료된 이후 이전에 중단된 일시적 Smart Access 시각 피드백 또는 Goodbye Lighting 을 자동으로 재개하지 않아야 한다."
00편 패턴 C. 01편의 VSS에서 "선점당한 소리를 되살리지 마라"고 한 것과 같다.
왜? Goodbye Lighting이 Emergency에 밀려 중단됐다. Emergency가 5분 후 해제됐다. 이제 와서 Goodbye를 보여주면 사용자는 "왜 지금 작별 인사를?"이라고 생각한다. 일시적 표시는 그 순간에만 의미가 있다.
5-4. 구현 — 우선순위 스택
#define AMB_PRIO_COUNT 6
/* 인덱스가 곧 우선순위 — 작을수록 우선 */
typedef enum {
AMB_P_EMERGENCY = 0,
AMB_P_FAULT = 1,
AMB_P_WARNING = 2,
AMB_P_ACCESS_FB = 3,
AMB_P_GOODBYE = 4,
AMB_P_NORMAL = 5,
} amb_prio_t;
static amb_item_t g_amb[AMB_PRIO_COUNT];
static bool g_amb_used[AMB_PRIO_COUNT];
/* 매 주기 호출 — 살아있는 것 중 가장 우선한 하나를 고른다 */
void ambient_tick(void)
{
/* 수명이 다한 항목 정리 */
for (int i = 0; i < AMB_PRIO_COUNT; i++) {
if (g_amb_used[i] && !amb_item_alive(&g_amb[i])) {
g_amb_used[i] = false;
/* §6.4.3 — 선점 후 재개를 위한 상태 저장을 하지 않는다 */
}
}
/* 최우선 항목 선택 */
for (int i = 0; i < AMB_PRIO_COUNT; i++) {
if (!g_amb_used[i]) continue;
if (ambient_gated_by_user(g_amb[i].kind)) continue; /* 일반만 설정에 종속 */
ambient_apply(&g_amb[i]);
return;
}
ambient_off();
}
핵심은 "선점"이라는 별도 동작이 없다는 것이다. 매 주기마다 살아있는 항목 중 최우선을 고르면 선점이 자동으로 일어난다. 그리고 선점당한 항목은 자기 수명이 다하면 그냥 사라진다 — 재개 로직을 만들 자리가 없으므로 요구사항 위반이 구조적으로 불가능해진다.
이런 걸 "requirement-by-construction"이라고 부를 만하다. 요구사항을
if문으로 체크하는 게 아니라, 요구사항을 어길 수 없는 자료구조를 고르는 것이다. 코드 리뷰에서 "여기서 재개하면 안 되는데"를 놓칠 여지가 없어진다.
5-5. 일시적 표시의 수명은 선점 중에도 흐르는가?
SR이 답하지 않는 부분이다.
Goodbye Lighting 시작 (지속시간 3초)
1초 경과
Emergency 발생 → Goodbye 선점당함
10초 경과
Emergency 해제
→ Goodbye 는? (남은 2초를 보여줄 것인가?)
**"자동으로 재개하지 않아야 한다"**가 답을 준다 — 재개하지 않는다. 따라서 선점 중에도 수명이 흐르든 멈추든 결과는 같다(어차피 다시 안 보여준다). 위 코드는 started_ms 기준이므로 수명이 계속 흐른다. 더 단순하고, 요구사항과도 일치한다.
6. 조도 기반 밝기 조절
- 시스템은 유효한 주변 조도 정보를 일반 Ambient Lighting 의 밝기 조절에 반영해야 한다.
- 시스템은 주변 조도 변화에 따라 일반 Ambient Lighting 의 밝기를 자동으로 조절해야 한다.
- 시스템은 주변 조도 정보를 신뢰할 수 없는 경우 해당 정보만을 근거로 새로운 자동 밝기 변경을 수행하지 않아야 한다.
- 시스템은 안전 관련 Ambient Lighting 표시의 식별 가능성이 일반 Ambient Lighting 의 사용자 밝기 설정 또는 자동 밝기 조절로 인해 상실되지 않도록 해야 한다.
6-1. 자동 조절의 대상은 "일반"뿐이다
유효한 주변 조도 정보를 일반 Ambient Lighting 의 밝기 조절에 반영
주변 조도 변화에 따라 일반 Ambient Lighting 의 밝기를 자동으로 조절
두 문장 모두 "일반"으로 한정돼 있다. 그리고 네 번째 문장이 안전 표시를 명시적으로 보호한다.
이유는 명확하다. 낮에 밝은 곳에서는 일반 무드등이 잘 안 보이므로 밝게, 밤에는 눈부시지 않게 어둡게 — 이건 편의 기능의 합리적 동작이다. 하지만 안전 표시는 낮에도 밤에도 반드시 보여야 한다.
6-2. 네 번째 문장의 정확한 의미
"시스템은 안전 관련 Ambient Lighting 표시의 **식별 가능성**이 일반 Ambient Lighting 의 사용자 밝기 설정 또는 자동 밝기 조절로 인해 상실되지 않도록 해야 한다."
**"식별 가능성"**이라는 단어를 썼다. 단순히 "밝기 하한"이 아니다.
밝은 대낮에는 절대 밝기가 높아도 주변광 대비 잘 안 보인다. 인간의 시각은 **절대 휘도가 아니라 대비(contrast)**에 반응한다.
식별 가능성 ∝ (표시 휘도 − 배경 휘도) / 배경 휘도
따라서 정확한 구현은 주변 조도에 따라 안전 표시 밝기 하한 자체를 올리는 것이다.
/* §6.4.3 — 안전 표시의 식별 가능성 보장 */
static uint8_t safety_min_duty_for(uint16_t lux, bool lux_valid)
{
if (!lux_valid) return AMB_SAFETY_MIN_DUTY_DEFAULT; /* 보수적 기본값 */
/* 주변이 밝을수록 하한을 높인다 — 대비를 유지하기 위해 */
if (lux >= LUX_BRIGHT_DAY) return 100; /* 대낮 — 최대 출력 */
if (lux >= LUX_INDOOR) return 80;
if (lux >= LUX_DUSK) return 60;
return 40; /* 야간 — 눈부심 방지하되 식별 가능 */
}
야간에도 40 % 아래로는 안 내려간다. 눈부심을 줄이는 것과 안 보이게 만드는 것은 다르다.
TBD-011이 이 값들을 확정한다: "조도 기준 Auto Light/Ambient 밝기 임계값 및 Hysteresis".
6-3. 히스테리시스는 여기서도 필요하다
03편의 조도 판정과 같은 문제다. 조도가 임계값 근처에서 흔들리면 조명 밝기가 깜빡거린다. 게다가 조명 자체가 조도 센서에 영향을 줄 수 있다 — 양성 피드백 루프가 생길 수 있다.
실내 조명 밝아짐 → 조도 센서가 밝게 읽음 → "밝으니 더 밝게" → 더 밝아짐 → …
이걸 막으려면:
- 조도 센서를 실외 조도 기준으로 배치한다 (§6.3.1이 "외부 조도"라고 한 이유)
- 밝기 변경 후 일정 시간 조도 판정을 억제한다
- 변화율을 제한한다 (slew rate limit)
/* 밝기 변화율 제한 — 급격한 변화를 사용자가 인지하지 못하게 + 피드백 루프 억제 */
#define DUTY_SLEW_PER_TICK 2 /* % per 20 ms tick → 100 % 변화에 1초 */
static uint8_t g_duty_now;
static void ambient_apply_duty(uint8_t target)
{
if (g_duty_now < target) g_duty_now = MIN(g_duty_now + DUTY_SLEW_PER_TICK, target);
else if (g_duty_now > target) g_duty_now = MAX(g_duty_now - DUTY_SLEW_PER_TICK, target);
led_set_duty(g_duty_now);
}
함정 — 안전 표시에는 slew를 적용하면 안 된다 Emergency가 발생했는데 밝기가 1초에 걸쳐 서서히 올라가면 반응이 늦다. 안전 표시는 즉시 최종 밝기로 점등해야 한다. slew는 일반 Ambient의 자동 조절에만 적용한다.
6-4. PWM 밝기와 인간의 지각
LED 밝기를 PWM 듀티로 조절할 때, 듀티 50 %가 체감 밝기 50 %가 아니다. 인간의 밝기 지각은 휘도의 세제곱근에 가깝다 — CIE의 명도 L* = 116·Y^(1/3) − 16(Y는 상대 휘도)이 그 모델이다.
| PWM 듀티 (≈상대 휘도 Y) | 체감 밝기 L* |
|---|---|
| 10 % | 약 38 |
| 25 % | 약 57 |
| 50 % | 약 76 |
| 100 % | 100 |
선형 듀티로 밝기 슬라이더를 만들면 사용자는 "아래쪽에서만 변하고 위쪽은 다 똑같다"고 느낀다. 감마 보정이 필요하다.
/* 지각 선형(L* 등간격) → PWM 듀티 변환
step k → L* = 10k → Y = ((L*+16)/116)^3 → duty = Y × 128 */
static const uint8_t LSTAR_LUT[11] = {
0, 1, 4, 8, 14, 24, 36, 52, 73, 98, 128 /* 0~10 단계 → 듀티(0~128) */
};
static uint8_t perceptual_to_duty(uint8_t step_0_10)
{
return LSTAR_LUT[step_0_10 > 10 ? 10 : step_0_10];
}
LUT를 쓰는 이유: pow()는 S32K144(Cortex-M4F, 단정밀도 FPU)에서도 비싸고, 11개 값이면 테이블이 훨씬 싸다. 밝기 단계가 이산적이므로 LUT로 충분하다. 표를 보면 아래쪽 단계 간 듀티 차이가 1~4에 불과하고 위쪽은 20~30씩 벌어진다 — 이게 선형 듀티 슬라이더가 "위쪽에서 변화가 안 느껴지는" 이유를 그대로 보여준다.
7. Ambient Lighting 동작
- 시스템은 결정된 Ambient Lighting 상태를 실제 조명 출력에 반영해야 한다.
한 문장이지만 05편의 Fan과 같은 요구-실제 분리를 함축한다. "결정된 상태"와 "실제 출력"이 별개 개념으로 존재한다.
8. 오류 및 안전 동작
- 시스템은 유효하지 않은 일반 Ambient Lighting 요청을 정상적인 조명 요청으로 실행하지 않아야 한다.
- 시스템은 Ambient Lighting 표시 판단에 필요한 통신 정보를 신뢰할 수 없는 경우 해당 정보에 기반한 새로운 조명 표시를 시작하지 않아야 한다.
- 시스템은 Ambient Lighting 출력 기능에 오류가 발생한 경우 해당 출력을 정상 동작 상태로 처리하지 않아야 한다.
- 시스템은 Ambient Lighting 관련 오류 상태를 차량 내 HMI 에서 사용자가 확인할 수 있도록 해야 한다.
8-1. 조명이 고장 나면 조명으로 알릴 수 없다
01편의 VSS와 같은 문제다.
§6.1.4 "시스템은 VSS 자체 오류(오디오 경로 또는 제어 이상)가 발생한 경우 차량 내 디스플레이(HMI)에 VSS 오류 상태를 전달하여 표시해야 한다." §6.4.3 "시스템은 Ambient Lighting 관련 오류 상태를 차량 내 HMI 에서 사용자가 확인할 수 있도록 해야 한다."
출력 채널이 고장 나면 다른 채널로 알린다. VSS는 HMI로, Ambient는 HMI로. HMI가 고장 나면? — §6.5가 다루지만, 결국 HMI는 최후의 알림 채널이므로 이중화가 어렵다. 실제 차량에서 계기판 경고등이 하드웨어 이중화되는 이유가 이것이다.
8-2. 조명 출력 오류를 어떻게 감지하는가
LED 스트립의 고장 모드:
| 고장 | 감지 방법 |
|---|---|
| 개방(open) — 배선 끊김, LED 소손 | 전류 감시 — 듀티를 줬는데 전류가 0 |
| 단락(short) — 배선 접촉 | 과전류 감지, 드라이버 IC 폴트 핀 |
| 부분 고장 — 일부 LED만 죽음 | 전류가 예상보다 낮음 (스트립 구조에 따라) |
| 드라이버 IC 고장 | 폴트 핀, I²C/SPI 통신 실패 |
/* 조명 출력 상태 확인 — 요구 듀티와 실측 전류의 관계 */
static bool ambient_output_healthy(uint8_t duty, uint16_t i_mA)
{
if (duty == 0) return (i_mA < LEAK_MAX_MA); /* 꺼졌는데 전류가 흐르면 단락 */
uint16_t expect = (uint16_t)((uint32_t)I_FULL_MA * duty / 100u);
uint16_t lo = (uint16_t)((uint32_t)expect * 60u / 100u);
uint16_t hi = (uint16_t)((uint32_t)expect * 140u / 100u);
return (i_mA >= lo && i_mA <= hi);
}
허용 범위를 넓게(±40 %) 잡는 이유: LED 순방향 전압은 온도와 개체차로 크게 변한다. 좁게 잡으면 정상 상태에서 오탐이 난다. 이건 잡아내려는 고장(개방=전류 0, 단락=과전류)이 극단적이라 넓은 창으로도 충분하기 때문에 가능한 선택이다.
9. 전체 통합 — 하나의 조명이 결정되는 과정
10. 한 줄 요약
| 항목 | 한 줄 |
|---|---|
| 일반 Ambient | 사용자 설정에 종속. 설정과 무관하게 Goodbye·Access 피드백·안전 표시는 수행 |
| Goodbye | 일시적, 자기 종료. 종료 후 이전 상태 복구가 아니라 최신 조건으로 재결정 |
| Smart Access 피드백 | 성공/실패를 구분되게 표시. VSS 소리와 짝을 이루는 다중 감각 피드백 |
| Warning/Fault/Emergency | 원인이 유효한 동안 유지. 사용자 ON/OFF·밝기 설정으로 무력화 불가 |
| 표시 우선순위 | Emergency > Fault > Warning > Access 피드백 > Goodbye > 일반. 선점된 일시적 표시는 재개 없음 |
| 조도 기반 밝기 | 일반 표시만 자동 조절. 안전 표시는 **식별 가능성**이 상실되지 않아야 (대비 기준) |
| 오류 | 조명 오류는 조명으로 알릴 수 없으므로 HMI로 알린다 |
설계 판단 체크포인트
구현에 들어가기 전에 팀이 답을 갖고 있어야 하는 것들이다. 답이 안 나오면 그 자리가 곧 설계 문의 또는 TBD 후보다.
-
사용자가 일반 Ambient Lighting 밝기를 최저로 낮췄다. Emergency 표시는 어떻게 되어야 하나? 최저 밝기로 표시되면 안 된다. §6.4.3은 "Warning, Fault 및 Emergency 표시가 일반 Ambient Lighting 의 ON/OFF 또는 밝기 설정에 의해 비활성화되지 않도록 해야 한다"와 "안전 관련 표시의 식별 가능성이 … 상실되지 않도록 해야 한다"를 둘 다 갖고 있다. 안전 표시는 별도의 밝기 경로를 타야 하며, 그 하한은 주변 조도에 따라 올라간다.
-
Goodbye Lighting이 Emergency에 선점당했다. Emergency 해제 후 남은 Goodbye 시간을 보여줘야 하나? 보여주지 않는다. §6.4.3은 "안전 관련 표시가 종료된 이후 이전에 중단된 일시적 Smart Access 시각 피드백 또는 Goodbye Lighting 을 자동으로 재개하지 않아야 한다"고 명시한다. 대신 "최신 차량 상태와 사용자 설정을 기준으로 다음 상태를 결정"한다.
-
밝기 자동 조절에 slew rate limit을 걸었다. Emergency 점등에도 적용하면 무엇이 문제인가? 위험 표시가 서서히 밝아져 인지가 늦어진다. 안전 표시는 즉시 최종 밝기로 점등해야 한다. slew는 일반 Ambient의 조도 기반 자동 조절에만 적용한다 — 거기서는 급변이 오히려 거슬리기 때문이다. 같은 메커니즘이 표시 종류에 따라 적용/미적용이 갈린다.
-
엔진룸 동물 진입을 Warning으로 볼지 Emergency로 볼지가 왜 TBD로 남았나? 기술 결정이 아니라 정책 결정이기 때문이다. 인명 위험은 아니지만(Emergency의 통상적 기준 미달) 시간이 촉박하고(Warning보다 급함), 차량 고장도 아니다(Fault 아님). TBD-034가 "별도 티어 여부"까지 열어 둔 것은 기존 3등급 체계에 안 맞을 가능성을 인정한 것이다. SR이 임의로 정하지 않고 열어 둔 게 옳은 판단이다.
-
조도 센서가 실내에 있으면 어떤 문제가 생기나? Ambient Lighting이 밝아지면 조도 센서가 그걸 읽어 "주변이 밝다"고 판단하고, 밝기를 더 올린다 — 양성 피드백으로 발산한다. §6.3.1이 "외부 조도를 측정해야 한다"고 명시한 이유 중 하나다. 실내 조도를 써야 한다면 자기 조명의 기여분을 모델링해 빼거나, 조명을 순간적으로 끄고 측정하는 방식이 필요하다.
-
우선순위 스택 구현에서 "선점" 함수를 따로 만들지 않은 것이 왜 요구사항 준수에 유리한가? 매 주기 최우선 항목을 다시 고르는 구조에서는 중단된 항목의 상태를 저장할 자리가 없다. 따라서 "자동 재개"를 구현하려면 새 자료구조를 추가해야 하고, 그건 코드 리뷰에서 눈에 띈다. 요구사항을
if문으로 방어하는 대신 어길 수 없는 구조를 택한 것이다. 반대로 명시적 선점/복원 스택을 쓰면 복원 코드가 자연스러워 보여 위반이 스며들기 쉽다.