chore: update code
This commit is contained in:
+130
-130
@@ -18,7 +18,7 @@
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-13a1 1 0 10-2 0 1 1 0 002 0zM2 10a1 1 0 011-1h1a1 1 0 110 2H3a1 1 0 01-1-1zm14 0a1 1 0 011-1h1a1 1 0 110 2h-1a1 1 0 01-1-1zM5.05 5.05a1 1 0 011.414 0l.707.707a1 1 0 11-1.414 1.414l-.707-.707a1 1 0 010-1.414zm9.9 9.9a1 1 0 011.414 0l.707.707a1 1 0 11-1.414 1.414l-.707-.707a1 1 0 010-1.414zM5.05 14.95a1 1 0 010-1.414l.707-.707a1 1 0 011.414 1.414l-.707.707a1 1 0 01-1.414 0zm9.9-9.9a1 1 0 010-1.414l.707-.707a1 1 0 011.414 1.414l-.707.707a1 1 0 01-1.414 0z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
<svg v-else class="w-5 h-5 text-slate-700" fill="currentColor" viewBox="0 0 20 20">
|
||||
<svg v-else class="w-5 h-5 text-slate-800" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
|
||||
</svg>
|
||||
</button>
|
||||
@@ -29,7 +29,7 @@
|
||||
class="lg:hidden flex border-b border-slate-200 dark:border-gray-800 bg-white dark:bg-dark z-10 flex-shrink-0">
|
||||
<button @click="mobileTab = 'settings'" :class="mobileTab === 'settings'
|
||||
? 'border-b-2 role-border-primary role-text-primary bg-transparent'
|
||||
: 'text-slate-500 dark:text-gray-400 hover:text-slate-700 dark:hover:text-gray-200'"
|
||||
: 'text-slate-600 dark:text-gray-400 hover:text-slate-800 dark:hover:text-gray-200'"
|
||||
class="flex-1 flex items-center justify-center gap-2 py-3 text-sm font-semibold transition-colors">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
@@ -41,7 +41,7 @@
|
||||
</button>
|
||||
<button @click="mobileTab = 'chart'" :class="mobileTab === 'chart'
|
||||
? 'border-b-2 role-border-primary role-text-primary bg-transparent'
|
||||
: 'text-slate-500 dark:text-gray-400 hover:text-slate-700 dark:hover:text-gray-200'"
|
||||
: 'text-slate-600 dark:text-gray-400 hover:text-slate-800 dark:hover:text-gray-200'"
|
||||
class="flex-1 flex items-center justify-center gap-2 py-3 text-sm font-semibold transition-colors">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
@@ -60,7 +60,7 @@
|
||||
<!-- 濾波器設計工具 -->
|
||||
<section>
|
||||
<h2
|
||||
class="text-sm font-semibold text-slate-500 dark:text-gray-400 uppercase tracking-wider mb-3 flex items-center justify-between">
|
||||
class="text-sm font-semibold text-slate-600 dark:text-gray-400 uppercase tracking-wider mb-3 flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
@@ -74,11 +74,11 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<button @click="writeToMCU"
|
||||
:disabled="writingMCU || !mcuConnected"
|
||||
class="primary-action text-[10px] font-bold role-bg-primary role-hover-primary role-text-on-fill role-border-primary border px-2 py-1 rounded transition-colors uppercase disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
class="primary-action text-[10px] font-bold role-bg-primary role-hover-primary role-text-on-fill role-border-primary border px-3 py-1 rounded-full transition-colors uppercase disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
{{ writingMCU ? '寫入中' : '寫值到 MCU' }}
|
||||
</button>
|
||||
<button @click="resetFilterParams"
|
||||
class="text-[10px] font-bold role-bg-primary-soft role-border-primary-soft role-text-primary role-hover-primary-soft border px-2 py-1 rounded transition-colors uppercase">重設設計</button>
|
||||
class="text-[10px] font-bold border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 px-3 py-1 rounded-full transition-colors uppercase">重設設計</button>
|
||||
</div>
|
||||
</h2>
|
||||
<div class="mb-3 space-y-2">
|
||||
@@ -93,7 +93,7 @@
|
||||
MCU 已連線
|
||||
</span>
|
||||
<button @click="disconnectMCUPort"
|
||||
class="text-[10px] font-bold bg-slate-200 dark:bg-gray-800 text-slate-600 dark:text-gray-300 hover:bg-slate-300 dark:hover:bg-gray-700 px-2 py-2 rounded transition-colors uppercase">
|
||||
class="text-[10px] font-bold bg-slate-200 dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-300 dark:hover:bg-gray-700 px-2 py-2 rounded transition-colors uppercase">
|
||||
斷線
|
||||
</button>
|
||||
</template>
|
||||
@@ -101,7 +101,7 @@
|
||||
<p v-if="!webSerialSupported" class="text-xs text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg px-3 py-2">
|
||||
⚠ 此瀏覽器不支援 Web Serial。MCU 寫值功能需使用 Chrome 或 Edge,且透過 HTTPS 連線。
|
||||
</p>
|
||||
<p v-if="mcuStatus" class="text-xs text-slate-500 dark:text-gray-400">{{ mcuStatus }}</p>
|
||||
<p v-if="mcuStatus" class="text-xs text-slate-600 dark:text-gray-400">{{ mcuStatus }}</p>
|
||||
</div>
|
||||
<select v-model="filterType" @change="onFilterTypeChange"
|
||||
class="w-full bg-slate-50 dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-base role-focus-primary focus:ring-1 role-focus-ring-primary outline-none transition-all mb-3 text-slate-900 dark:text-gray-100 truncate">
|
||||
@@ -113,48 +113,48 @@
|
||||
class="space-y-3 bg-slate-100 dark:bg-gray-900 p-4 rounded-lg border border-slate-200 dark:border-gray-800 shadow-inner">
|
||||
<!-- Lowpass / Highpass -->
|
||||
<div v-if="['Lowpass (低通)', 'Highpass (高通)'].includes(filterType)">
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 flex justify-between mb-1"><span>截止頻率
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 flex justify-between mb-1"><span>截止頻率
|
||||
f_c (Hz)</span><span class="role-text-primary">{{
|
||||
formatK(params.fc) }}</span></label>
|
||||
<input type="range" v-model.number="params.fc" @input="debouncedApply" :min="1" :max="fs/2"
|
||||
class="w-full role-range-primary mb-2">
|
||||
<input type="number" v-model.number="params.fc" @change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base role-focus-primary outline-none text-slate-900 dark:text-gray-100">
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 mt-3 mb-2 block">階數 Order</label>
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 mt-3 mb-2 block">階數 Order</label>
|
||||
<div class="selector-button-group flex gap-2">
|
||||
<button v-for="o in [1,2,3]" :key="o" @click="params.order = o; applyFilterDesign()"
|
||||
:class="params.order === o ? 'primary-action role-bg-primary role-text-on-fill role-border-primary' : 'bg-slate-200 dark:bg-gray-800 text-slate-600 dark:text-gray-400 border-slate-300 dark:border-gray-700'"
|
||||
:class="params.order === o ? 'primary-action role-bg-primary role-text-on-fill role-border-primary' : 'bg-slate-200 dark:bg-gray-800 text-slate-700 dark:text-gray-400 border-slate-300 dark:border-gray-700'"
|
||||
class="selector-button flex-1 rounded py-2.5 text-base font-medium border transition-colors">{{ o
|
||||
}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Bandpass -->
|
||||
<div v-if="filterType === 'Bandpass (帶通)'">
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 flex justify-between mb-1"><span>下截止頻率
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 flex justify-between mb-1"><span>下截止頻率
|
||||
f_low (Hz)</span><span class="role-text-primary">{{
|
||||
formatK(params.bp_f_low) }}</span></label>
|
||||
<input type="number" v-model.number="params.bp_f_low" @change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base mb-2 text-slate-900 dark:text-gray-100">
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 flex justify-between mb-1"><span>上截止頻率
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 flex justify-between mb-1"><span>上截止頻率
|
||||
f_high (Hz)</span><span class="role-text-primary">{{
|
||||
formatK(params.bp_f_high) }}</span></label>
|
||||
<input type="number" v-model.number="params.bp_f_high" @change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base mb-2 text-slate-900 dark:text-gray-100">
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 mt-2 mb-2 block">階數 Order</label>
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 mt-2 mb-2 block">階數 Order</label>
|
||||
<div class="selector-button-group flex gap-2">
|
||||
<button v-for="o in [1,2,3]" :key="o" @click="params.bp_order = o; applyFilterDesign()"
|
||||
:class="params.bp_order === o ? 'primary-action role-bg-primary role-text-on-fill role-border-primary' : 'bg-slate-200 dark:bg-gray-800 text-slate-600 dark:text-gray-400 border-slate-300 dark:border-gray-700'"
|
||||
:class="params.bp_order === o ? 'primary-action role-bg-primary role-text-on-fill role-border-primary' : 'bg-slate-200 dark:bg-gray-800 text-slate-700 dark:text-gray-400 border-slate-300 dark:border-gray-700'"
|
||||
class="selector-button flex-1 rounded py-1.5 text-sm border transition-colors">{{ o }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Notch -->
|
||||
<div v-if="filterType === 'Notch (陷波器)'" class="space-y-3">
|
||||
<div><label class="text-sm text-slate-500 dark:text-gray-400 mb-2 block">中心頻率 f_0
|
||||
<div><label class="text-sm text-slate-600 dark:text-gray-400 mb-2 block">中心頻率 f_0
|
||||
(Hz)</label><input type="number" v-model.number="params.notch_f0"
|
||||
@change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base text-slate-900 dark:text-gray-100">
|
||||
</div>
|
||||
<div><label class="text-sm text-slate-500 dark:text-gray-400 mb-2 block">品質因數
|
||||
<div><label class="text-sm text-slate-600 dark:text-gray-400 mb-2 block">品質因數
|
||||
Q</label><input type="number" v-model.number="params.notch_q"
|
||||
@change="applyFilterDesign" step="0.1"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base text-slate-900 dark:text-gray-100">
|
||||
@@ -162,12 +162,12 @@
|
||||
</div>
|
||||
<!-- 1P1Z -->
|
||||
<div v-if="filterType === '1P1Z (一極一零)'" class="space-y-3">
|
||||
<div><label class="text-sm text-slate-500 dark:text-gray-400 mb-2 block">零點頻率 Freq_z
|
||||
<div><label class="text-sm text-slate-600 dark:text-gray-400 mb-2 block">零點頻率 Freq_z
|
||||
(Hz)</label><input type="number" v-model.number="params.opoz_fz"
|
||||
@change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base text-slate-900 dark:text-gray-100">
|
||||
</div>
|
||||
<div><label class="text-sm text-slate-500 dark:text-gray-400 mb-2 block">極點頻率 Freq_p
|
||||
<div><label class="text-sm text-slate-600 dark:text-gray-400 mb-2 block">極點頻率 Freq_p
|
||||
(Hz)</label><input type="number" v-model.number="params.opoz_fp"
|
||||
@change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base text-slate-900 dark:text-gray-100">
|
||||
@@ -175,17 +175,17 @@
|
||||
</div>
|
||||
<!-- 2P1Z -->
|
||||
<div v-if="filterType === '2P1Z (二極一零)'" class="space-y-3">
|
||||
<div><label class="text-sm text-slate-500 dark:text-gray-400 mb-2 block">零點頻率 Freq_z
|
||||
<div><label class="text-sm text-slate-600 dark:text-gray-400 mb-2 block">零點頻率 Freq_z
|
||||
(Hz)</label><input type="number" v-model.number="params.tp1z_fz"
|
||||
@change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base text-slate-900 dark:text-gray-100">
|
||||
</div>
|
||||
<div><label class="text-sm text-slate-500 dark:text-gray-400 mb-2 block">極點頻率 1 Freq_p1
|
||||
<div><label class="text-sm text-slate-600 dark:text-gray-400 mb-2 block">極點頻率 1 Freq_p1
|
||||
(Hz)</label><input type="number" v-model.number="params.tp1z_fp1"
|
||||
@change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base text-slate-900 dark:text-gray-100">
|
||||
</div>
|
||||
<div><label class="text-sm text-slate-500 dark:text-gray-400 mb-2 block">極點頻率 2 Freq_p2
|
||||
<div><label class="text-sm text-slate-600 dark:text-gray-400 mb-2 block">極點頻率 2 Freq_p2
|
||||
(Hz)</label><input type="number" v-model.number="params.tp1z_fp2"
|
||||
@change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base text-slate-900 dark:text-gray-100">
|
||||
@@ -193,22 +193,22 @@
|
||||
</div>
|
||||
<!-- 2P2Z -->
|
||||
<div v-if="filterType === '2P2Z (二極二零)'" class="space-y-3">
|
||||
<div><label class="text-sm text-slate-500 dark:text-gray-400 mb-2 block">零點頻率 1 Freq_z1
|
||||
<div><label class="text-sm text-slate-600 dark:text-gray-400 mb-2 block">零點頻率 1 Freq_z1
|
||||
(Hz)</label><input type="number" v-model.number="params.tptz_fz1"
|
||||
@change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base text-slate-900 dark:text-gray-100">
|
||||
</div>
|
||||
<div><label class="text-sm text-slate-500 dark:text-gray-400 mb-2 block">零點頻率 2 Freq_z2
|
||||
<div><label class="text-sm text-slate-600 dark:text-gray-400 mb-2 block">零點頻率 2 Freq_z2
|
||||
(Hz)</label><input type="number" v-model.number="params.tptz_fz2"
|
||||
@change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base text-slate-900 dark:text-gray-100">
|
||||
</div>
|
||||
<div><label class="text-sm text-slate-500 dark:text-gray-400 mb-2 block">極點頻率 1 Freq_p1
|
||||
<div><label class="text-sm text-slate-600 dark:text-gray-400 mb-2 block">極點頻率 1 Freq_p1
|
||||
(Hz)</label><input type="number" v-model.number="params.tptz_fp1"
|
||||
@change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base text-slate-900 dark:text-gray-100">
|
||||
</div>
|
||||
<div><label class="text-sm text-slate-500 dark:text-gray-400 mb-2 block">極點頻率 2 Freq_p2
|
||||
<div><label class="text-sm text-slate-600 dark:text-gray-400 mb-2 block">極點頻率 2 Freq_p2
|
||||
(Hz)</label><input type="number" v-model.number="params.tptz_fp2"
|
||||
@change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-3 text-base text-slate-900 dark:text-gray-100">
|
||||
@@ -218,7 +218,7 @@
|
||||
<div v-if="filterType === 'PID 控制器'" class="space-y-3">
|
||||
<div v-for="k in [{key:'kp',label:'比例增益 Kp'},{key:'ki',label:'積分增益 Ki'},{key:'kd',label:'微分增益 Kd'}]"
|
||||
:key="k.key">
|
||||
<label class="text-sm font-medium text-slate-500 dark:text-gray-400 mb-1 block">{{
|
||||
<label class="text-sm font-medium text-slate-600 dark:text-gray-400 mb-1 block">{{
|
||||
k.label
|
||||
}}</label>
|
||||
<input type="number" v-model.number="params[k.key]" @change="applyFilterDesign"
|
||||
@@ -229,12 +229,12 @@
|
||||
<!-- SOGI -->
|
||||
<div v-if="filterType === 'SOGI-Alpha (帶通)' || filterType === 'SOGI-Beta (低通)'"
|
||||
class="space-y-3">
|
||||
<div><label class="text-xs text-slate-500 dark:text-gray-400 mb-1 block">中心頻率 f_0
|
||||
<div><label class="text-xs text-slate-600 dark:text-gray-400 mb-1 block">中心頻率 f_0
|
||||
(Hz)</label><input type="number" v-model.number="params.sogi_f0"
|
||||
@change="applyFilterDesign"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-2 text-sm text-slate-900 dark:text-gray-100">
|
||||
</div>
|
||||
<div><label class="text-xs text-slate-500 dark:text-gray-400 mb-1 block">阻尼因數
|
||||
<div><label class="text-xs text-slate-600 dark:text-gray-400 mb-1 block">阻尼因數
|
||||
k</label><input type="number" v-model.number="params.sogi_k"
|
||||
@change="applyFilterDesign" step="0.001"
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded p-2 text-sm text-slate-900 dark:text-gray-100">
|
||||
@@ -247,7 +247,7 @@
|
||||
|
||||
<!-- 系統參數 -->
|
||||
<section>
|
||||
<h2 class="text-sm font-semibold text-slate-500 dark:text-gray-400 uppercase tracking-wider mb-3 flex items-center gap-2">
|
||||
<h2 class="text-sm font-semibold text-slate-600 dark:text-gray-400 uppercase tracking-wider mb-3 flex items-center gap-2">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z">
|
||||
@@ -258,7 +258,7 @@
|
||||
系統參數</h2>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 flex justify-between mb-1"><span>取樣頻率
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 flex justify-between mb-1"><span>取樣頻率
|
||||
fs
|
||||
(Hz)</span><span class="role-text-primary">{{ formatK(fs)
|
||||
}}</span></label>
|
||||
@@ -266,7 +266,7 @@
|
||||
class="w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-base role-focus-primary outline-none transition-all text-slate-900 dark:text-gray-100">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 flex justify-between mb-1"><span>系統增益
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 flex justify-between mb-1"><span>系統增益
|
||||
K (System Gain)</span><span class="role-text-primary">{{ systemGain
|
||||
}}x</span></label>
|
||||
<input type="number" v-model.number="systemGain" @input="onGainChange" step="0.1"
|
||||
@@ -281,7 +281,7 @@
|
||||
<!-- 係數手動設定 -->
|
||||
<section class="coeff-section">
|
||||
<h2
|
||||
class="text-sm font-semibold text-slate-500 dark:text-gray-400 uppercase tracking-wider mb-3 flex justify-between items-center">
|
||||
class="text-sm font-semibold text-slate-600 dark:text-gray-400 uppercase tracking-wider mb-3 flex justify-between items-center">
|
||||
<span class="flex items-center gap-2">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
@@ -290,17 +290,17 @@
|
||||
濾波器係數設定</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<button v-if="isManualDirty" @click="clearManualAdjustments"
|
||||
class="text-[10px] font-bold role-bg-primary-soft role-border-primary-soft role-text-primary role-hover-primary-soft border px-2 py-1 rounded transition-colors uppercase mr-1">重設</button>
|
||||
class="text-[10px] font-bold border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 px-3 py-1 rounded-full transition-colors uppercase mr-1">重設</button>
|
||||
</div>
|
||||
</h2>
|
||||
<div class="mb-3">
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 mb-1 block">前饋分子係數 b (b0 ~ b6)</label>
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 mb-1 block">前饋分子係數 b (b0 ~ b6)</label>
|
||||
<!-- b 係數標記顯示 -->
|
||||
<div class="flex flex-wrap gap-1 mb-2">
|
||||
<div v-for="item in floatingCoeffs.b" :key="item.label"
|
||||
class="role-bg-primary-soft px-1.5 py-0.5 rounded border role-border-primary-soft flex gap-1 items-center">
|
||||
<span class="text-[9px] role-text-primary-muted font-bold">{{ item.label }}:</span>
|
||||
<span class="text-[10px] font-mono role-text-primary">{{ item.val
|
||||
class="bg-slate-700 dark:bg-gray-300 px-1.5 py-0.5 rounded border border-transparent flex gap-1 items-center shadow-sm">
|
||||
<span class="text-[9px] text-white dark:text-gray-900 font-bold">{{ item.label }}:</span>
|
||||
<span class="text-[10px] font-mono text-white dark:text-gray-900 font-bold">{{ item.val
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -312,19 +312,19 @@
|
||||
<details :open="activeCoeffAdjustment === 'bScale'"
|
||||
class="mb-3 bg-slate-50 dark:bg-gray-900 border border-slate-200 dark:border-gray-800 rounded-lg">
|
||||
<summary @click.prevent="toggleCoeffAdjustment('bScale')"
|
||||
class="text-sm text-slate-500 dark:text-gray-400 px-3 py-3 cursor-pointer hover:text-slate-600 dark:hover:text-gray-300">
|
||||
class="text-sm text-slate-600 dark:text-gray-400 px-3 py-3 cursor-pointer hover:text-slate-700 dark:hover:text-gray-300">
|
||||
b 係數倍率微調</summary>
|
||||
<div class="px-3 pb-3">
|
||||
<div class="selector-button-group flex items-center gap-2 mb-2 mt-1">
|
||||
<span class="text-xs text-slate-400 dark:text-gray-500">靈敏度:</span>
|
||||
<span class="text-xs text-slate-500 dark:text-gray-500">靈敏度:</span>
|
||||
<button v-for="s in ['1%','2x','10x']" :key="s" @click="sense_b = s"
|
||||
:class="sense_b === s ? 'primary-action role-bg-primary role-text-on-fill role-border-primary' : 'bg-slate-200 dark:bg-gray-800 text-slate-500 dark:text-gray-400'"
|
||||
:class="sense_b === s ? 'primary-action role-bg-primary role-text-on-fill role-border-primary' : 'bg-slate-200 dark:bg-gray-800 text-slate-600 dark:text-gray-400'"
|
||||
class="selector-button btn-small px-3 py-1.5 rounded text-xs font-medium border border-slate-300 dark:border-gray-700">{{
|
||||
s }}</button>
|
||||
</div>
|
||||
<template v-for="i in 7" :key="'bs'+i">
|
||||
<div v-if="baseB[i-1] !== 0">
|
||||
<label class="text-xs text-slate-400 dark:text-gray-500 flex justify-between mt-1">
|
||||
<label class="text-xs text-slate-500 dark:text-gray-500 flex justify-between mt-1">
|
||||
<span>b{{ i-1 }} (基準: {{ (baseB[i-1] * (systemGain || 1.0)).toPrecision(6)
|
||||
}})</span>
|
||||
<span v-if="outOfRangeB[i-1]" class="role-text-warning font-bold animate-pulse">超出滑桿範圍</span>
|
||||
@@ -337,14 +337,14 @@
|
||||
</div>
|
||||
</details>
|
||||
<div class="mb-3">
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 mb-1 block">回饋分母係數 a (a0=1, a1 ~
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 mb-1 block">回饋分母係數 a (a0=1, a1 ~
|
||||
a6)</label>
|
||||
<!-- a 係數標記顯示 -->
|
||||
<div class="flex flex-wrap gap-1 mb-2">
|
||||
<div v-for="item in floatingCoeffs.a" :key="item.label"
|
||||
class="role-bg-primary-soft px-1.5 py-0.5 rounded border role-border-primary-soft flex gap-1 items-center">
|
||||
<span class="text-[9px] role-text-primary-muted font-bold">{{ item.label }}:</span>
|
||||
<span class="text-[10px] font-mono role-text-primary">{{ item.val
|
||||
class="bg-slate-700 dark:bg-gray-300 px-1.5 py-0.5 rounded border border-transparent flex gap-1 items-center shadow-sm">
|
||||
<span class="text-[9px] text-white dark:text-gray-900 font-bold">{{ item.label }}:</span>
|
||||
<span class="text-[10px] font-mono text-white dark:text-gray-900 font-bold">{{ item.val
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -356,19 +356,19 @@
|
||||
<details :open="activeCoeffAdjustment === 'aScale'"
|
||||
class="bg-slate-50 dark:bg-gray-900 border border-slate-200 dark:border-gray-800 rounded-lg">
|
||||
<summary @click.prevent="toggleCoeffAdjustment('aScale')"
|
||||
class="text-sm text-slate-500 dark:text-gray-400 px-3 py-3 cursor-pointer hover:text-slate-600 dark:hover:text-gray-300">
|
||||
class="text-sm text-slate-600 dark:text-gray-400 px-3 py-3 cursor-pointer hover:text-slate-700 dark:hover:text-gray-300">
|
||||
a 係數倍率微調</summary>
|
||||
<div class="px-3 pb-3">
|
||||
<div class="selector-button-group flex items-center gap-2 mb-2 mt-1">
|
||||
<span class="text-xs text-slate-400 dark:text-gray-500">靈敏度:</span>
|
||||
<span class="text-xs text-slate-500 dark:text-gray-500">靈敏度:</span>
|
||||
<button v-for="s in ['1%','2x','10x']" :key="s" @click="sense_a = s"
|
||||
:class="sense_a === s ? 'primary-action role-bg-primary role-text-on-fill role-border-primary' : 'bg-slate-200 dark:bg-gray-800 text-slate-500 dark:text-gray-400'"
|
||||
:class="sense_a === s ? 'primary-action role-bg-primary role-text-on-fill role-border-primary' : 'bg-slate-200 dark:bg-gray-800 text-slate-600 dark:text-gray-400'"
|
||||
class="selector-button px-3 py-1.5 rounded text-xs font-medium border border-slate-300 dark:border-gray-700">{{
|
||||
s }}</button>
|
||||
</div>
|
||||
<template v-for="i in 6" :key="'as'+i">
|
||||
<div v-if="baseA[i] !== 0">
|
||||
<label class="text-xs text-slate-400 dark:text-gray-500 flex justify-between mt-1">
|
||||
<label class="text-xs text-slate-500 dark:text-gray-500 flex justify-between mt-1">
|
||||
<span>a{{ i }} (基準: {{ baseA[i].toPrecision(6) }})</span>
|
||||
<span v-if="outOfRangeA[i]" class="role-text-warning font-bold animate-pulse">超出滑桿範圍</span>
|
||||
</label>
|
||||
@@ -384,86 +384,86 @@
|
||||
<details :open="activeCoeffAdjustment === 'a1a2'"
|
||||
class="mt-3 bg-slate-50 dark:bg-gray-900 border border-slate-200 dark:border-gray-800 rounded-lg">
|
||||
<summary @click.prevent="toggleCoeffAdjustment('a1a2')"
|
||||
class="text-sm text-slate-500 dark:text-gray-400 px-3 py-3 cursor-pointer hover:text-slate-600 dark:hover:text-gray-300">
|
||||
class="text-sm text-slate-600 dark:text-gray-400 px-3 py-3 cursor-pointer hover:text-slate-700 dark:hover:text-gray-300">
|
||||
a1/a2 步階微調</summary>
|
||||
<div class="px-3 pb-3">
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="grid grid-cols-[3.25rem_1fr_3.25rem] gap-2 mb-3">
|
||||
<button type="button" @click="adjustAFineStep(-1)" title="調粗"
|
||||
class="rounded-lg border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-600 dark:text-gray-300 py-2 text-xs font-bold transition-colors hover:bg-slate-100 dark:hover:bg-gray-700">
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-sm font-bold shadow-sm">
|
||||
粗
|
||||
</button>
|
||||
<div class="min-w-0 rounded-lg border role-border-primary-soft role-bg-primary-soft px-3 py-2 text-center">
|
||||
<div class="min-w-0 rounded-lg border role-border-primary-soft role-bg-primary-soft px-3 h-[42px] flex flex-col justify-center text-center">
|
||||
<span class="block text-[9px] role-text-primary-muted role-text-primary font-bold">微調量</span>
|
||||
<span class="block truncate font-mono text-sm font-bold role-text-primary" :title="formatAFineDelta()">
|
||||
<span class="block truncate font-mono text-sm font-bold role-text-primary leading-none mt-0.5" :title="formatAFineDelta()">
|
||||
+/-{{ formatAFineDelta() }}
|
||||
</span>
|
||||
</div>
|
||||
<button type="button" @click="adjustAFineStep(1)" title="調細"
|
||||
class="rounded-lg border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-600 dark:text-gray-300 py-2 text-xs font-bold transition-colors hover:bg-slate-100 dark:hover:bg-gray-700">
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-sm font-bold shadow-sm">
|
||||
細
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-[11px] font-semibold text-slate-400 dark:text-gray-500 mb-2">
|
||||
<div class="text-[11px] font-semibold text-slate-500 dark:text-gray-500 mb-2">
|
||||
{{ isTouchInput ? '觸控拖曳微調' : '直接輸入 / shift + 滾輪微調' }}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<article v-if="!isTouchInput" class="block">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">δ</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">δ</span>
|
||||
<div class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startA1A2Repeat('delta', -1)" @pointerup="stopA1A2Repeat" @pointerleave="stopA1A2Repeat" @pointercancel="stopA1A2Repeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="formatFineCoeff(currentDelta)" :step="aFineStep"
|
||||
@change="setAFineDirect('delta', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleAFineWheel('delta', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-xs font-mono text-center role-text-primary">
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg px-2 h-[42px] text-xs font-mono text-center role-text-primary">
|
||||
<button type="button" @pointerdown.prevent="startA1A2Repeat('delta', 1)" @pointerup="stopA1A2Repeat" @pointerleave="stopA1A2Repeat" @pointercancel="stopA1A2Repeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
</article>
|
||||
<button v-else type="button" @pointerdown.prevent="startAFineDrag('delta', $event)"
|
||||
class="touch-none select-none w-28 mx-auto bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center role-active-border-primary active:ring-2 role-active-ring-primary">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">δ</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">δ</span>
|
||||
<span class="block max-w-full truncate text-base font-mono font-bold role-text-primary" :title="formatFineCoeff(currentDelta)">{{ formatFineCoeff(currentDelta) }}</span>
|
||||
</button>
|
||||
<article v-if="!isTouchInput" class="block">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">r</span>
|
||||
<div class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startA1A2Repeat('r', -1)" @pointerup="stopA1A2Repeat" @pointerleave="stopA1A2Repeat" @pointercancel="stopA1A2Repeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="formatFineCoeff(currentR)" :step="aFineStep"
|
||||
@change="setAFineDirect('r', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleAFineWheel('r', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-xs font-mono text-center text-slate-700 dark:text-gray-300">
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg px-2 h-[42px] text-xs font-mono text-center role-text-primary">
|
||||
<button type="button" @pointerdown.prevent="startA1A2Repeat('r', 1)" @pointerup="stopA1A2Repeat" @pointerleave="stopA1A2Repeat" @pointercancel="stopA1A2Repeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
</article>
|
||||
<button v-else type="button" @pointerdown.prevent="startAFineDrag('r', $event)"
|
||||
class="touch-none select-none w-28 mx-auto bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center active:border-slate-400 active:ring-2 active:ring-slate-400/20">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r</span>
|
||||
<span class="block max-w-full truncate text-base font-mono font-bold text-slate-700 dark:text-gray-300" :title="formatFineCoeff(currentR)">{{ formatFineCoeff(currentR) }}</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">r</span>
|
||||
<span class="block max-w-full truncate text-base font-mono font-bold role-text-primary" :title="formatFineCoeff(currentR)">{{ formatFineCoeff(currentR) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2 mb-4">
|
||||
<div class="min-w-0 bg-white dark:bg-gray-800 border border-slate-100 dark:border-gray-700 rounded-lg p-2 text-center shadow-sm">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">a1 = -1 - r + δ</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">a1 = -1 - r + δ</span>
|
||||
<span class="block max-w-full truncate text-sm font-mono font-bold role-text-primary-muted" :title="formatFineCoeff(currentA1)">{{ formatFineCoeff(currentA1) }}</span>
|
||||
</div>
|
||||
<div class="min-w-0 bg-white dark:bg-gray-800 border border-slate-100 dark:border-gray-700 rounded-lg p-2 text-center shadow-sm">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">a2 = r + δ</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">a2 = r + δ</span>
|
||||
<span class="block max-w-full truncate text-sm font-mono font-bold role-text-primary-muted" :title="formatFineCoeff(currentA2)">{{ formatFineCoeff(currentA2) }}</span>
|
||||
</div>
|
||||
<div class="min-w-0 bg-white dark:bg-gray-800 border border-slate-100 dark:border-gray-700 rounded-lg p-2 text-center shadow-sm">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">差值 a1-a2</span>
|
||||
<span class="block max-w-full truncate text-xs font-mono text-slate-600 dark:text-gray-300" :title="formatFineCoeff(currentA1A2Diff)">{{ formatFineCoeff(currentA1A2Diff) }}</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">差值 a1-a2</span>
|
||||
<span class="block max-w-full truncate text-xs font-mono text-slate-700 dark:text-gray-300" :title="formatFineCoeff(currentA1A2Diff)">{{ formatFineCoeff(currentA1A2Diff) }}</span>
|
||||
</div>
|
||||
<div class="min-w-0 bg-white dark:bg-gray-800 border border-slate-100 dark:border-gray-700 rounded-lg p-2 text-center shadow-sm">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">總和 a1+a2</span>
|
||||
<span class="block max-w-full truncate text-xs font-mono text-slate-600 dark:text-gray-300" :title="formatFineCoeff(currentA1A2Sum)">{{ formatFineCoeff(currentA1A2Sum) }}</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">總和 a1+a2</span>
|
||||
<span class="block max-w-full truncate text-xs font-mono text-slate-700 dark:text-gray-300" :title="formatFineCoeff(currentA1A2Sum)">{{ formatFineCoeff(currentA1A2Sum) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -482,77 +482,77 @@
|
||||
<!-- 定點數轉換 -->
|
||||
<section class="fixed-section">
|
||||
<h2
|
||||
class="text-sm font-semibold text-slate-500 dark:text-gray-400 uppercase tracking-wider mb-3 flex justify-between items-center">
|
||||
class="text-sm font-semibold text-slate-600 dark:text-gray-400 uppercase tracking-wider mb-3 flex justify-between items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M9 7h6M9 12h6M9 17h3M5 5a2 2 0 012-2h7l5 5v11a2 2 0 01-2 2H7a2 2 0 01-2-2V5z"></path>
|
||||
</svg>
|
||||
<span>定點數係數轉換</span>
|
||||
<span
|
||||
<span v-if="shiftBitsB > 0"
|
||||
class="text-xs role-bg-primary-soft role-text-primary px-2 py-1 rounded">b Q{{
|
||||
shiftBitsB }}</span>
|
||||
<span
|
||||
<span v-if="shiftBitsA > 0"
|
||||
class="text-xs role-bg-primary-soft role-text-primary px-2 py-1 rounded">a Q{{
|
||||
shiftBitsA }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button v-if="isFixedPointDirty" @click="resetFixedPointSettings"
|
||||
class="text-[10px] font-bold role-bg-primary-soft role-border-primary-soft role-text-primary role-hover-primary-soft border px-2 py-1 rounded transition-colors uppercase mr-1">重設</button>
|
||||
class="text-[10px] font-bold border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 px-3 py-1 rounded-full transition-colors uppercase mr-1">重設</button>
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
<div class="mb-4 grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 mb-1 block">b 左移位元數 (Shift Bits)</label>
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 mb-1 block">b 左移位元數 (Shift Bits)</label>
|
||||
<div v-if="!isTouchInput" class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startShiftBitRepeat('b', -1)" @pointerup="stopShiftBitRepeat" @pointerleave="stopShiftBitRepeat" @pointercancel="stopShiftBitRepeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="shiftBitsB" min="0"
|
||||
@change="setShiftBits('b', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleShiftBitWheel('b', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-base role-focus-primary outline-none transition-all text-slate-900 dark:text-gray-100">
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg px-2 h-[42px] text-center text-base role-focus-primary outline-none transition-all text-slate-900 dark:text-gray-100">
|
||||
<button type="button" @pointerdown.prevent="startShiftBitRepeat('b', 1)" @pointerup="stopShiftBitRepeat" @pointerleave="stopShiftBitRepeat" @pointercancel="stopShiftBitRepeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
<button v-else type="button" @pointerdown.prevent="startShiftBitDrag('b', $event)"
|
||||
class="touch-none select-none block w-24 mx-auto bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center role-active-border-primary active:ring-2 role-active-ring-primary">
|
||||
<span class="text-lg font-mono font-bold role-text-primary">{{ shiftBitsB }}</span>
|
||||
</button>
|
||||
<p class="text-[10px] text-slate-400 mt-1">b scaling: 2^{{ shiftBitsB }} ({{
|
||||
<p class="text-[10px] text-slate-500 mt-1">b scaling: 2^{{ shiftBitsB }} ({{
|
||||
Math.pow(2, shiftBitsB) }})</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 mb-1 block">a 左移位元數 (Shift Bits)</label>
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 mb-1 block">a 左移位元數 (Shift Bits)</label>
|
||||
<div v-if="!isTouchInput" class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startShiftBitRepeat('a', -1)" @pointerup="stopShiftBitRepeat" @pointerleave="stopShiftBitRepeat" @pointercancel="stopShiftBitRepeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="shiftBitsA" min="0"
|
||||
@change="setShiftBits('a', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleShiftBitWheel('a', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-base role-focus-primary outline-none transition-all text-slate-900 dark:text-gray-100">
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg px-2 h-[42px] text-center text-base role-focus-primary outline-none transition-all text-slate-900 dark:text-gray-100">
|
||||
<button type="button" @pointerdown.prevent="startShiftBitRepeat('a', 1)" @pointerup="stopShiftBitRepeat" @pointerleave="stopShiftBitRepeat" @pointercancel="stopShiftBitRepeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
<button v-else type="button" @pointerdown.prevent="startShiftBitDrag('a', $event)"
|
||||
class="touch-none select-none block w-24 mx-auto bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center role-active-border-primary active:ring-2 role-active-ring-primary">
|
||||
<span class="text-lg font-mono font-bold role-text-primary">{{ shiftBitsA }}</span>
|
||||
</button>
|
||||
<p class="text-[10px] text-slate-400 mt-1">a scaling: 2^{{ shiftBitsA }} ({{
|
||||
<p class="text-[10px] text-slate-500 mt-1">a scaling: 2^{{ shiftBitsA }} ({{
|
||||
Math.pow(2, shiftBitsA) }})</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 mb-1 block">定點分子係數 b
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 mb-1 block">定點分子係數 b
|
||||
(Integers)</label>
|
||||
<div class="flex flex-wrap gap-1 mb-2">
|
||||
<div v-for="(item, idx) in fixedPointCoeffs.b" :key="item.label"
|
||||
:class="item.isOverridden ? 'role-border-primary ring-1 role-ring-primary' : 'role-border-primary-soft'"
|
||||
class="role-bg-primary-soft px-1.5 py-0.5 rounded border flex items-center gap-1 transition-all">
|
||||
<span class="text-[9px] role-text-primary font-bold">{{ item.label }}:</span>
|
||||
<span class="text-[10px] font-mono role-text-primary font-bold"
|
||||
:class="item.isOverridden ? 'border-amber-400 dark:border-amber-500 ring-1 ring-amber-400 dark:ring-amber-500' : 'border-transparent'"
|
||||
class="bg-slate-700 dark:bg-gray-300 px-1.5 py-0.5 rounded border flex items-center gap-1 transition-all shadow-sm">
|
||||
<span class="text-[9px] text-white dark:text-gray-900 font-bold">{{ item.label }}:</span>
|
||||
<span class="text-[10px] font-mono text-white dark:text-gray-900 font-bold"
|
||||
:class="{ 'italic': !item.isOverridden }">{{ item.val }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -560,14 +560,14 @@
|
||||
class="w-full bg-slate-50 dark:bg-gray-900 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-sm role-focus-primary outline-none font-mono resize-none leading-relaxed role-text-primary"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 mb-1 block">定點分母係數 a
|
||||
<label class="text-sm text-slate-600 dark:text-gray-400 mb-1 block">定點分母係數 a
|
||||
(Integers)</label>
|
||||
<div class="flex flex-wrap gap-1 mb-2">
|
||||
<div v-for="(item, idx) in fixedPointCoeffs.a" :key="item.label"
|
||||
:class="item.isOverridden ? 'role-border-primary ring-1 role-ring-primary' : 'role-border-primary-soft'"
|
||||
class="role-bg-primary-soft px-1.5 py-0.5 rounded border flex items-center gap-1 transition-all">
|
||||
<span class="text-[9px] role-text-primary-muted font-bold">{{ item.label }}:</span>
|
||||
<span class="text-[10px] font-mono role-text-primary font-bold"
|
||||
:class="item.isOverridden ? 'border-amber-400 dark:border-amber-500 ring-1 ring-amber-400 dark:ring-amber-500' : 'border-transparent'"
|
||||
class="bg-slate-700 dark:bg-gray-300 px-1.5 py-0.5 rounded border flex items-center gap-1 transition-all shadow-sm">
|
||||
<span class="text-[9px] text-white dark:text-gray-900 font-bold">{{ item.label }}:</span>
|
||||
<span class="text-[10px] font-mono text-white dark:text-gray-900 font-bold"
|
||||
:class="{ 'italic': !item.isOverridden }">{{ item.val }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -575,78 +575,78 @@
|
||||
class="w-full bg-slate-50 dark:bg-gray-900 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-sm role-focus-primary outline-none font-mono resize-none leading-relaxed role-text-primary"></textarea>
|
||||
</div>
|
||||
<details class="mt-3 bg-slate-50 dark:bg-gray-900 border border-slate-200 dark:border-gray-800 rounded-lg">
|
||||
<summary class="text-sm text-slate-500 dark:text-gray-400 px-3 py-3 cursor-pointer hover:text-slate-600 dark:hover:text-gray-300">
|
||||
<summary class="text-sm text-slate-600 dark:text-gray-400 px-3 py-3 cursor-pointer hover:text-slate-700 dark:hover:text-gray-300">
|
||||
a1/a2 步階微調</summary>
|
||||
<div class="px-3 pb-3">
|
||||
<div class="mb-4">
|
||||
<div class="grid grid-cols-[3.25rem_1fr_3.25rem] gap-2 mb-3">
|
||||
<button type="button" @click="adjustFixedAFineStep(-1)" title="調粗"
|
||||
class="rounded-lg border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-600 dark:text-gray-300 py-2 text-xs font-bold transition-colors hover:bg-slate-100 dark:hover:bg-gray-700">粗</button>
|
||||
<div class="min-w-0 rounded-lg border role-border-primary-soft role-bg-primary-soft px-3 py-2 text-center">
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-sm font-bold shadow-sm">粗</button>
|
||||
<div class="min-w-0 rounded-lg border role-border-primary-soft role-bg-primary-soft px-3 h-[42px] flex flex-col justify-center text-center">
|
||||
<span class="block text-[9px] role-text-primary-muted role-text-primary font-bold">微調量</span>
|
||||
<span class="block truncate font-mono text-sm font-bold role-text-primary" :title="formatFixedAFineDelta()">+/-{{ formatFixedAFineDelta() }}</span>
|
||||
<span class="block truncate font-mono text-sm font-bold role-text-primary leading-none mt-0.5" :title="formatFixedAFineDelta()">+/-{{ formatFixedAFineDelta() }}</span>
|
||||
</div>
|
||||
<button type="button" @click="adjustFixedAFineStep(1)" title="調細"
|
||||
class="rounded-lg border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-600 dark:text-gray-300 py-2 text-xs font-bold transition-colors hover:bg-slate-100 dark:hover:bg-gray-700">細</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-sm font-bold shadow-sm">細</button>
|
||||
</div>
|
||||
<div class="text-[11px] font-semibold text-slate-400 dark:text-gray-500 mb-2">
|
||||
<div class="text-[11px] font-semibold text-slate-500 dark:text-gray-500 mb-2">
|
||||
{{ isTouchInput ? '觸控拖曳微調' : '直接輸入 / shift + 滾輪微調' }}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<article v-if="!isTouchInput" class="block">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">δ</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">δ</span>
|
||||
<div class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startFixedA1A2Repeat('delta', -1)" @pointerup="stopFixedA1A2Repeat" @pointerleave="stopFixedA1A2Repeat" @pointercancel="stopFixedA1A2Repeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="formatFixedFineCoeff(fixedCurrentDelta)" :step="fixedAFineStep"
|
||||
@change="setFixedAFineDirect('delta', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleFixedAFineWheel('delta', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-xs font-mono text-center role-text-primary">
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg px-2 h-[42px] text-xs font-mono text-center role-text-primary">
|
||||
<button type="button" @pointerdown.prevent="startFixedA1A2Repeat('delta', 1)" @pointerup="stopFixedA1A2Repeat" @pointerleave="stopFixedA1A2Repeat" @pointercancel="stopFixedA1A2Repeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
</article>
|
||||
<button v-else type="button" @pointerdown.prevent="startFixedAFineDrag('delta', $event)"
|
||||
class="touch-none select-none w-28 mx-auto bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center role-active-border-primary active:ring-2 role-active-ring-primary">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">δ</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">δ</span>
|
||||
<span class="block max-w-full truncate text-base font-mono font-bold role-text-primary" :title="formatFixedFineCoeff(fixedCurrentDelta)">{{ formatFixedFineCoeff(fixedCurrentDelta) }}</span>
|
||||
</button>
|
||||
<article v-if="!isTouchInput" class="block">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">r</span>
|
||||
<div class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startFixedA1A2Repeat('r', -1)" @pointerup="stopFixedA1A2Repeat" @pointerleave="stopFixedA1A2Repeat" @pointercancel="stopFixedA1A2Repeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="formatFixedFineCoeff(fixedCurrentR)" :step="fixedAFineStep"
|
||||
@change="setFixedAFineDirect('r', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleFixedAFineWheel('r', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-xs font-mono text-center text-slate-700 dark:text-gray-300">
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-xs font-mono text-center role-text-primary">
|
||||
<button type="button" @pointerdown.prevent="startFixedA1A2Repeat('r', 1)" @pointerup="stopFixedA1A2Repeat" @pointerleave="stopFixedA1A2Repeat" @pointercancel="stopFixedA1A2Repeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-slate-700 dark:text-gray-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
</article>
|
||||
<button v-else type="button" @pointerdown.prevent="startFixedAFineDrag('r', $event)"
|
||||
class="touch-none select-none w-28 mx-auto bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center active:border-slate-400 active:ring-2 active:ring-slate-400/20">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r</span>
|
||||
<span class="block max-w-full truncate text-base font-mono font-bold text-slate-700 dark:text-gray-300" :title="formatFixedFineCoeff(fixedCurrentR)">{{ formatFixedFineCoeff(fixedCurrentR) }}</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">r</span>
|
||||
<span class="block max-w-full truncate text-base font-mono font-bold role-text-primary" :title="formatFixedFineCoeff(fixedCurrentR)">{{ formatFixedFineCoeff(fixedCurrentR) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2 mb-4">
|
||||
<div class="min-w-0 bg-white dark:bg-gray-800 border border-slate-100 dark:border-gray-700 rounded-lg p-2 text-center shadow-sm">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">a1 = -a0 - r + δ</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">a1 = -a0 - r + δ</span>
|
||||
<span class="block max-w-full truncate text-sm font-mono font-bold role-text-primary-muted" :title="formatFixedFineCoeff(fixedCurrentA1)">{{ formatFixedFineCoeff(fixedCurrentA1) }}</span>
|
||||
</div>
|
||||
<div class="min-w-0 bg-white dark:bg-gray-800 border border-slate-100 dark:border-gray-700 rounded-lg p-2 text-center shadow-sm">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">a2 = r + δ</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">a2 = r + δ</span>
|
||||
<span class="block max-w-full truncate text-sm font-mono font-bold role-text-primary-muted" :title="formatFixedFineCoeff(fixedCurrentA2)">{{ formatFixedFineCoeff(fixedCurrentA2) }}</span>
|
||||
</div>
|
||||
<div class="min-w-0 bg-white dark:bg-gray-800 border border-slate-100 dark:border-gray-700 rounded-lg p-2 text-center shadow-sm">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">差值 a1-a2</span>
|
||||
<span class="block max-w-full truncate text-xs font-mono text-slate-600 dark:text-gray-300" :title="formatFixedFineCoeff(fixedCurrentA1A2Diff)">{{ formatFixedFineCoeff(fixedCurrentA1A2Diff) }}</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">差值 a1-a2</span>
|
||||
<span class="block max-w-full truncate text-xs font-mono text-slate-700 dark:text-gray-300" :title="formatFixedFineCoeff(fixedCurrentA1A2Diff)">{{ formatFixedFineCoeff(fixedCurrentA1A2Diff) }}</span>
|
||||
</div>
|
||||
<div class="min-w-0 bg-white dark:bg-gray-800 border border-slate-100 dark:border-gray-700 rounded-lg p-2 text-center shadow-sm">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">總和 a1+a2</span>
|
||||
<span class="block max-w-full truncate text-xs font-mono text-slate-600 dark:text-gray-300" :title="formatFixedFineCoeff(fixedCurrentA1A2Sum)">{{ formatFixedFineCoeff(fixedCurrentA1A2Sum) }}</span>
|
||||
<span class="text-[9px] text-slate-500 block font-bold mb-1">總和 a1+a2</span>
|
||||
<span class="block max-w-full truncate text-xs font-mono text-slate-700 dark:text-gray-300" :title="formatFixedFineCoeff(fixedCurrentA1A2Sum)">{{ formatFixedFineCoeff(fixedCurrentA1A2Sum) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="role-bg-warning-soft border role-border-warning-soft rounded-lg p-3">
|
||||
@@ -658,7 +658,7 @@
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<p class="text-[10px] text-slate-400 dark:text-gray-500 mt-3 italic">註:b 係數乘以 2^{{ shiftBitsB }},a
|
||||
<p class="text-[10px] text-slate-500 dark:text-gray-500 mt-3 italic">註:b 係數乘以 2^{{ shiftBitsB }},a
|
||||
係數乘以 2^{{ shiftBitsA }} 後四捨五入,可用於定點數 DSP 實作。</p>
|
||||
</section>
|
||||
|
||||
@@ -718,10 +718,10 @@
|
||||
時域訊號探討</h3>
|
||||
</div>
|
||||
<div class="p-6 z-10 relative bg-white dark:bg-dark transition-colors duration-300">
|
||||
<p class="text-base text-slate-500 dark:text-gray-400 mb-4">請上傳包含時域訊號的 CSV 檔案,系統會使用上述設定的差分濾波器。
|
||||
<p class="text-base text-slate-600 dark:text-gray-400 mb-4">請上傳包含時域訊號的 CSV 檔案,系統會使用上述設定的差分濾波器。
|
||||
</p>
|
||||
<div
|
||||
class="flex items-center flex-wrap gap-4 mb-6 p-4 bg-slate-50 dark:bg-gray-900/50 rounded-lg border border-slate-100 dark:border-gray-800">
|
||||
class="flex items-center flex-wrap gap-4 mb-6 p-4 bg-white dark:bg-gray-800 rounded-lg border border-slate-100 dark:border-gray-700 shadow-sm">
|
||||
<label
|
||||
class="cursor-pointer role-bg-primary role-hover-primary role-text-on-fill px-5 py-3 rounded-lg text-base font-semibold transition-all shadow-md role-shadow-primary flex items-center gap-2">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -735,7 +735,7 @@
|
||||
<span v-if="csvFile" class="text-base role-text-primary font-mono">{{
|
||||
csvFile.name }}</span>
|
||||
<span v-if="csvInfo"
|
||||
class="text-sm text-slate-500 dark:text-gray-400 font-mono">
|
||||
class="text-sm text-slate-600 dark:text-gray-400 font-mono">
|
||||
{{ csvInfo.rows }} 筆 / {{ csvInfo.columns }} 欄
|
||||
</span>
|
||||
<span v-if="csvParseError"
|
||||
@@ -747,7 +747,7 @@
|
||||
class="h-8 border-l border-slate-200 dark:border-gray-700 mx-2"></div>
|
||||
|
||||
<div v-if="csvColumns.length > 0" class="flex items-center gap-2">
|
||||
<span class="text-sm text-slate-400">訊號欄位:</span>
|
||||
<span class="text-sm text-slate-500">訊號欄位:</span>
|
||||
<select v-model="selectedColumn"
|
||||
class="bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-base role-focus-primary outline-none text-slate-900 dark:text-gray-100">
|
||||
<option v-for="(col, idx) in csvColumns" :key="idx" :value="idx">{{ col }}</option>
|
||||
@@ -773,7 +773,7 @@
|
||||
<!-- 資料預覽 -->
|
||||
<div v-if="csvPreview.length > 0" class="mb-6">
|
||||
<h4
|
||||
class="text-sm font-semibold text-slate-500 dark:text-gray-400 mb-2 uppercase tracking-tight">
|
||||
class="text-sm font-semibold text-slate-600 dark:text-gray-400 mb-2 uppercase tracking-tight">
|
||||
資料預覽 (前五筆):</h4>
|
||||
<div
|
||||
class="overflow-x-auto rounded-xl border border-slate-100 dark:border-gray-800 shadow-inner">
|
||||
@@ -781,7 +781,7 @@
|
||||
<thead>
|
||||
<tr class="bg-slate-50 dark:bg-gray-800/50">
|
||||
<th v-for="col in csvColumns" :key="col"
|
||||
class="px-4 py-3 font-semibold text-slate-700 dark:text-gray-300">{{ col
|
||||
class="px-4 py-3 font-semibold text-slate-800 dark:text-gray-300">{{ col
|
||||
}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -789,7 +789,7 @@
|
||||
<tr v-for="(row, ri) in csvPreview" :key="ri"
|
||||
class="hover:bg-slate-50/50 dark:hover:bg-gray-800/30 transition-colors">
|
||||
<td v-for="(val, ci) in row" :key="ci"
|
||||
class="px-4 py-2 font-mono text-slate-600 dark:text-gray-400">{{ val }}
|
||||
class="px-4 py-2 font-mono text-slate-700 dark:text-gray-400">{{ val }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -807,7 +807,7 @@
|
||||
</div>
|
||||
|
||||
<div v-if="!filterDone && !loadingFilter && csvPreview.length === 0"
|
||||
class="h-[300px] flex flex-col items-center justify-center text-slate-400 dark:text-gray-500 border-2 border-dashed border-slate-200 dark:border-gray-800 rounded-xl bg-slate-50/50 dark:bg-gray-900/30">
|
||||
class="h-[300px] flex flex-col items-center justify-center text-slate-500 dark:text-gray-500 border-2 border-dashed border-slate-200 dark:border-gray-800 rounded-xl bg-slate-50/50 dark:bg-gray-900/30">
|
||||
<svg class="w-12 h-12 mb-3 opacity-50" fill="none" stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
|
||||
@@ -823,7 +823,7 @@
|
||||
<!-- 全域置底版權宣告 -->
|
||||
<footer
|
||||
class="bg-white dark:bg-dark border-t border-slate-200 dark:border-gray-800 py-2 text-center z-20 transition-colors duration-300">
|
||||
<p class="text-xs text-slate-400 dark:text-gray-600">© 2026 喆富創新科技股份有限公司. All rights reserved.</p>
|
||||
<p class="text-xs text-slate-500 dark:text-gray-600">© 2026 喆富創新科技股份有限公司. All rights reserved.</p>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+59
-2
@@ -69,6 +69,7 @@ export default {
|
||||
webSerialSupported: false,
|
||||
writingMCU: false,
|
||||
mcuStatus: '',
|
||||
saveSettingsTimeout: null,
|
||||
}
|
||||
},
|
||||
|
||||
@@ -203,6 +204,25 @@ export default {
|
||||
return this.fixedCurrentA1 + this.fixedCurrentA2;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
fs: 'debouncedSaveSettings',
|
||||
b_str: 'debouncedSaveSettings',
|
||||
a_str: 'debouncedSaveSettings',
|
||||
filterType: 'debouncedSaveSettings',
|
||||
systemGain: 'debouncedSaveSettings',
|
||||
params: { handler: 'debouncedSaveSettings', deep: true },
|
||||
baseB: { handler: 'debouncedSaveSettings', deep: true },
|
||||
baseA: { handler: 'debouncedSaveSettings', deep: true },
|
||||
bSliders: { handler: 'debouncedSaveSettings', deep: true },
|
||||
aSliders: { handler: 'debouncedSaveSettings', deep: true },
|
||||
sense_b: 'debouncedSaveSettings',
|
||||
sense_a: 'debouncedSaveSettings',
|
||||
shiftBitsB: 'debouncedSaveSettings',
|
||||
shiftBitsA: 'debouncedSaveSettings',
|
||||
fixedOverrides: { handler: 'debouncedSaveSettings', deep: true },
|
||||
aFineStep: 'debouncedSaveSettings',
|
||||
fixedAFineStep: 'debouncedSaveSettings'
|
||||
},
|
||||
mounted() {
|
||||
this.isTouchInput = window.matchMedia?.('(pointer: coarse)').matches || false;
|
||||
window.addEventListener('pointerdown', this.rememberPointerType);
|
||||
@@ -214,6 +234,9 @@ export default {
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
|
||||
this.loadSettings();
|
||||
|
||||
this.webSerialSupported = ('serial' in navigator);
|
||||
if (!this.webSerialSupported) {
|
||||
this.mcuStatus = '此瀏覽器不支援 Web Serial(需使用 Chrome 或 Edge,且透過 HTTPS 連線)';
|
||||
@@ -230,6 +253,40 @@ export default {
|
||||
this.stopFixedAFineDrag();
|
||||
},
|
||||
methods: {
|
||||
loadSettings() {
|
||||
try {
|
||||
const saved = localStorage.getItem('dea_settings');
|
||||
if (saved) {
|
||||
const parsed = JSON.parse(saved);
|
||||
const keys = [
|
||||
'fs', 'b_str', 'a_str', 'filterType', 'systemGain', 'params',
|
||||
'baseB', 'baseA', 'bSliders', 'aSliders', 'sense_b', 'sense_a',
|
||||
'shiftBitsB', 'shiftBitsA', 'fixedOverrides', 'aFineStep', 'fixedAFineStep'
|
||||
];
|
||||
keys.forEach(k => {
|
||||
if (parsed[k] !== undefined) {
|
||||
this[k] = parsed[k];
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to load settings:', e);
|
||||
}
|
||||
},
|
||||
saveSettings() {
|
||||
const keys = [
|
||||
'fs', 'b_str', 'a_str', 'filterType', 'systemGain', 'params',
|
||||
'baseB', 'baseA', 'bSliders', 'aSliders', 'sense_b', 'sense_a',
|
||||
'shiftBitsB', 'shiftBitsA', 'fixedOverrides', 'aFineStep', 'fixedAFineStep'
|
||||
];
|
||||
const settings = {};
|
||||
keys.forEach(k => settings[k] = this[k]);
|
||||
localStorage.setItem('dea_settings', JSON.stringify(settings));
|
||||
},
|
||||
debouncedSaveSettings() {
|
||||
clearTimeout(this.saveSettingsTimeout);
|
||||
this.saveSettingsTimeout = setTimeout(() => this.saveSettings(), 500);
|
||||
},
|
||||
rememberPointerType(event) {
|
||||
if (event.pointerType === 'touch') {
|
||||
this.isTouchInput = true;
|
||||
@@ -595,7 +652,7 @@ export default {
|
||||
this.outOfRangeB[i] = false;
|
||||
if (base !== 0 && val !== 0 && (val / (base * gain)) > 0) {
|
||||
let logVal = Math.log10(val / (base * gain));
|
||||
if (Math.abs(logVal) < 1e-12) logVal = 0; // 消除浮點數微小誤差
|
||||
if (Math.abs(logVal) < 1e-8) logVal = 0; // 消除浮點數微小誤差
|
||||
if (Math.abs(logVal) > this.maxLogB) this.outOfRangeB[i] = true;
|
||||
this.bSliders[i] = Math.max(-this.maxLogB, Math.min(this.maxLogB, logVal));
|
||||
} else {
|
||||
@@ -610,7 +667,7 @@ export default {
|
||||
this.outOfRangeA[i] = false;
|
||||
if (base !== 0 && val !== 0 && (val / base) > 0) {
|
||||
let logVal = Math.log10(val / base);
|
||||
if (Math.abs(logVal) < 1e-12) logVal = 0; // 消除浮點數微小誤差
|
||||
if (Math.abs(logVal) < 1e-8) logVal = 0; // 消除浮點數微小誤差
|
||||
if (Math.abs(logVal) > this.maxLogA) this.outOfRangeA[i] = true;
|
||||
this.aSliders[i] = Math.max(-this.maxLogA, Math.min(this.maxLogA, logVal));
|
||||
} else {
|
||||
|
||||
+1489
-1648
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user