# 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="/files/iUJDGZjz2lgNU7jjKfhP" 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)

***


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aic-eec.com/interfacing-with-infineon-psoc-tm-edge/hmi-development/spectrum-analyzer/ux-ui-design/noise-generator.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
