LabHub
배우기 러닝패스 코스

The Sensor Is Fine. The Board Cannot Understand It

Recovering the Baud Rate from Garbled Characters

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

포착된 표본 열에서 UART 프레임을 직접 해독하고, 송신 클럭이 어긋난 포착에서 실제 비트 시간을 되재어 읽히는 문장을 되살립니다. 그 과정에서 패리티와 프레이밍 오류가 각각 무엇을 잡고 무엇을 놓치는지 숫자로 셉니다.

왜 중요한가

UART 에는 클럭 선이 없습니다. 양쪽이 같은 비트 시간을 믿고 있어야 하는데 그 믿음을 확인할 방법이 프레임마다 한 번, 시작 비트뿐입니다. 그래서 클럭이 어긋나면 프레임 뒤쪽부터 무너지고, 증상은 "글자가 깨진다" 로만 올라옵니다. 이 실습은 그 신고를 몇 퍼센트 어긋났는가라는 숫자로 바꾸는 연습입니다. 값을 고치는 것이 아니라 시간 축을 고친다는 점이 핵심이고, 그 시간 축의 근거는 포착 파일 안에 이미 들어 있습니다.

단계

  1. 표본 열에 시간 축을 세운다 — 머리말의 표본 속도로 표본 번호를 시각으로 바꾸고 개요를 남깁니다.
  2. 임계값을 올리면 모서리가 움직인다 — rising_edges 를 구현하고 두 임계값의 검출 시각 차이를 잽니다.
  3. 프레임 해독기를 쓴다 — 시작 모서리에서 (k + 0.5) 비트 시간 떨어진 표본을 읽는 decode 를 구현합니다.
  4. 정상 포착을 읽는다 — 프레이밍 오류 0으로 문장을 얻습니다.
  5. 표본을 솎아 판독이 무너지는 곳을 찾는다 — 1·4·10·25·50 간격으로 다시 해독합니다.
  6. 명목 보율로 읽었을 때의 증상 — 어긋난 포착의 프레이밍 오류와 가장 짧은 펄스를 잽니다.
  7. 비트 시간을 되재어 문장을 되살린다 — 거친 값에서 시작해 긴 기준선으로 좁힙니다.
  8. 패리티가 놓친 것을 센다 — 조용히 틀린 프레임이 몇 개인지 셉니다.

참고

표본 열에 시간 축을 세운다

mkdir -p /root/bus-uart 로 작업 폴더를 만들고, /opt/fixtures/serialbus/uart/hello-9600.csv 를 읽어 /root/bus-uart/survey.json 에 sample_rate_hz(머리말에서 읽은 값), sample_count(표본 행 수), duration_us(표본 수 x 1,000,000 / 표본 속도), min_v, max_v, idle_level(첫 표본이 1.65 V 이상이면 "high", 아니면 "low")을 저장하세요.

머리말은 # 로 시작하는 줄이고 sample_rate_hz 가 거기 있습니다. 그다음 줄이 열 이름이고 그 아래가 표본입니다. 시각은 파일에 없고 표본 번호와 표본 속도로 계산합니다.

임계값을 올리면 모서리가 움직인다

/root/bus-uart/edges.py 에 rising_edges(volts, threshold_v) 를 구현하세요. volts[i-1] 이 임계값 미만이고 volts[i] 가 임계값 이상인 i 들을 오름차순 목록으로 돌려줍니다(i 는 1부터). 그리고 hello-9600.csv 에 임계값 1.65 V 와 2.90 V 를 각각 적용해 /root/bus-uart/threshold.json 에 count_1v65, count_2v90, first_1v65, first_2v90, shift_samples(first_2v90 빼기 first_1v65), shift_us 를 저장하세요.

모서리가 유한한 기울기를 가지면 높은 임계값은 더 늦게 넘어섭니다. 두 임계값의 개수가 같은데 시각만 다르다는 점을 확인하세요. 채점기는 포착 파일과 무관한 짧은 표로도 이 함수를 시험합니다.

프레임 해독기를 쓴다

/root/bus-uart/uart.py 에 decode(volts, sample_rate_hz, baud, threshold_v=1.65, parity=None) 을 구현하세요. 유휴(높음)에서 시작 비트로 떨어지는 모서리 i 를 찾고, i 에서 (k + 0.5) 비트 시간 떨어진 표본을 int() 로 잘라 k번 비트로 읽습니다. 데이터는 8비트이고 낮은 자리부터입니다. 프레임 하나를 {"start": i, "value": 바이트, "framing_ok": 정지비트가 1인가, "parity_ok": 패리티가 맞는가} 로 담아 목록으로 돌려주고, 정지 비트를 읽은 표본 바로 다음부터 다음 모서리를 찾습니다. parity 는 None 또는 "even" 입니다.

