> For the complete documentation index, see [llms.txt](https://docs.aic-eec.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.aic-eec.com/interfacing-with-infineon-psoc-tm-edge/hmi-development/spectrum-analyzer/ux-ui-design/noise-generator.md).

# Noise Generator

## Lab 2: Noise Generator

### Part 3 - Oscilloscope & Signal Processing

***

### 1. โครงสร้างภาพรวมของ Lab

#### Why? - ทำไมต้องเรียนรู้เรื่องนี้

* **Real-World Signals**: สัญญาณจริงในทุกระบบมี Noise ปนเสมอ ไม่ว่าจะเป็นสาย Power, Sensor Reading หรือ Communication Channel
* **Signal-to-Noise Ratio (SNR)**: SNR เป็น metric สำคัญที่สุดในวิศวกรรมไฟฟ้า ใช้วัดคุณภาพสัญญาณในระบบ Power, Audio และ Communications
* **Filter Design Foundation**: การเข้าใจ Noise เป็นพื้นฐานสำหรับการออกแบบ Filter ทั้ง Hardware (RC, LC) และ Software (Moving Average, FIR, IIR)
* **EE Application**: ในระบบ Power Quality ต้องแยก Fundamental (50Hz) ออกจาก Noise, ใน Sensor System ต้องกรอง EMI Noise ออกจากข้อมูล

#### What? - จะได้เรียนรู้อะไร

1. **White Noise Generation**: สร้าง random noise ด้วย `rand()` และเข้าใจคุณสมบัติ
2. **Gaussian-like Noise**: สร้าง noise แบบ Gaussian ด้วยเทคนิค Central Limit Theorem
3. **SNR Concept**: ทำความเข้าใจ Signal-to-Noise Ratio ทั้งแบบ Linear และ dB
4. **Signal + Noise Visualization**: แสดงสัญญาณ Clean กับ Noisy แยกหรือซ้อนกัน
5. **RMS Calculation**: วัดค่า RMS ของ Noise สำหรับคำนวณ SNR

#### How? - จะทำอย่างไร

1. สร้าง Sine wave เป็นสัญญาณ Clean
2. สร้าง White noise ด้วย `rand()`
3. รวม Clean + Noise แล้วแสดงผลบน Chart
4. เพิ่ม Slider ปรับระดับ Noise (ควบคุม SNR)
5. แสดงค่า SNR แบบ real-time

<figure><img src="https://1856353139-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MClo3nC-1US0rbK8Qau%2Fuploads%2FbENSCgTBXBN9Jhl5W70l%2Fweek5_ex2_noise_generator.gif?alt=media&amp;token=9e2dca6e-669c-4432-b0f0-e5e1714d31fc" alt=""><figcaption></figcaption></figure>

***

### 2. หลักการทำงานและ Flowchart

#### 2.1 White Noise Characteristics

```
┌─────────────────────────────────────────────────────────────┐
│                    WHITE NOISE                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Time Domain:                                              │
│                                                             │
│   ▃▅█▂▇▃▆▁▄█▅▂▇▃▅▁▄▇▂▃█▅▆▁▄▅▃▇▂█▄▅▃▆▁▇▂▅▃█▄▆▁               │
│                                                             │
│   - Random amplitude at each sample                         │
│   - No pattern or periodicity                               │
│   - Contains all frequencies equally                        │
│                                                             │
│   Frequency Domain:                                         │
│                                                             │
│   ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ← Flat spectrum         │
│   ─────────────────────────────────                         │
│   0 Hz                          Fs/2                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

#### 2.2 RMS Calculation

```
┌─────────────────────────────────────────────────────────────┐
│                  RMS (Root Mean Square)                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   Formula:                                                  │
│                                                             │
│         ┌─────────────────────────────┐                     │
│         │    1   N-1                  │                     │
│   RMS = │   ─── Σ  x[i]²              │                     │
│         │    N  i=0                   │                     │
│         └─────────────────────────────┘                     │
│                                                             │
│   Example:                                                  │
│   Samples: [100, -50, 75, -100, 50]                         │
│   Squares: [10000, 2500, 5625, 10000, 2500]                 │
│   Mean: 30625 / 5 = 6125                                    │
│   RMS: sqrt(6125) = 78.3                                    │
│                                                             │
│   For noise with amplitude ±1000:                           │
│   Theoretical RMS ≈ 1000 / √3 ≈ 577                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

***

### 3. ฟังก์ชันสำคัญ

#### 3.1 Noise Generation

<table><thead><tr><th>Function</th><th width="198.5738525390625">Description</th><th>Algorithm</th></tr></thead><tbody><tr><td><code>generate_white_noise()</code></td><td>Uniform random noise</td><td><code>rand() % (2*amp+1) - amp</code></td></tr><tr><td><code>generate_gaussian_noise()</code></td><td>Gaussian-like noise</td><td>เฉลี่ย 12 ค่า <code>rand()</code> (CLT)</td></tr><tr><td><code>calc_rms()</code></td><td>คำนวณ RMS</td><td><code>sqrt(mean(x^2))</code></td></tr><tr><td><code>calc_snr_db()</code></td><td>คำนวณ SNR (dB)</td><td><code>20*log10(rms_signal/rms_noise)</code></td></tr></tbody></table>

#### 3.2 LVGL Timer API

<table><thead><tr><th width="339.5078125">Function</th><th>Description</th></tr></thead><tbody><tr><td><code>lv_timer_create(cb, period_ms, data)</code></td><td>สร้าง timer สำหรับ real-time update</td></tr><tr><td><code>lv_timer_pause(timer)</code></td><td>หยุด timer ชั่วคราว</td></tr><tr><td><code>lv_timer_resume(timer)</code></td><td>กลับมาทำงานต่อ</td></tr></tbody></table>

***

### 4. Code เต็ม

#### 4.1 Constants and Global Variables

```c
#include "lvgl.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

#define BUFFER_SIZE     256
#define DISPLAY_POINTS  200
#define SAMPLE_RATE     10000
#define SIGNAL_FREQ     100
#define SIGNAL_AMP      1000

/* Global variables */
static int16_t clean_buffer[BUFFER_SIZE];   /* สัญญาณ Clean */
static int16_t noise_buffer[BUFFER_SIZE];   /* Noise เท่านั้น */
static int16_t noisy_buffer[BUFFER_SIZE];   /* Clean + Noise */
static lv_obj_t *ex2_chart = NULL;
static lv_chart_series_t *clean_series = NULL;  /* สีเขียว */
static lv_chart_series_t *noisy_series = NULL;  /* สีแดง */
static lv_obj_t *snr_label = NULL;
static lv_obj_t *rms_label = NULL;
static lv_obj_t *noise_val_label = NULL;
static lv_obj_t *btn_label = NULL;
static lv_timer_t *noise_timer = NULL;
static int16_t noise_amplitude = 200;
static bool noise_running = true;
static bool use_gaussian = false;
```

#### 4.2 Noise Generation Functions

```c
/* White noise: Uniform distribution */
static void generate_white_noise(int16_t *buf, uint16_t count, int16_t amp)
{
    for (uint16_t i = 0; i < count; i++) {
        buf[i] = (int16_t)((rand() % (2 * amp + 1)) - amp);
    }
}

/* Gaussian-like noise ด้วย Central Limit Theorem
 * เฉลี่ยค่า random 12 ค่า จะได้การกระจายแบบ Gaussian โดยประมาณ
 */
static void generate_gaussian_noise(int16_t *buf, uint16_t count, int16_t amp)
{
    for (uint16_t i = 0; i < count; i++) {
        int32_t sum = 0;
        for (int j = 0; j < 12; j++) {
            sum += rand() % 1001;  /* 0-1000 */
        }
        /* sum อยู่ในช่วง 0-12000, center = 6000 */
        /* scale ให้อยู่ในช่วง -amp..+amp */
        buf[i] = (int16_t)((sum - 6000) * amp / 3000);
    }
}

/* สร้าง Clean sine wave */
static void generate_clean_signal(void)
{
    for (uint16_t i = 0; i < BUFFER_SIZE; i++) {
        double t = (double)i / (double)SAMPLE_RATE;
        clean_buffer[i] = (int16_t)(SIGNAL_AMP *
                          sin(2.0 * M_PI * SIGNAL_FREQ * t));
    }
}
```

#### 4.3 RMS and SNR Calculation

```c
/* คำนวณ RMS */
static float calc_rms(const int16_t *buf, uint16_t count)
{
    double sum_sq = 0;
    for (uint16_t i = 0; i < count; i++) {
        sum_sq += (double)buf[i] * buf[i];
    }
    return (float)sqrt(sum_sq / count);
}

/* คำนวณ SNR (dB) */
static float calc_snr_db(float rms_signal, float rms_noise)
{
    if (rms_noise < 0.001f) return 99.9f;  /* Noise ต่ำมาก */
    return 20.0f * log10f(rms_signal / rms_noise);
}
```

#### 4.4 Chart Update Function

```c
static void update_display(void)
{
    if (!ex2_chart) return;

    /* สร้าง Noise */
    if (use_gaussian) {
        generate_gaussian_noise(noise_buffer, BUFFER_SIZE, noise_amplitude);
    } else {
        generate_white_noise(noise_buffer, BUFFER_SIZE, noise_amplitude);
    }

    /* รวม Clean + Noise */
    for (uint16_t i = 0; i < BUFFER_SIZE; i++) {
        noisy_buffer[i] = clean_buffer[i] + noise_buffer[i];
    }

    /* Map ค่าไปยัง Chart (ใช้ range ที่กว้างกว่าเพื่อรองรับ noise) */
    int16_t total_range = SIGNAL_AMP + noise_amplitude;
    if (total_range < SIGNAL_AMP) total_range = SIGNAL_AMP;

    for (int i = 0; i < DISPLAY_POINTS; i++) {
        /* Clean signal - สีเขียว */
        int32_t clean_val = ((int32_t)clean_buffer[i] + total_range) * 100
                            / (2 * total_range);
        if (clean_val < 0) clean_val = 0;
        if (clean_val > 100) clean_val = 100;
        lv_chart_set_value_by_id(ex2_chart, clean_series, i, clean_val);

        /* Noisy signal - สีแดง */
        int32_t noisy_val = ((int32_t)noisy_buffer[i] + total_range) * 100
                            / (2 * total_range);
        if (noisy_val < 0) noisy_val = 0;
        if (noisy_val > 100) noisy_val = 100;
        lv_chart_set_value_by_id(ex2_chart, noisy_series, i, noisy_val);
    }
    lv_chart_refresh(ex2_chart);

    /* คำนวณและแสดง RMS / SNR */
    float rms_sig = calc_rms(clean_buffer, BUFFER_SIZE);
    float rms_noi = calc_rms(noise_buffer, BUFFER_SIZE);
    float snr = calc_snr_db(rms_sig, rms_noi);

    if (snr_label) {
        lv_label_set_text_fmt(snr_label, "SNR: %.1f dB", (double)snr);
    }
    if (rms_label) {
        lv_label_set_text_fmt(rms_label, "RMS(S): %.0f  RMS(N): %.0f",
                              (double)rms_sig, (double)rms_noi);
    }
}
```

#### 4.5 Timer and Event Callbacks

```c
/* Timer callback - อัพเดทข้อมูลทุก 100ms */
static void noise_timer_cb(lv_timer_t *timer)
{
    (void)timer;
    if (!noise_running) return;
    update_display();
}

/* Noise level slider callback */
static void noise_slider_cb(lv_event_t *e)
{
    if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return;
    lv_obj_t *slider = lv_event_get_target(e);
    noise_amplitude = (int16_t)lv_slider_get_value(slider);
    lv_label_set_text_fmt(noise_val_label, "%d", (int)noise_amplitude);
    if (!noise_running) update_display();
}

/* Start/Stop button callback */
static void start_stop_cb(lv_event_t *e)
{
    if (lv_event_get_code(e) != LV_EVENT_CLICKED) return;
    noise_running = !noise_running;
    lv_label_set_text(btn_label, noise_running ? "Stop" : "Start");
}

/* Gaussian/Uniform toggle callback */
static void gaussian_toggle_cb(lv_event_t *e)
{
    if (lv_event_get_code(e) != LV_EVENT_CLICKED) return;
    use_gaussian = !use_gaussian;

    lv_obj_t *btn = lv_event_get_target(e);
    lv_obj_t *label = lv_obj_get_child(btn, 0);
    lv_label_set_text(label, use_gaussian ? "Gaussian" : "Uniform");
}
```

#### 4.6 Main Function

```c
void part3_ex2_noise_generator(void)
{
    lv_obj_t *scr = lv_screen_active();
    lv_obj_set_style_bg_color(scr, lv_color_hex(0x0a0a1e), 0);

    /* สร้าง Clean signal ครั้งเดียว */
    generate_clean_signal();

    /* ===== Title ===== */
    lv_obj_t *title = lv_label_create(scr);
    lv_label_set_text(title, "Part 3 Ex2: Noise Generator & SNR");
    lv_obj_set_style_text_color(title, lv_color_hex(0x00ff88), 0);
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 5);

    /* ===== Chart ===== */
    ex2_chart = lv_chart_create(scr);
    lv_obj_set_size(ex2_chart, 400, 150);
    lv_obj_align(ex2_chart, LV_ALIGN_TOP_MID, 0, 30);
    lv_chart_set_type(ex2_chart, LV_CHART_TYPE_LINE);
    lv_chart_set_point_count(ex2_chart, DISPLAY_POINTS);
    lv_chart_set_range(ex2_chart, LV_CHART_AXIS_PRIMARY_Y, 0, 100);
    lv_chart_set_div_line_count(ex2_chart, 4, 5);

    /* Dark oscilloscope style */
    lv_obj_set_style_bg_color(ex2_chart, lv_color_hex(0x001a00), 0);
    lv_obj_set_style_line_color(ex2_chart, lv_color_hex(0x003300), LV_PART_MAIN);
    lv_obj_set_style_border_color(ex2_chart, lv_color_hex(0x006600), 0);
    lv_obj_set_style_border_width(ex2_chart, 1, 0);
    lv_obj_set_style_size(ex2_chart, 0, 0, LV_PART_INDICATOR);

    /* Clean signal series (สีเขียว) */
    clean_series = lv_chart_add_series(ex2_chart,
                       lv_color_hex(0x00ff00), LV_CHART_AXIS_PRIMARY_Y);

    /* Noisy signal series (สีแดง) */
    noisy_series = lv_chart_add_series(ex2_chart,
                       lv_color_hex(0xff4444), LV_CHART_AXIS_PRIMARY_Y);

    /* ===== Noise Level Slider ===== */
    lv_obj_t *noise_lbl = lv_label_create(scr);
    lv_label_set_text(noise_lbl, "Noise Level:");
    lv_obj_set_style_text_color(noise_lbl, lv_color_hex(0xaaaaaa), 0);
    lv_obj_align(noise_lbl, LV_ALIGN_LEFT_MID, 10, 50);

    lv_obj_t *noise_slider = lv_slider_create(scr);
    lv_slider_set_range(noise_slider, 0, 1000);
    lv_slider_set_value(noise_slider, noise_amplitude, LV_ANIM_OFF);
    lv_obj_set_width(noise_slider, 200);
    lv_obj_align(noise_slider, LV_ALIGN_CENTER, 20, 50);
    lv_obj_add_event_cb(noise_slider, noise_slider_cb,
                        LV_EVENT_VALUE_CHANGED, NULL);

    noise_val_label = lv_label_create(scr);
    lv_label_set_text_fmt(noise_val_label, "%d", (int)noise_amplitude);
    lv_obj_set_style_text_color(noise_val_label, lv_color_hex(0xff6666), 0);
    lv_obj_align(noise_val_label, LV_ALIGN_RIGHT_MID, -15, 50);

    /* ===== SNR and RMS Labels ===== */
    snr_label = lv_label_create(scr);
    lv_label_set_text(snr_label, "SNR: -- dB");
    lv_obj_set_style_text_color(snr_label, lv_color_hex(0x00ffff), 0);
    lv_obj_set_style_text_font(snr_label, &lv_font_montserrat_16, 0);
    lv_obj_align(snr_label, LV_ALIGN_BOTTOM_LEFT, 10, -40);

    rms_label = lv_label_create(scr);
    lv_label_set_text(rms_label, "RMS(S): --  RMS(N): --");
    lv_obj_set_style_text_color(rms_label, lv_color_hex(0x888888), 0);
    lv_obj_align(rms_label, LV_ALIGN_BOTTOM_RIGHT, -10, -40);

    /* ===== Control Buttons ===== */
    /* Start/Stop */
    lv_obj_t *btn_ss = lv_btn_create(scr);
    lv_obj_set_size(btn_ss, 80, 30);
    lv_obj_align(btn_ss, LV_ALIGN_BOTTOM_LEFT, 10, -5);
    lv_obj_add_event_cb(btn_ss, start_stop_cb, LV_EVENT_CLICKED, NULL);

    btn_label = lv_label_create(btn_ss);
    lv_label_set_text(btn_label, "Stop");
    lv_obj_center(btn_label);

    /* Gaussian/Uniform toggle */
    lv_obj_t *btn_gauss = lv_btn_create(scr);
    lv_obj_set_size(btn_gauss, 100, 30);
    lv_obj_align(btn_gauss, LV_ALIGN_BOTTOM_LEFT, 100, -5);
    lv_obj_add_event_cb(btn_gauss, gaussian_toggle_cb,
                        LV_EVENT_CLICKED, NULL);

    lv_obj_t *gauss_lbl = lv_label_create(btn_gauss);
    lv_label_set_text(gauss_lbl, "Uniform");
    lv_obj_center(gauss_lbl);

    /* ===== Timer ===== */
    noise_timer = lv_timer_create(noise_timer_cb, 100, NULL);

    /* แสดงผลครั้งแรก */
    update_display();
}
```

***

### 5. องค์ความรู้และเทคนิค

#### 5.1 Pseudo-Random Number Generator (PRNG)

```c
/* rand() ใน C stdlib เป็น Pseudo-Random Number Generator
 * ให้ลำดับตัวเลขที่ดูสุ่ม แต่จริงๆ เป็น deterministic
 *
 * Simple Linear Congruential Generator:
 * X(n+1) = (a * X(n) + c) mod m
 *
 * สำหรับ White Noise:
 * - ทุกค่ามีโอกาสเกิดเท่ากัน (Uniform Distribution)
 * - ไม่มี pattern หรือ periodicity
 * - Flat frequency spectrum (พลังงานเท่ากันทุกความถี่)
 *
 * การตั้ง seed:
 * srand(42);  // ให้ผลซ้ำเดิมทุกครั้ง (ดีสำหรับ debug)
 * srand(time(NULL));  // ให้ผลต่างทุกครั้ง
 */
```

#### 5.2 Central Limit Theorem สำหรับ Gaussian Noise

```c
/* Central Limit Theorem (CLT):
 * ถ้าเฉลี่ยค่าสุ่มหลายๆ ค่า ผลลัพธ์จะเข้าใกล้ Gaussian Distribution
 *
 * เทคนิค: เฉลี่ย 12 ค่า rand() (เลือก 12 เพราะ variance = 1)
 *
 * ข้อดี:
 * - ไม่ต้องใช้ Box-Muller transform (ซับซ้อน)
 * - เหมาะกับ Embedded ที่มี resource จำกัด
 *
 * ข้อเสีย:
 * - ไม่ใช่ Gaussian จริงๆ (tail ถูกตัด)
 * - ต้องเรียก rand() 12 ครั้งต่อ sample
 */
```

#### 5.3 SNR in Decibels

```c
/* SNR สามารถแสดงเป็น dB (Decibel) ซึ่งเป็น logarithmic scale
 *
 * SNR_dB = 20 * log10(A_signal / A_noise)     [Amplitude]
 * SNR_dB = 10 * log10(P_signal / P_noise)     [Power]
 *
 * ข้อดีของ dB:
 * - ช่วงกว้าง (0.001 ถึง 1000000) แสดงเป็นตัวเลขเล็กๆ ได้
 * - การคูณกลายเป็นการบวก (cascade system)
 *
 * ตัวอย่าง:
 * A_signal = 1000, A_noise = 100
 * SNR = 20 * log10(1000/100) = 20 * log10(10) = 20 dB
 *
 * A_signal = 1000, A_noise = 10
 * SNR = 20 * log10(1000/10) = 20 * log10(100) = 40 dB
 */
```

#### 5.4 Start/Stop Pattern สำหรับ Real-time Display

```c
/* Pattern สำหรับ Timer-based real-time update:
 *
 * 1. สร้าง timer ด้วย lv_timer_create()
 * 2. ใน callback ตรวจ running flag
 * 3. ปุ่ม toggle เปลี่ยน flag
 *
 * เป็น pattern มาตรฐานที่ใช้ได้กับทุก Lab
 */
static bool running = true;

void timer_cb(lv_timer_t *t) {
    if (!running) return;   /* ข้ามถ้าหยุดอยู่ */
    /* ... update data and display ... */
}

void toggle_cb(lv_event_t *e) {
    running = !running;
    lv_label_set_text(label, running ? "Stop" : "Start");
}
```

***

### 6. แบบฝึกหัด

#### Exercise 1: Adjustable SNR Display (ปรับ SNR ด้วย Slider)

**โจทย์:** แก้ไขให้ Slider แสดงเป็น SNR (dB) โดยตรงแทนที่จะเป็น Noise Level

* Slider ช่วง 0-60 dB
* คำนวณ noise\_amplitude จาก SNR ที่ต้องการ: `noise_amp = signal_amp / 10^(SNR_dB/20)`
* เมื่อ SNR = 0 dB, noise amplitude = signal amplitude
* เมื่อ SNR = 60 dB, noise amplitude ต่ำมาก (เกือบ clean)
* แสดง color indicator: Green (>30dB), Yellow (10-30dB), Red (<10dB)

**คำใบ้:**

```c
/* คำนวณ noise amplitude จาก SNR */
float snr_linear = powf(10.0f, snr_db / 20.0f);
int16_t noise_amp = (int16_t)(SIGNAL_AMP / snr_linear);
```

#### Exercise 2: Moving Average Filter (ตัว Filter พื้นฐาน)

**โจทย์:** เพิ่ม Moving Average Filter เพื่อลด noise และแสดงผลลัพธ์

* เพิ่ม series ที่ 3 (สีฟ้า) สำหรับสัญญาณที่ผ่าน filter
* ใช้ Moving Average ขนาด N (เลือกจาก dropdown: 3, 5, 7, 11, 15)
* แสดง SNR ก่อนและหลัง filter เปรียบเทียบ
* สังเกตว่า filter ทำให้สัญญาณ delay และสูญเสีย high-frequency

**คำใบ้:**

```c
/* Simple Moving Average Filter */
void moving_average(const int16_t *input, int16_t *output,
                    uint16_t count, uint8_t window)
{
    for (int i = 0; i < count; i++) {
        int32_t sum = 0;
        int start = i - window / 2;
        int end = i + window / 2;
        int valid = 0;
        for (int j = start; j <= end; j++) {
            if (j >= 0 && j < count) {
                sum += input[j];
                valid++;
            }
        }
        output[i] = (int16_t)(sum / valid);
    }
}
```

***

### 7. References

* [White Noise](https://en.wikipedia.org/wiki/White_noise)
* [RMS Calculation](https://en.wikipedia.org/wiki/Root_mean_square)
* [LVGL Button](https://docs.lvgl.io/9.2/widgets/button.html)

***
