初始导入:PhoneRail 五路手机直供电源板

原理图/PCB 已在 EasyEDA 里做到 12 页跨页 0 漏接,固件刚完成骨架包 A1。
这次是把已有的一切一次性纳入版本控制,没有改动任何设计内容。

纳入:
- 设计与需求文档、PROGRESS.md、小米8 参考原理图 PDF 与追脚脚本
- .claude/ 下的 EasyEDA 桥接工具链与几何/网表缓存(eda-parts.json、
  pcb-parts.json、pcb-pads.json、p2-ch1.json 等,是重画各页的唯一事实来源)
- 117 张验证截图(本项目所有方向问题都是靠人眼看截图发现的,属于证据不是垃圾)
- firmware/ 的 A1 骨架与 site/ 文档站源码

.gitignore 排除:构建产物(firmware/build、host/build、sdkconfig)、
site/node_modules、__pycache__、桥接日志 _bridge.log、脚本自标的 *.stale。

.gitattributes 默认 `* -text`:现有 .py/.json 大多是 CRLF,原样存,
不让 git 重写行尾。唯一例外是 *.sh 强制 LF —— 带 \r 的 shell 脚本 bash 跑不了。

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QYuExfgyYa6DzRRcUso8M8
这个提交包含在:
zhou yang
2026-09-06 18:18:14 +08:00
共同撰写人 Claude Opus 5
当前提交 2bfe308b30
共修改 526 个文件,包含 80633 行新增和 0 行删除
+376
查看文件
@@ -0,0 +1,376 @@
# -*- coding: utf-8 -*-
u"""载板 C4-SITE1 ~ C4-SITE5:每台手机一页。
一页里三块:
【A】电池排线 4 焊盘直焊座 J8n(P+/P−/TEMP/ID,2026-09-06 取代 TP8n1/2)+ TEMP 10k / ID 68k + §6.3 电容组(六颗)
【B】四根侧键顶针(PWR / VUP / VDN + J604 三键公共地)
【C】EDL:1.25mm 2P 座 + TS5A3157 模拟开关 + 100k 下拉 + 去耦
用法: /c/Python314/python .claude/c4_site.py [--push] [--only N]
所有引脚坐标都是**读回来再算线**的(skill §6b:create 的 rotation 与
setState_Rotation 差 180 度,电容跨距也各不相同)。EXPECT 里断言不过就直接终止。
"""
import json
import time
import os
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
from eda import run
SCH = '302e115180aaa8fc' # phonerail-carrier
LIB = '0819f05c4eef4c71ace90d822a990e87'
LIB_POGO = '543725e8f0454017a5691e7e570aeef6' # 个人库,顶针自建封装版在这里
P = {
# 2026-09-06 换矮封装(用户拍板):红宝石 6.3TPV2200M10X10.5,C3828985,SMD D10×L10.5,
# 液态铝电解 ESR 60mΩ@100kHz(§6.2 要 40~100,非聚合物)。立创库里这颗器件挂的是直插封装,
# 所以在个人库里建了「6.3TPV2200M10X10.5 SMD」:符号复制自立创,封装 = CAP-SMD_BD10.0-L10.3-W10.3-LS11.2-FD_H10.5
# 的副本(焊盘 1 = +,渲染图核过)。旧件 C49256788(10×16 插件)留作历史。
'C2200U': '4bb2e55e90d9441eb935a4b4af5beeed', # C3828985 2200uF/6.3V SMD 10x10.5(个人库 LIB_POGO)
'C2200U_OLD': '1477290f70a341ce8e1943e3cd679351', # C49256788 2200uF/10V 插件(作废)
'C100U': 'a110bb2c7fd046cdb078d7f64c0f9f04', # C15008 100uF/6.3V 1206
'C22U': '0de9d28bbcd34ca3bf2a4d78faaa50bb', # C12891 22uF/25V 1206
'C100N': '87bb635d0b2f489a9f60e7cd225beb3c', # C1525 100nF 0402
'TPRED': '17b5ed105c5b4fe180eee3ffb4702963', # C9900007423 红色测试点(2026-09-06 起不用)
'TPBLK': 'cf8b789614c04ee491fd17b89f81a8ab', # C9900007426 黑色测试点(2026-09-06 起不用)
# 2026-09-06 用户拍板:电池排线在保护板焊点处剪断,4 个焊盘直焊载板(设计文档 §13.5 末尾)。
# 个人库自建器件 MI8-BAT-FPC-4P(fp_batfpc.py):1=P+ 2=P− 3=TEMP 4=ID;保护板上的 NTC(10k) / ID(68k) 用固定电阻复制。
'BATFPC': '22b0c3130f0b4ff6baab529761dab3e4', # MI8-BAT-FPC-4P(个人库 LIB_POGO)
'R10K': 'c3b9baa5ef2e4070a4c0f9e9cd04fe6e', # C25744 10k 0402 R78n TEMP
'R68K': '94441cf3b58f4f16af17200285ebaf08', # C36871 68k 0402 R79n ID
# ⚠ 2026-09-04 改用**个人库自建封装版**:立创原封装 SMD-1_BD2.0-H3.6 的焊盘是 Ø2.5,
# 而小米8 四个侧键触点最近的一对(J601↔J604)只隔 2.217 mm —— 两个焊盘会叠在一起短路。
# 顶针法兰本身只有 Ø2.0,所以是立创把焊盘开大了。自建封装 POGO-SMD1P_D1.9 缩到 Ø1.9
# (净间距 0.317 mm,过 0.2 mm 规则)。见 .claude/pcb2_place.py 文件头。
'POGO': '1c8e0ea1e66142a8860d6812b1dbe356', # YTC1P-2510-01 D1.9(个人库,LIB_POGO)
'POGO_LCSC': '43b0bd715d444e1fab4580cb95b3686d', # 作废:C5221288 原件(焊盘 Ø2.5)
'CONN2P': '6461e6e5749d421bb997627984866425', # C225104 A1251WV-S-2P
'SW': '5acafd8eb5a641fcaf05190902930635', # C353034 TS5A3157DBVR
'R100K': 'acc294b6661948399f609b41c77ed19f', # C25741 100k 0402
}
# ---- 版面常量 ----
Y_VO, Y_GND = 720, 540 # 【A】两条母线
Y_CAP = 630 # 电容中心
CAP_X = [250, 350, 450, 550, 650, 750]
X_RAIL0, X_RAIL1 = 90, 940 # 2026-09-06 母线西端 150→90:给 J8n 的四条竖走廊让地方
J8 = (225, 630) # 排线直焊座:符号 4 脚朝**左**(实测 pin 在 cx−20)、1 在最上,放在第一颗电容左边
# 四脚各自的竖走廊:1(P+) 往上接 VO 母线,走最远的 100(它的竖线在所有横线之上,不会被穿);
# 往下走的 2/3/4 按「越低的越靠近引脚」排:2→130 落 GND 母线,3→170 进 R78n,4→190 进 R79n。
COL = {'1': 100, '2': 130, '3': 170, '4': 190}
R78, R79 = (170, 575), (190, 575) # TEMP / ID 电阻(竖放),下端直接落在 GND 母线上
POGO_X = [220, 320, 420, 520] # 【B】四根顶针的元件中心
Y_POGO = 380
POGO_PORT_Y = [330, 300, 270, 240]
J6, U4 = (660, 400), (880, 400) # 【C】
R74, C76 = (975, 370), (1060, 370)
CAPS = [('C2200U', u'2200µF/6.3V'), ('C100U', u'100µF'), ('C100U', u'100µF'),
('C22U', u'22µF'), ('C22U', u'22µF'), ('C100N', u'100nF')]
def call(js, timeout=180):
r = run(js, timeout=timeout)
if not r.get('success'):
raise RuntimeError(json.dumps(r, ensure_ascii=False)[:800])
return r['result']
def names(n):
base = 801 + (n - 1) * 10
return {
'caps': ['C%d' % (base + i) for i in range(6)],
'j8': 'J8%d' % n, 'r78': 'R78%d' % n, 'r79': 'R79%d' % n,
'pogo': ['J%d' % (41 + (n - 1) * 4 + i) for i in range(4)],
'j6': 'J6%d' % n, 'u4': 'U4%d' % n, 'r74': 'R74%d' % n, 'c76': 'C76%d' % n,
}
def placement(n):
d = names(n)
out = [[d['j8'], P['BATFPC'], J8[0], J8[1], 0, LIB_POGO],
[d['r78'], P['R10K'], R78[0], R78[1], 270, LIB],
[d['r79'], P['R68K'], R79[0], R79[1], 270, LIB]]
for i, (key, _v) in enumerate(CAPS):
# C80x1 是电解,pin1 = 正极,必须朝上接 VOn_P。
# 实测这颗 2200µF(C49256788)在 create() 里传 270 得到 pin1 在**下**,
# 和 skill §6 那张表相反 —— 所以传 90。下面 EXPECT 会再断言一次。
rot = 90 if key == 'C2200U' else 270
out.append([d['caps'][i], P[key], CAP_X[i], Y_CAP, rot, LIB_POGO if key == 'C2200U' else LIB])
for i, x in enumerate(POGO_X):
out.append([d['pogo'][i], P['POGO'], x, Y_POGO, 0, LIB_POGO])
out += [[d['j6'], P['CONN2P'], J6[0], J6[1], 0, LIB],
[d['u4'], P['SW'], U4[0], U4[1], 0, LIB],
[d['r74'], P['R100K'], R74[0], R74[1], 270, LIB],
[d['c76'], P['C100N'], C76[0], C76[1], 270, LIB]]
# ⚠️ 每条必须是 6 项。少了 lib 那一项 -> `create({libraryUuid: undefined, uuid})`
# **永远不 resolve、也不抛错**,表现成「桥接 30 秒超时」,极难往这里想。
for it in out:
assert len(it) == 6 and it[5], u'placement 少了 libraryUuid: %s' % (it,)
return out
def wiring(n, pins):
u"""pins: {位号: {引脚号: (x, y)}},全部是读回来的真实坐标。"""
d = names(n)
W, F, PT, TX = [], [], [], []
def w(*p):
for i in range(0, len(p) - 2, 2):
W.append([p[i], p[i + 1], p[i + 2], p[i + 3]])
# ---------- 【A】P+/P- 与电容组 ----------
w(X_RAIL0, Y_VO, X_RAIL1, Y_VO)
PT.append(['BI', 'VO%d_P' % n, X_RAIL1, Y_VO, 0])
w(X_RAIL0, Y_GND, 900, Y_GND, 900, 510)
F.append(['Ground', 'GND', 900, 510, 0])
# 排线直焊座 J8n:4 脚朝左、1 在最上(build_page 里断言过)。每脚先横走到自己的竖走廊再上下落(COL 的注释)。
jp = pins[d['j8']]
for k in ('1', '2', '3', '4'):
px, py = jp[k]
assert px > max(COL.values()), u'%s.%s 引脚 x=%d 太靠左(走廊到 %d)' % (d['j8'], k, px, max(COL.values()))
w(px, py, COL[k], py)
w(COL['1'], jp['1'][1], COL['1'], Y_VO)
w(COL['2'], jp['2'][1], COL['2'], Y_GND)
for k, rn in (('3', d['r78']), ('4', d['r79'])):
rp = pins[rn]
rtop = max(rp.values(), key=lambda v: v[1])
rbot = min(rp.values(), key=lambda v: v[1])
assert rtop[0] == COL[k], u'%s 不在走廊 x=%d 上(实测 %s)' % (rn, COL[k], rtop)
w(COL[k], jp[k][1], COL[k], rtop[1])
w(rbot[0], rbot[1], rbot[0], Y_GND) # 直接落在 GND 母线上(T 型连接)
for i in range(6):
pm = pins[d['caps'][i]]
top = max(pm.values(), key=lambda v: v[1])
bot = min(pm.values(), key=lambda v: v[1])
if i == 0: # 电解:pin1 = 正极,必须是接 VOn_P 的那一头(换了符号 rot 语义可能变,读回来断言)
assert pm['1'][1] > pm['2'][1], u'%s 的 pin1(+) 不在上面: %s' % (d['caps'][0], pm)
w(top[0], top[1], top[0], Y_VO)
w(bot[0], bot[1], bot[0], Y_GND)
# ---------- 【B】侧键顶针 ----------
labels = ['POGO%d_PWR' % n, 'POGO%d_VUP' % n, 'POGO%d_VDN' % n, None]
for i in range(4):
px, py = pins[d['pogo'][i]]['1']
yy = POGO_PORT_Y[i]
w(px, py, px, yy)
if labels[i]:
PT.append(['BI', labels[i], px, yy, 180])
else:
F.append(['Ground', 'GND', px, yy, 0])
# ---------- 【C】EDL ----------
j, u = pins[d['j6']], pins[d['u4']]
no, gnd, com = u['1'], u['2'], u['4']
inn, vp = u['6'], u['5']
s1, s2, t3, t4 = j['1'], j['2'], j['3'], j['4']
# 模拟开关的两个通路端分别在符号两侧,只能一上一下绕,两条走廊错开且互不交叉
w(no[0], no[1], no[0], 490, 530, 490, 530, s1[1], s1[0], s1[1])
w(com[0], com[1], com[0], 300, 560, 300, 560, s2[1], s2[0], s2[1])
w(gnd[0], gnd[1], 825, gnd[1], 825, 350)
F.append(['Ground', 'GND', 825, 350, 0])
# 1.25mm 座的两个固定脚接地(和 C3-USB 上的 J17 一致)
w(t3[0], t3[1], 600, t3[1], 600, 445)
F.append(['Ground', 'GND', 600, 445, 180])
w(t4[0], t4[1], 600, t4[1], 600, 355)
F.append(['Ground', 'GND', 600, 355, 0])
# V+ 往下走,避开 IN 那条横线
w(vp[0], vp[1], 945, vp[1], 945, 340)
F.append(['Power', '+3V3_HUB', 945, 340, 180])
# IN <- EDL_ENn,中途接 100k 下拉
w(inn[0], inn[1], 1000, inn[1])
PT.append(['BI', 'EDL_EN%d' % n, 1000, inn[1], 0])
rp = pins[d['r74']]
rtop = max(rp.values(), key=lambda v: v[1])
rbot = min(rp.values(), key=lambda v: v[1])
w(rtop[0], inn[1], rtop[0], rtop[1])
w(rbot[0], rbot[1], rbot[0], 320)
F.append(['Ground', 'GND', rbot[0], 320, 0])
cp = pins[d['c76']]
ctop = max(cp.values(), key=lambda v: v[1])
cbot = min(cp.values(), key=lambda v: v[1])
w(ctop[0], ctop[1], ctop[0], 420)
F.append(['Power', '+3V3_HUB', ctop[0], 420, 0])
w(cbot[0], cbot[1], cbot[0], 320)
F.append(['Ground', 'GND', cbot[0], 320, 0])
TX += [
(45, 806, u'【CH%d 手机位】P+/P− 焊盘 + 电容组 + 侧键顶针 + EDL' % n, 13),
(45, 788, u'【A】%s:原厂电池排线在保护板焊点处剪断,4 个焊盘直焊(1=P+ 2=P− 3=TEMP 4=ID,间距 2.0)。保护板整块不要(§13.5 末尾)。'
% names(n)['j8'], 10),
(45, 772, u' %s 10k 代替保护板上的 NTC(模拟 25 °C),%s 68k 照抄保护板上的 ID 电阻,都对 P−。'
% (names(n)['r78'], names(n)['r79']), 10),
(45, 470, u'【B】四根顶针顶主板反面的 J601~J604(§13.3)。J604 是三键公共地,', 10),
(45, 454, u' 直接接本路的电源返回地。驱动 MOS 在下板,这里只有顶针。', 10),
(45, 438, u'顶针 = YTC1P-2510-01(立创 C5221288):自由高 3.5 / 行程 0.8 / 压到底 2.7 mm。', 10),
(45, 422, u' 配 3.0mm 立柱 → 压缩 0.5mm(62% 行程,约 62g),屏蔽罩净空 1.6mm。', 10),
(45, 406, u' 冠径只有 Ø0.9 球头,±1mm 落点公差靠首件实测校正(§22.3c / §22.3e)。', 10),
(45, 205, u'【C】%s 是 1.25mm 2P 座,两根线焊到主板正面的 TP1204 / TP1205(§22.4)。'
% names(n)['j6'], 10),
(45, 189, u' %s 导通即短接两点 → 手机进 9008。串了 1.8k,短路电流只有 1mA。'
% names(n)['u4'], 10),
(45, 173, u' %s 100k 下拉紧挨开关放:ESP32 复位期间 74HC595 输出高阻,'
% names(n)['r74'], 10),
(45, 157, u' 靠它保证手机不会被误拉进 9008(§8.1 复位安全)。', 10),
]
# 所有导线必须是水平或垂直、且非零长度 —— 斜线会被 create() 直接拒掉,
# 零长度会变成 skill §8a 那种「看着连上、重画就断」的隐形漏接。
# 另外:旗/端口的坐标会被吸附到 5 的倍数,导线端点不会。两边差 2 个单位,
# 旗就落在导线中段 = 真断线,而 verify_sch 和 DRC 都看不出来
# (C1-CONN 的 GND 旗写成 y=362 就是这么断的,只有 port_audit 抓得到)。
for a, b, c, e in W:
assert (a == c) != (b == e), u'CH%d 有一段不是横平竖直:%s' % (n, (a, b, c, e))
for v in (a, b, c, e):
assert v % 5 == 0, u'CH%d 导线坐标 %d 不是 5 的倍数:%s' % (n, v, (a, b, c, e))
for kind, lst in (('flag', F), ('port', PT)):
for it in lst:
assert it[2] % 5 == 0 and it[3] % 5 == 0, u'CH%d 的 %s %s 坐标 (%d,%d) 不是 5 的倍数' % (n, kind, it[1], it[2], it[3])
return W, F, PT, TX
def build_page(n, push):
d = names(n)
pname = 'C4-SITE%d' % n
if not push:
return
page = call("""
const info = await eda.dmt_Schematic.getSchematicInfo('%s');
for (const p of (info.page||[])) if (p.name==='%s') return p.uuid;
const u = await eda.dmt_Schematic.createSchematicPage('%s');
await eda.dmt_Schematic.modifySchematicPageName(u,'%s');
return u;
""" % (SCH, pname.lower(), SCH, pname))
call("""
await eda.dmt_EditorControl.openDocument('%s'); await new Promise(r=>setTimeout(r,900));
const n=(await eda.dmt_Schematic.getCurrentSchematicPageInfo()).name;
if (n!=='%s') throw new Error('page='+n);
for (const c of await eda.sch_PrimitiveComponent.getAll()){
if (c.getState_ComponentType()==='sheet') continue;
await eda.sch_PrimitiveComponent.delete(c.getState_PrimitiveId());
}
for (const w of await eda.sch_PrimitiveWire.getAll()) await eda.sch_PrimitiveWire.delete(w.getState_PrimitiveId());
for (const t of await eda.sch_PrimitiveText.getAll()) await eda.sch_PrimitiveText.delete(t.getState_PrimitiveId());
return 1;
""" % (page, pname))
place = placement(n)
# 分两段:创建走「甩出去 + 轮询」绕开桥接的 30 秒单次上限(16 个件逐个联网取
# 库时会撑爆),认领位号留在正常的 await 请求里(快,且要读回全部元件比对坐标)。
# ⚠️ 这个模式只对「慢」有效。2026-09-04 曾经卡在 done=0 不动,真因是
# `placement()` 里那两个测试点漏了 libraryUuid —— `create({libraryUuid: undefined})`
# **永远不 resolve 也不报错**,轮询看起来和卡死一样。见 skill §9c-2 与上面的断言。
call("""
globalThis.__pl = {done: 0, err: null, n: %d};
(async () => {
for (const [d, uuid, x, y, rot, lib] of %s) {
try { await eda.sch_PrimitiveComponent.create({libraryUuid: lib, uuid}, x, y, undefined, rot, false, true, true); }
catch (e) { globalThis.__pl.err = d + ': ' + String(e).slice(0, 150); }
globalThis.__pl.done++;
}
})();
return 'fired';
""" % (len(place), json.dumps(place)))
for _ in range(150):
st = call("return globalThis.__pl;")
if st['err']:
raise SystemExit(u'%s 放置失败: %s' % (pname, st['err']))
if st['done'] >= st['n']:
break
time.sleep(2)
else:
raise SystemExit(u'%s 放置超时,只完成 %d/%d' % (pname, st['done'], st['n']))
# 按坐标认领位号(本页每个件的 (x,y) 都是唯一的)
named = call("""
const P = %s;
let k = 0;
const cs = (await eda.sch_PrimitiveComponent.getAll()).filter(c => c.getState_ComponentType() === 'part');
for (const [d, uuid, x, y] of P) {
for (const c of cs) {
if (Math.round(c.getState_X()) !== x || Math.round(c.getState_Y()) !== y) continue;
await c.toAsync().setState_Designator(d).done();
k++; break;
}
}
return k;
""" % json.dumps([[d, u, x, y] for d, u, x, y, r, l in place]))
if named != len(place):
raise SystemExit(u'%s 只认领到 %d/%d 个位号' % (pname, named, len(place)))
raw = call("""
const out={};
for (const c of await eda.sch_PrimitiveComponent.getAll()){
if (c.getState_ComponentType()!=='part') continue;
out[c.getState_Designator()] = (await c.getAllPins()).map(
p=>[p.getState_PinNumber(), (p.getState_PinName?p.getState_PinName():''),
Math.round(p.getState_X()), Math.round(p.getState_Y())]);
}
return out;
""")
pins = {k: {p: (x, y) for p, _n, x, y in v} for k, v in raw.items()}
pnames = {k: {p: _n for p, _n, x, y in v} for k, v in raw.items()}
bad = []
jp = pins[d['j8']]
if sorted(jp) != ['1', '2', '3', '4']:
bad.append(u'%s 引脚号 %s 不是 1..4' % (d['j8'], sorted(jp)))
elif not (jp['1'][1] > jp['2'][1] > jp['3'][1] > jp['4'][1]) or len({v[0] for v in jp.values()}) != 1 or jp['1'][0] >= J8[0]:
bad.append(u'%s 引脚要 4 脚朝左、1 在最上,实测 %s' % (d['j8'], jp))
# 电解电容 pin1 = 正极,必须在上(接 VOn_P)
c801 = pins[d['caps'][0]]
if c801['1'][1] <= c801['2'][1]:
bad.append(u'%s 是电解电容,pin1(正极) 必须在上,实测 %s' % (d['caps'][0], c801))
# 模拟开关引脚名要对上 NO/GND/NC/COM/V+/IN
want = {'1': 'NO', '2': 'GND', '3': 'NC', '4': 'COM', '5': 'V+', '6': 'IN'}
got = pnames[d['u4']]
for k, v in want.items():
if got.get(k, '').upper().replace('VCC', 'V+') != v:
bad.append(u'%s.%s 引脚名期望 %s 实测 %s' % (d['u4'], k, v, got.get(k)))
if bad:
raise SystemExit(pname + u' 引脚核对失败:\n ' + '\n '.join(bad))
W, F, PT, TX = wiring(n, pins)
def batch(items, size, js):
for i in range(0, len(items), size):
call(js % json.dumps(items[i:i + size], ensure_ascii=False))
batch(W, 40, "for (const s of %s) await eda.sch_PrimitiveWire.create(s); return 1;")
batch(PT, 25, "for (const p of %s) await eda.sch_PrimitiveComponent.createNetPort(p[0],p[1],p[2],p[3],p[4],false); return 1;")
batch(F, 25, "for (const f of %s) await eda.sch_PrimitiveComponent.createNetFlag(f[0],f[1],f[2],f[3],f[4],false); return 1;")
batch([list(t) for t in TX], 15,
"for (const t of %s) await eda.sch_PrimitiveText.create(t[0],t[1],t[2],0,null,null,t[3],false,false,false,0); return 1;")
print(u'%s: 元件 %d 导线 %d 旗 %d 端口 %d 文字 %d (%s)'
% (pname, len(place), len(W), len(F), len(PT), len(TX), page))
return page
def main():
push = '--push' in sys.argv
only = None
if '--only' in sys.argv:
only = int(sys.argv[sys.argv.index('--only') + 1])
sites = [only] if only else [1, 2, 3, 4, 5]
if not push:
for n in sites:
print(u'C4-SITE%d 元件 %d 个' % (n, len(placement(n))))
print(u'(未推送,加 --push)')
return
nc = {}
for n in sites:
build_page(n, True)
nc['U4%d' % n] = ['3'] # 模拟开关的 NC 端不用
ncf = os.path.join(HERE, 'c4-nets-nc.json')
old = json.load(open(ncf, encoding='utf-8')) if os.path.exists(ncf) else {}
old.update(nc)
json.dump(old, open(ncf, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
if __name__ == '__main__':
main()