2024年2月8日 星期四

linux 上解決核心模組 ch34x.ko 掛載 /dev/ttyUSB0 時被強制斷線的問題

 參考討論文章: https://unix.stackexchange.com/questions/670636/unable-to-use-usb-dongle-based-on-usb-serial-converter-chip

只要有 root 權限執行以下腳本就能解決, 一勞永逸辦法是將它放在 /etc/rc.local 裡面, 一開機就執行

        # ...

    for f in /usr/lib/udev/rules.d/*brltty*.rules; do
        sudo ln -s /dev/null "/etc/udev/rules.d/$(basename "$f")"
    done
    sudo udevadm control --reload-rules

     

2024年1月27日 星期六

量測電感與電容

 參考文章:  https://www-schiessle-de.translate.goog/emt1/MessKleinKap/MessKleinKap1.htm?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=en&_x_tr_pto=nui

討論文章: https://www.edaboard.com/threads/why-not-the-lm339-instead-of-lm311-in-aade-lc-meter-clones.183392/ 

開源碼: https://github.com/coreWeaver/LC-Meter

 

備註: 透過比較器 LM311 及 L1, C1 產生最高工作頻率 f1, 並利用準確 C0 值去校準 L1 及 C1. 透過串聯 L1 與待測電感或是並聯 C1 與待測電容, 量測出諧振頻率而計算出待測數值.  LM311 比較器可以產生 1MHz 頻率, 用 68uH/1000pF 時, 最高工作頻率約 750kHz, 若要改用 LM393 可能要修改 L1/C1 為 100uH/2nF 讓工作頻率低於 500kHz.

2024年1月16日 星期二

使用低功耗低壓差 3.3V LDO 搭配可調 Emitter follower (2N2222 npn 電晶體 + 可變電阻) 調整鋰電池電壓(3.6 ~ 4.2V) 成 1.5V

 

市面上便宜的時鐘, 內部機芯大多使用晶體振盪器產生固定頻率的脈沖去驅動 Lavet-type 步進馬達, 它只要一個 1.5V 電池就足以推動, 而馬達扭力主要是靠線圈內的電流產生磁力所致, 因此電流才是動力來源. 電壓太高會造成線圈電流飽和導致發熱,可能影響震盪頻率甚至燒毀, 使用鋰電池最怕的是電路短路產生爆炸, 下圖電路中分壓電阻 Ru 用 2k 歐姆用來限制 emiiter follower 的基極(base)電流, 可變電阻用來調整射極端輸出電壓(調整到要能同時推動時/分/秒針), 可變電阻 Rv 用 0 ~10k 歐姆, 計算出主動區輸出大約是 3.3*Rv/(Rv + Ru) - 0.6, 也就是說可調整輸出至 0 ~ 2.15V, 若無法成功運轉, 只要降低 Ru 值再調整可變電阻就可以了.




2024年1月13日 星期六

簡單電路用來測量鋰電池內電阻

 


Linux mint 的狀態欄不小心刪掉了

 參考論壇的討論文章: https://forums.linuxmint.com/viewtopic.php?p=2004336#p2004336

開啟終端機 (按快速鍵: Ctrl + alt + T), 再執行以下命令就能恢復:

    dconf reset -f /org/cinnamon/

2023年11月16日 星期四

新硬碟在 uefi bios 的主機上, 安裝 linux mint livecd, 預留空間存放 windows10 安裝檔

1. 上  Linux mint 官網下載 iso 檔:  https://linuxmint.com 將它燒錄成 DVD,  設定 UEFI BIOS 從 DVD 開機

2. 開啟終端機,  在 nvme SSD 上產生 GPT, ESP 用來安裝 grub2 啟動多重作業系統, 8G NTFS 分區先暫存 Windows 10 安裝檔, EXT4 分區用來裝 Linux 作業系統,先作好這 4 個分割區(BBP + ESP + NTFS + EXT4), 後續視需要再添加(若要安裝 Windows 10 作業系統,大概還需要 NTFS 80G 的空間 ).
sudo gdisk /dev/nvme0n1
    partition 1: 16M BBP  (0xEF02) BIOS boot partition
    partition 2: 4G  ESP  (0xEF00) EFI system partition
    partition 3: 8G  NTFS (0x0700) Microsoft basic data
    partition 4: 40G EXT4 (0x8300) Linux filesystem
sudo partprobe /dev/nvme0n1

3. 列出分割區資訊
lsblk -oname,label,size,uuid && df

4. 格式化各分割區, 並設定標籤, 將來方便管理
sudo mkfs.fat  /dev/nvme0n1p2 -n ESP -F 32
sudo mkfs.ntfs /dev/nvme0n1p3 -L NTFS
sudo mkfs.ext4 /dev/nvme0n1p4 -L LINUX

5. 掛上 ESP
sudo mkdir /mnt/nvme0n1p2 && sudo mount /dev/nvme0n1p2 /mnt/nvme0n1p2

6. 安裝 grub2 到 ESP
sudo grub-install --root-directory=/mnt/nvme0n1p2 /dev/nvme0n1

7.  可以將 livecd 複製存到 ESP 分區的目錄 /boot 裡面, 並編輯 ESP 分區內 /boot/grub/grub.cfg 檔案, 之後就能移除 DVD, 改從 SSD 快速開機
sudo dd if=/dev/loop0 of=/mnt/nvme0n1p2/boot/linuxmint21_2.iso
sudo xed /mnt/nvme0n1p2/boot/grub/grub.cfg
######## grub.cfg  檔案內容, espUUID 記得要修改!!!
    set timeout=3
    set default=0
    menuentry "Linux Mint 21.2 livecd ISO" {
        insmod iso9660
        set espUUID=D9E6-44CC
        search --no-floppy --fs-uuid --set=root $espUUID
        set isoFile=/boot/linuxmint21_2.iso
        loopback loop $isoFile
        linux  (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isoFile locale=zh_TW.UTF-8
        initrd (loop)/casper/initrd.lz
    }
######## grub.cfg 檔案結尾
 
8. 清除快取並釋放 ESP 分區, 重新開機 (設定好 UEFI BIOS boot source 從  SSD 開機)
sync && sudo umount /mnt/nvme0n1p2 && reboot

9. Linux 開完機, 可以點選桌面的安裝圖示, 將整個作業系統安裝到 EXT4 分區, 只要修改 grub.cfg, 添加啟動 Linux 項目, 其中 ext4UUID 必需修正 !!!
menuentry 'Linux Mint 21.2 Cinnamon' --hotkey=1 {
    set ext4UUID=c5bfc0ce-3f68-4bf5-9eb2-8344ff02cd03
    search --no-floppy --fs-uuid --set=root $ext4UUID
    linux    /boot/vmlinuz-5.15.0-76-generic root=UUID=$ext4UUID ro  quiet splash
    initrd    /boot/initrd.img-5.15.0-76-generic
}

10. 複製 Windows10 安裝光碟的內容, 用 SSD 也能快速安裝 Windows10
sudo mkdir /mnt/iso /mnt/nvme0n1p3
sudo mount -o loop ~/Downloads/windows10.iso /mnt/iso
sudo mount /dev/nvme0n1p3    /mnt/nvme0n1p3
sudo cp -r /mnt/iso/boot     /mnt/nvme0n1p3
sudo cp -r /mnt/iso/efi      /mnt/nvme0n1p3
sudo cp -r /mnt/iso/sources  /mnt/nvme0n1p3
sudo cp -r /mnt/iso/support  /mnt/nvme0n1p3
sudo cp /mnt/iso/bootmgr     /mnt/nvme0n1p3
sudo cp /mnt/iso/bootmgr.efi /mnt/nvme0n1p3
sudo cp /mnt/iso/setup.exe /mnt/nvme0n1p3
sudo cp /mnt/iso/autorun.inf /mnt/nvme0n1p3
sync

11. 修改 grub.cfg, 加入安裝 Windows10 選項, 其中 ntfsUUID 需修正 !!!
    menuentry "安裝 Windows 10" {
        insmod part_gpt
        set ntfsUUID=1AFC6C0B6ED4A89E
        search --no-floppy --fs-uuid --set=root $ntfsUUID
        chainloader /efi/boot/bootx64.efi
    }

12. 當安裝完 Windows10, 修改 grub.cfg, 可以自行添加啟動 Windows10 選項
    menuentry "啟動 Windows 10" {
        insmod part_gpt
        set espUUID=D9E6-44CC
        search --no-floppy --fs-uuid --set=root $espUUID
        chainloader /EFI/Microsoft/Boot/bootmgfw.efi
    }

P.S. 若安裝完 Windows10 或是 Linux mint, 導致無法透過原先的 grub2 開機, 可以調整 UEFI BIOS 開機選項, 或是乾脆使用 Linux mint 開機光碟, 進入 Linux 系統後, 再重新植入 grub2:
    sudo mkdir /mnt/nvme0n1p2
    sudo mount /dev/nvme0n1p2 /mnt/nvme0n1p2
    sudo grub-install --root-directory=/mnt/nvme0n1p2 /dev/nvme0n1


2023年8月22日 星期二

拆開濾水器罐

由於濾水器活動外殼容器(可當成螺帽來看)在下面, 拆開更換濾芯時要注意方向性. 如果由上往下俯視時,  螺帽在下面時, 轉動下面的螺帽逆時針方向是鎖死, 順時鐘方向才是打開. 如果螺帽放在上方, 正如一般螺絲頭的方向(順時針方向是鎖死, 逆時鐘方向是打開).

2023年8月13日 星期日

使用 CDS5516 晚上啟動繼電器

 

LDR CDS5516 晚上電阻大於 20k Ohm, 利用比較器 LM393 搭配 10k 可變電阻, 將它設定在 1V ~ 1.5V 啟動繼電器, 5V 繼電器開啟時, 大約消耗電流  70mA

2023年5月5日 星期五

vscode 改用 vscodium

我用舊版的 vscode,  一直以來都很習慣, 近來發現只要 vscode 一執行起來, 就會發現滑鼠莫名奇妙失焦, 有時反應遲鈍, 或是明明已經點了滑鼠左鍵卻要再點一次, 還以為滑鼠故障了, 更換新的按鍵還是一樣, 於是懷疑程式在作祟, 之前就知道 vscode 會莫名其妙連上網, 因此我有新增一個群組, 用 iptables 限制該群組的網路行動, 要執行 vscode 前先切換成該群組再去執行, 最近實在無法忍受滑鼠不受控的行為, 於是上網查是否有替代方案, 找到這篇文章:  https://opensource.com/article/20/6/open-source-alternatives-vs-code , 意思是從 MS 官網下載的 vscode 並非真正的  opensource license, 它會在有需要時就開啟 telemetry 之類一些有的沒的. 於是決定將它移除, 改用 vscodium 試看看, 到 vscodium release 網址下載 deb 安裝檔:    https://github.com/VSCodium/vscodium/releases    接著用  sudo dpkg  -i 直接安裝:
       sudo dpkg -i ~/Downloads/codium_1.77.3.23102_amd64.deb
或是依照官網 https://vscodium.com  的 3 個指示安裝最新版本:
wget -qO - https://gitlab.com/paulcarroty/vscodium-deb-rpm-repo/raw/master/pub.gpg \
| gpg --dearmor \
| sudo dd of=/usr/share/keyrings/vscodium-archive-keyring.gpg

echo 'deb [ signed-by=/usr/share/keyrings/vscodium-archive-keyring.gpg ] \
https://download.vscodium.com/debs vscodium main' \
| sudo tee /etc/apt/sources.list.d/vscodium.list

sudo apt update && sudo apt install codium

後記: . 現在使用起來, 滑鼠已經不會遲鈍, 猜測原因可能是之前 vscode 偷偷連上網路卻被 iptables 擋住, 造成系統阻塞. 刪除 vscode 轉向 vscodium 編輯文字檔真的順暢多了.

2023年5月3日 星期三

使用 Jacobi SVD 將矩陣分解成 USVᵗ

延續上一篇利用 Givens 旋轉矩陣 Gⱼ(θ), 將 GⱼᵗG 不斷夾在 IA之間, 產生兩個新矩陣, 最後在右邊產生上三角矩陣 R, 左邊自然生成一個正交矩陣 Q 的過程

           A = IA = I(GⱼᵗGⱼ)A  = (IGⱼᵗ ...) * (... Gⱼ)A  = Q * R
詳細參考 https://masontseng.blogspot.com/2023/04/c-givens-qr.html

在QR 分解過程主要是清除元素, 而 Jacobi SVD 目的是要算出左右正交矩陣, 因此計算 θ 角的方式並不同, 但 Jacobi 擺放 2x2 旋轉矩陣 Jⱼᵢ(θ) 的方式與 Givens 旋轉矩陣 Gⱼ(θ)擺放位置相同,也是在單位矩陣的對角線上,將 2x2 旋轉矩陣的元素擺放到 4 個角落: J(j,j)=cosθ, J(i,j)=-sinθ, J(j,i)=sinθ, J(i,i)=cosθ, 而且Jacobi 矩陣的轉置矩陣也是反矩陣 Jᵗ(θ)=J(θ)⁻, 也就是說 J(θ)Jᵗ(θ) = I. 利用 Jacobi 旋轉矩陣不停將一對 JⱼᵢJⱼᵢᵗ 夾在 AI 中間,產生兩個新矩陣, 詳細分解過程如下:

           A = AI = A(J₁J₁ᵗ)I  = AJ₁ * J₁ᵗI  =  (AJ₁) * (IJ₁)ᵗ
              =  (AJ₁)J₂J₂ᵗ(IJ₁)ᵗ = (AJ₁J₂) * (IJ₁J₂)ᵗ
              =  (AJ₁J₂) ... JⱼᵢJⱼᵢᵗ ... (IJ₁J₂)ᵗ
              =  (AJ₁J₂J₃ ... Jⱼᵢ) * (IJ₁J₂ ...Jⱼᵢ)ᵗ
              =  L * Vᵗ = (US)Vᵗ = USVᵗ

由此可知 A 和 I 只要分別不斷往右同時乘上一個旋轉矩陣 Jⱼᵢ(θ), 就可同時生出兩個新矩陣 L 和 Vᵗ, 這過程稱為單邊 Jacobi SVD (Singular Value Decomoposition) 分解法, 因Jacobi 矩陣 Jⱼᵢ(θ) 是正交矩陣, 透過不斷插入一對 JⱼᵢJⱼᵢᵗ 新矩陣, 右邊自然分解出一個正交矩陣 Vᵗ, 結束後, 左邊 L 矩陣再透過對角化,分解成兩個新矩陣 U 和 S(只要將 U 變成單位向量所組成的矩陣, S 就是行向量長度所組成的對角線矩陣), 為了要把左邊矩陣 L 化成正交矩陣, 旋轉角計算方式就要特別設計, 目的是將所有 L = [L̂] 內的行向量 L̂ₖ,  一一旋轉變成相互正交的行向量 L̂ᵢ, L̂ⱼ, 也就是讓 L̂ᵢ•L̂ⱼ = 0 , 之後算出每個行向量 L̂ₖ 的長度 σₖ =||L̂ₖ||₂ , 讓所有正交的行向量歸一化, 變成單位行向量 ûₖ = L̂ₖ/σₖ (從這不難看出 σ 稱為奇異值 singular value 的原因, 若 σₖ 等於 0, ûₖ 將變成無窮大的奇向量, 因此必須把奇異值 σ = 0 這一項剔除掉, 對應的 ûₖ/v̂ₖ 行向量也順勢移除, 或者將他排序過, 將奇異值 0 擺至矩陣最後面, 讓它不影響計算結果), 最後將正交單位行向量組成 U = [û] 矩陣,  奇異值 σₖ 擺至另一個矩陣的對角線上, 組成 S 矩陣: σₖ = ||L̂ₖ||₂,  S = Λ = [σ]. 

任何矩陣都可以透過 Jacobi SVD分解成 3 個 U S V 矩陣, 演算法詳見以下在第 32 頁 Algorithm 4.1 One-sided Jacobi for the singular value 文章  https://netlib.org/lapack/lawnspdf/lawn15.pdf . 底下是  Jacobi SVD 用疊代法分解的程序,算出 L, V 矩陣:
           初始化 L = A, V = I, Jⱼᵢ(θ)=[cosθ, -sinθ ; sinθ, cosθ]
           L =  L *Jⱼᵢ(θ)
           V =  V *Jⱼᵢ(θ)
// svdTest.c
#include <stdio.h>
#include <math.h>
#include <vector>
#define tol ((double)1e-12)
using namespace std;
using F_vector = vector<double>;
using F_matrix = vector<F_vector>;
void vectorShow(F_vector v) {// 列出向量元素
    for (int i = 0; i < v.size(); i ++) { printf("%-10.4f   \t", v[i]); }
    printf("\n");
}
void matrixShow(F_matrix matrix) {// 列出矩陣元素
    for (int j = 0; j < matrix.size(); j ++) {
        printf("<%-2d:>\t", j);
        vectorShow(matrix[j]);
    }
    printf("\n");
}

F_matrix transpose(F_matrix &matrix) { //  矩陣轉置
    int row = matrix.size();
    int col = matrix[0].size();
    F_matrix temp(col, F_vector(row));
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j ++) {  
            temp[j][i] = matrix[i][j];
        }
    }
    return temp;
}
F_matrix eye(int n) {// 單位矩陣
    F_matrix temp(n, F_vector(n));
    for (int i = 0; i < n; i ++) {
        for (int j = 0; j < n; j++) {
            temp[i][j] = (i == j);
        }
    }
    return temp;
}
F_matrix product(F_matrix q, F_matrix r) { // 矩陣相乘
    int dim = q[0].size();
    if (dim != r.size()) return F_matrix(0);
    int m = q.size();
    int n = r[0].size();
    F_matrix x(m, F_vector(n));
    for (int i = 0; i < m; i ++) {
        for (int j = 0; j < n; j ++) {
            double sum = 0;
            for (int k = 0; k < dim; k++) {
                sum += q[i][k] * r[k][j];
            }
            x[i][j] = sum;
        }
    }
    return x;
}
 
void jacobiSVD(F_matrix &A) {// A = USVᵗ, A -> U[σ]V
    int mm  = A.size();
    int nn  = A[0].size();
    F_matrix aU = A;// 初始化 L 左矩陣
    F_matrix aV = eye(nn);// 初始化 V 正交矩陣
    if (mm < nn) {
        aU.resize(nn);
        for (int i = mm; i < nn; i++) {  aU[i].resize(nn); }
        mm = nn;
    }
    int iterations = 30;
    while (iterations -- > 0) {
        double converge = 0;
        for (int i = 1; i < nn; i ++) {
            for (int j = 0; j < i; j ++) {
                double a = 0;
                double b = 0;
                double c = 0;
                for (int k = 0; k < mm; k ++) {
                    a += aU[k][j] * aU[k][j];// a = L̂ⱼ•L̂ⱼ
                    b += aU[k][i] * aU[k][i];// b = L̂ᵢ•L̂ᵢ
                    c += aU[k][j] * aU[k][i];// c = L̂ⱼ•L̂ᵢ = L̂ᵢ•L̂ⱼ
                }
                if ( a < tol || b < tol) { continue; }
                double s = (c < 0) ? -c / sqrt(a*b) : c / sqrt(a*b);
                if (s < tol)  { continue;  }
                if (s > converge) converge = s;
                F_matrix J = eye(nn);// 初始化旋轉矩陣 Jⱼᵢ(θ) = I
                s = (b - a) / (2 * c);// 只要算出 cosθ, sinθ, 不用算出旋轉角 θ
                double t = (s < 0) ? -1.0/(-s + sqrt(1 + s*s)) : 1.0/(s + sqrt(1 + s*s));
                c = 1 / sqrt(1 + t * t);
                s = t * c; 
                J[j][j] = c;
                J[i][j] = -s;
                J[j][i] = s;
                J[i][i] = c;
                aU = product(aU, J);// 更新 L 左矩陣
                aV = product(aV, J);// 更新 V 正交矩陣
            }
        }
        if (converge < 1e-9) break; // todo: break when stable
    }
    printf("Singular value:\nσ:\t");
    for (int i = 0; i < nn; i ++) {
        double sum = 0;
        for (int k = 0; k < mm; k ++) {
            sum += aU[k][i] * aU[k][i]; // Σ(L̂ᵢ[k])²
        }
        double sigma = sqrt(sum);// Singular value σᵢ = ||L̂ᵢ||₂ = √[Σ(L̂ᵢ[k])²]
        if (sigma > 1e-9) {// sigma != 0
            for (int k = 0; k < mm; k ++) {// ûᵢ = L̂ᵢ/σᵢ, that is why σ name "singular value"
                aU[k][i] /= sigma;
            }
        }
        printf("%-10.4f\t", sigma);
    }// todo: 未完成排序, 當 σ = 0 需刪除, 對應的 û/v̂ 向量也要跟著移除.
    printf("\n");
    matrixShow(aU);
    matrixShow(aV);
}
F_matrix arrayclone(double *array, int w, int h) {
    F_matrix temp(h, F_vector(w));
    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) { temp[i][j]= *array ++; }
    }
    return temp;
}
int main(int argc, char **argv) {
    double a[] = {
        1,  2, 3,  4, 5,
        6,  7, 8,  9,10
    };
    F_matrix B = arrayclone(a, 5, 2);
    F_matrix C = transpose(B);
    matrixShow(B);
    jacobiSVD(B);
    printf("\n======================\n");
    matrixShow(C);
    jacobiSVD(C);
}

用 python 解簡單的常微分方程式

# sudo apt install python3-pip # python3 -m venv venv # cd venv # . bin/activate # pip3 install numpy matplotlib import numpy as np import m...