비트당 표본 수는 sample_rate_hz / baud 이며 정수가 아닙니다. parity 가 None 이면 parity_ok 는 언제나 참입니다. 채점기가 손으로 만든 시험표(정상 프레임, 정지 비트가 0인 프레임, 패리티 비트만 뒤집은 프레임)로 먼저 확인합니다.

정상 포착을 읽는다

hello-9600.csv 를 9600 Bd 로 해독해 /root/bus-uart/message.json 에 frame_count, framing_errors, bytes(정수 목록), text(바이트를 그대로 글자로 이은 문자열)를 저장하세요.

프레이밍 오류가 0이어야 정상입니다. 0이 아니면 비트당 표본 수나 시작 모서리를 찾는 조건을 다시 보세요.

표본을 솎아 판독이 무너지는 곳을 찾는다

hello-9600.csv 의 표본 열을 1, 4, 10, 25, 50 간격으로 솎아(volts[::k]) 각각 표본 속도를 rate // k 로 두고 9600 Bd 로 다시 해독하세요. /root/bus-uart/sampling.json 에 cases 를 이 순서 그대로 5개 담고, 각 항목에 decimate, sample_rate_hz, samples_per_bit, text_ok(솎지 않은 결과와 문자열이 같은가)를 넣으세요. 그리고 처음으로 text_ok 가 거짓이 되는 간격을 first_failing_decimate 에 적으세요.

비트당 표본 수가 1에 가까워지면 해독기는 오류를 내지 않고 다른 글자를 냅니다. text_ok 는 불리언이어야 하고 문자열 "true" 가 아닙니다.

명목 보율로 읽었을 때의 증상

/opt/fixtures/serialbus/uart/drift-8n1.csv 를 9600 Bd 로 해독해 /root/bus-uart/drift.json 에 frame_count, framing_errors, bytes 를 저장하세요. 여기에 더해, 준위가 바뀌는 표본 번호들을 모아 이웃 간격의 최솟값을 구하고 shortest_pulse_us 로, 1,000,000 / 9600 을 nominal_bit_time_us 로, 둘의 비를 ratio 로 적으세요.

가장 짧은 간격은 비트 하나입니다. 그 값이 명목 비트 시간과 몇 퍼센트 다른지가 이 포착의 진단입니다. 프레임 수는 정상 포착과 비슷한데 프레이밍 오류만 늘어난다는 점을 확인하세요.

비트 시간을 되재어 문장을 되살린다

drift-8n1.csv 에서 비트 시간을 되재세요. 먼저 모서리 간격의 최솟값을 거친 값으로 잡고, 그다음 기준선을 늘려 가며 좁힙니다 — 첫 모서리에서 (지금 추정값 x 16) 표본 안에 드는 가장 먼 모서리를 골라 그 거리를 추정값으로 나눠 반올림한 정수로 다시 나누고, 배수를 4배씩 키워 마지막 모서리까지 되풀이합니다. 결과를 /root/bus-uart/measured.json 에 bit_time_us, baud(1,000,000 나누기 bit_time_us), 그 보율로 다시 해독한 text, framing_errors 로 저장하세요.

한 번에 전체 길이로 반올림하면 안 됩니다 — 거친 값의 오차에 비트 개수를 곱한 값이 0.5비트를 넘으면 엉뚱한 정수를 고릅니다. 거친 값만으로도 글자는 읽히지만 채점은 0.2 % 안쪽을 요구하므로 좁히는 단계를 건너뛸 수 없습니다.

패리티가 놓친 것을 센다

/opt/fixtures/serialbus/uart/drift-8e1.csv 를 짝수 패리티로 두 번 해독하세요 — 한 번은 되잰 비트 시간으로(이쪽이 참값), 한 번은 9600 Bd 로. /root/bus-uart/parity.json 에 frames, parity_errors, framing_errors, flagged(둘 중 하나라도 걸린 프레임 수), wrong_bytes(참값과 값이 다른 프레임 수), silently_wrong(값이 다른데 패리티도 프레이밍도 통과한 프레임 수), text_at_measured 를 적으세요.

패리티는 1의 개수의 홀짝만 봅니다. 짝수 개가 뒤집히면 그대로 통과하므로 flagged 가 wrong_bytes 보다 작을 수 있습니다. 두 해독 결과를 프레임 순서대로 짝지어 견주세요.