Chapter 16: Discrete-Time and Sampled-Data System Dynamics

Lesson 1: Sampling Process, Aliasing, and Sample-and-Hold Devices

This lesson develops the mathematical foundations of periodic sampling and sample-and-hold interfaces for dynamic systems. We derive spectral replication, explain aliasing as a deterministic folding phenomenon, and model zero-order hold devices as continuous-time dynamical operators that shape intersample behavior in sampled-data systems.

1. Conceptual Overview

In a sampled-data system, the physical plant is often continuous-time, but sensing, computation, and actuation are mediated by periodic sampling and holding. If \( x(t) \) is a continuous signal and \( T_s \) is the sampling period, then the discrete sequence is \( x[k]=x(kT_s) \). The fundamental issue is that sampling is not merely “taking points”; in the frequency domain it creates shifted spectral copies. If these copies overlap, aliasing occurs and information is irreversibly mixed.

On the actuation side, a digital controller typically outputs a sequence \( u[k] \), while the plant requires a continuous signal \( u(t) \). A sample-and-hold device (most commonly a zero-order hold) converts the sequence into a piecewise-constant waveform. This intersample waveform matters because the continuous-time plant evolves between sampling instants.

flowchart TD
  A["Continuous signal x(t)"] --> B["Anti-alias filter"]
  B --> C["Sampler (period Ts)"]
  C --> D["Sequence x[k]"]
  D --> E["Digital computation"]
  E --> F["Sequence u[k]"]
  F --> G["Hold device"]
  G --> H["Continuous input u(t) to plant"]
  H --> I["Continuous plant dynamics"]
  I --> A
        

2. Ideal Sampling as Multiplication by an Impulse Train

The mathematically ideal sampler is represented by a Dirac impulse train (also called a comb):

\[ p_{T_s}(t)=\sum_{k=-\infty}^{\infty} \delta(t-kT_s). \]

Multiplying the continuous signal by the comb produces the sampled impulse signal:

\[ x_s(t)=x(t)p_{T_s}(t)=\sum_{k=-\infty}^{\infty} x(kT_s)\,\delta(t-kT_s) =\sum_{k=-\infty}^{\infty} x[k] \, \delta(t-kT_s). \]

This representation is the most useful one for analysis because multiplication in time becomes convolution in frequency. Let \( \omega_s = 2\pi/T_s \) denote the angular sampling frequency. The Fourier transform of the impulse train is

\[ P_{T_s}(j\omega)=\frac{2\pi}{T_s} \sum_{m=-\infty}^{\infty} \delta(\omega-m\omega_s). \]

Therefore, the sampling operation replicates the spectrum of \( x(t) \) at all integer multiples of \( \omega_s \).

3. Spectral Replication and the Aliasing Mechanism

Let \( X(j\omega) \) be the Fourier transform of \( x(t) \). Using the product-convolution property:

\[ X_s(j\omega)=\frac{1}{2\pi}\left(X(j\omega) * P_{T_s}(j\omega)\right). \]

Substituting the comb spectrum gives the standard spectral replication formula:

\[ X_s(j\omega)=\frac{1}{T_s}\sum_{m=-\infty}^{\infty} X\big(j(\omega-m\omega_s)\big). \]

Proof sketch. Convolution with a shifted impulse shifts the function, i.e., \( f(\omega)*\delta(\omega-\omega_0)=f(\omega-\omega_0) \). The factor \( 2\pi/T_s \) from \( P_{T_s}(j\omega) \) combines with \( 1/(2\pi) \) to yield \( 1/T_s \).

If the original signal is bandlimited to \( |\omega| \leq \Omega_B \), then there is no overlap among adjacent spectral copies provided

\[ \omega_s > 2\Omega_B \quad \Longleftrightarrow \quad f_s > 2B, \]

where \( B=\Omega_B/(2\pi) \). This is the Nyquist sampling condition. If instead \( \omega_s \leq 2\Omega_B \), the shifted spectra overlap and the baseband copy is contaminated by higher images. That distortion is aliasing.

For a pure sinusoid \( x(t)=A\sin(2\pi f_0 t) \), the sampled sequence is identical for infinitely many continuous-time frequencies. In particular,

\[ \sin(2\pi f_0 kT_s)=\sin\big(2\pi (f_0-mf_s)kT_s\big), \qquad m\in\mathbb{Z}. \]

Thus, the observable frequency in the principal Nyquist interval \( [0,f_s/2] \) is the folded (aliased) frequency obtained by reflecting the true frequency around integer multiples of \( f_s \).

4. Reconstruction Idealization and Anti-Alias Filter Design Logic

If \( x(t) \) is bandlimited and \( f_s > 2B \), an ideal low-pass reconstruction filter can recover the original signal from the impulse samples exactly. In the time domain, the classical sinc interpolation formula is

\[ x(t)=\sum_{k=-\infty}^{\infty} x[k]\, \operatorname{sinc}\!\left(\frac{t-kT_s}{T_s}\right), \]

where \( \operatorname{sinc}(\xi)=\dfrac{\sin(\pi \xi)}{\pi \xi} \). In practice, no physical anti-alias filter is ideal, so engineers choose a sampling frequency high enough to leave a transition band between the signal bandwidth and the Nyquist frequency.

A common design rule is: (i) determine the highest useful signal frequency \( B_{sig} \), (ii) choose an analog low-pass anti-alias filter cutoff \( B_c \gtrsim B_{sig} \), and (iii) choose \( f_s \) so that the filter's stopband attenuation is adequate by the time frequency reaches \( f_s/2 \).

This is why real instrumentation systems often sample at rates much higher than \( 2B_{sig} \): not because the theorem is wrong, but because realizable filters have finite roll-off.

5. Sample-and-Hold Devices and Zero-Order Hold Modeling

A sample-and-hold device captures a sample and maintains it for a finite interval until the next update. In control actuation, the most common model is the zero-order hold (ZOH), which outputs a piecewise-constant signal:

\[ u_h(t)=u[k], \qquad kT_s \leq t < (k+1)T_s. \]

Equivalently, the ZOH output can be written as

\[ u_h(t)=\sum_{k=-\infty}^{\infty} u[k]\,h_0(t-kT_s), \]

where the hold kernel is a rectangular pulse

\[ h_0(t)= \begin{cases} 1, & 0 \leq t < T_s, \\ 0, & \text{otherwise}. \end{cases} \]

In terms of step functions \( \mathbf{u}(t) \) (unit step), this is

\[ h_0(t)=\mathbf{u}(t)-\mathbf{u}(t-T_s). \]

flowchart TD
  A["Sequence u[k]"] --> B["DAC latch"]
  B --> C["Hold capacitor / buffer"]
  C --> D["Piecewise-constant output u_h(t)"]
  D --> E["Continuous plant G(s)"]
  E --> F["Continuous response y(t)"]
  F --> G["Sampler every Ts"]
  G --> H["Sequence y[k]"]
        

Important modeling point. The ZOH is not a mere plotting convention. It changes the continuous-time signal presented to the plant and introduces amplitude droop and phase lag that become increasingly relevant as frequency approaches the Nyquist limit.

6. Derivation of the Zero-Order Hold Transfer Function

The Laplace transform of the ZOH kernel \( h_0(t)=\mathbf{u}(t)-\mathbf{u}(t-T_s) \) is

\[ H_0(s)=\mathcal{L}\{h_0(t)\} =\int_0^{T_s} e^{-st}\,dt =\frac{1-e^{-sT_s}}{s}. \]

This is the continuous-time transfer function from an impulse-weighted sample train to the held waveform. Evaluating on the imaginary axis gives the frequency response:

\[ H_0(j\omega)=\frac{1-e^{-j\omega T_s}}{j\omega} =e^{-j\omega T_s/2}\,\frac{2\sin(\omega T_s/2)}{\omega}. \]

Using the unnormalized sinc function \( \operatorname{sinc}_u(x)=\sin(x)/x \),

\[ H_0(j\omega)=e^{-j\omega T_s/2}\,T_s\,\operatorname{sinc}_u\!\left(\frac{\omega T_s}{2}\right). \]

If the DAC chain is normalized by \( 1/T_s \), the effective ZOH factor becomes

\[ \tilde{H}_0(j\omega)=e^{-j\omega T_s/2} \operatorname{sinc}_u\!\left(\frac{\omega T_s}{2}\right), \]

which makes the low-frequency gain approach unity. The factor \( e^{-j\omega T_s/2} \) behaves like an approximate delay of \( T_s/2 \), while the sinc term causes high-frequency attenuation (“hold droop”).

7. Sampled-Data Interpretation in Control-Oriented Dynamics

Suppose the plant is a continuous-time LTI model \( G(s) \) and the controller computes \( u[k] \) every \( T_s \) seconds. Then the actual plant input is \( u_h(t) \), not the sequence itself. The state trajectory evolves continuously between samples according to the plant differential equations driven by a constant input over each interval:

\[ \dot{\mathbf{x}}(t)=\mathbf{A}\mathbf{x}(t)+\mathbf{B}u[k], \qquad kT_s \leq t < (k+1)T_s. \]

This piecewise-constant forcing is exactly what links continuous-time dynamics (already studied in previous chapters) to the discrete-time models that will be developed in the next lessons. The hold device is therefore part of the plant-interface dynamics and must be included in any accurate sampled-data analysis.

From an engineering viewpoint, sampling frequency selection is a multi-constraint problem: anti-aliasing on the sensing side, hold-induced distortion on the actuation side, computational load, and desired closed-loop bandwidth. A common control heuristic is to sample at least 10–20 times faster than the dominant closed-loop bandwidth to reduce intersample distortion and phase lag, while still respecting noise and hardware constraints.

8. Python Implementation

The following script synthesizes a signal with one in-band sinusoid and one out-of-band sinusoid, samples it, computes the aliased component, and visualizes the zero-order hold waveform and spectra.

Chapter16_Lesson1.py


\
# Chapter16_Lesson1.py
"""
Sampling, aliasing, and zero-order hold demo for Chapter 16 Lesson 1.
Generates:
  1) time-domain plot (continuous, samples, and ZOH output)
  2) magnitude spectrum plot (continuous and sampled)
  3) CSV file with sampled and held values
"""

import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path


def alias_frequency(f0, fs):
    """Return folded alias frequency in [0, fs/2]."""
    m = int(np.round(f0 / fs))
    fa = abs(f0 - m * fs)
    if fa > fs / 2:
        fa = fs - fa
    return fa


# Parameters
fs = 80.0                  # sampling frequency [Hz]
Ts = 1.0 / fs
f1 = 12.0                  # in-band component
f2 = 55.0                  # above Nyquist -> aliases to 25 Hz for fs=80
A1, A2 = 1.0, 0.7
duration = 0.25            # seconds

# "Continuous-time" dense grid for visualization
fdense = 5000.0
td = np.arange(0.0, duration, 1.0 / fdense)
x_cont = A1 * np.sin(2 * np.pi * f1 * td) + A2 * np.sin(2 * np.pi * f2 * td)

# Samples
n = np.arange(0, int(duration * fs))
ts = n * Ts
x_samp = A1 * np.sin(2 * np.pi * f1 * ts) + A2 * np.sin(2 * np.pi * f2 * ts)

# Zero-order hold reconstruction on dense grid
# For each dense-time point t, hold the most recent sample x[k] for kTs <= t < (k+1)Ts
k_idx = np.floor(td / Ts).astype(int)
k_idx = np.clip(k_idx, 0, len(x_samp) - 1)
x_zoh = x_samp[k_idx]

# FFT for spectral picture (zero-pad for smooth display)
Nfft = 4096
Xc = np.fft.rfft(x_cont, n=Nfft)
Xs = np.fft.rfft(x_samp, n=Nfft)
fc = np.fft.rfftfreq(Nfft, d=1.0 / fdense)
fspec = np.fft.rfftfreq(Nfft, d=1.0 / fs)

# Save sampled and held values to CSV
out_dir = Path(".")
csv_path = out_dir / "Chapter16_Lesson1_samples.csv"
with csv_path.open("w", encoding="utf-8") as f:
    f.write("t_dense,x_cont,x_zoh\n")
    for t_i, xc_i, xh_i in zip(td, x_cont, x_zoh):
        f.write(f"{t_i:.8f},{xc_i:.8f},{xh_i:.8f}\n")
    f.write("\n")
    f.write("k,ts,x_sample\n")
    for k, t_k, x_k in zip(n, ts, x_samp):
        f.write(f"{k},{t_k:.8f},{x_k:.8f}\n")

print(f"Alias of {f2:.1f} Hz at fs={fs:.1f} Hz is {alias_frequency(f2, fs):.1f} Hz")
print(f"CSV saved: {csv_path.resolve()}")

# Plot 1: Time-domain waveform and ZOH
plt.figure(figsize=(10, 4.8))
plt.plot(td, x_cont, label="Continuous (dense reference)")
plt.step(td, x_zoh, where="post", label="ZOH output")
plt.plot(ts, x_samp, "o", label="Samples")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude")
plt.title("Sampling and Zero-Order Hold")
plt.grid(True, alpha=0.3)
plt.legend()
plt.tight_layout()
plt.savefig("Chapter16_Lesson1_time.png", dpi=150)

# Plot 2: Spectra
plt.figure(figsize=(10, 4.8))
plt.plot(fc, np.abs(Xc), label="Dense-grid spectrum")
plt.plot(fspec, np.abs(Xs), label="Sampled-sequence spectrum")
plt.xlim(0, 120)
plt.xlabel("Frequency [Hz]")
plt.ylabel("Magnitude (unnormalized)")
plt.title("Aliasing in the Frequency Domain")
plt.grid(True, alpha=0.3)
plt.legend()
plt.tight_layout()
plt.savefig("Chapter16_Lesson1_spectrum.png", dpi=150)

plt.show()

      

9. C++ Implementation

This C++ program generates sampled and ZOH data and exports a CSV file for plotting in any external tool (MATLAB, Python, spreadsheet, etc.).

Chapter16_Lesson1.cpp


\
/* Chapter16_Lesson1.cpp
   Sampling, aliasing, and zero-order hold data generation.
   Compile (example): g++ -std=c++17 Chapter16_Lesson1.cpp -O2 -o Chapter16_Lesson1
*/

#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <vector>

double aliasFrequency(double f0, double fs) {
    long m = static_cast<long>(std::llround(f0 / fs));
    double fa = std::fabs(f0 - m * fs);
    if (fa > fs / 2.0) {
        fa = fs - fa;
    }
    return fa;
}

int main() {
    const double fs = 80.0;
    const double Ts = 1.0 / fs;
    const double f1 = 12.0;
    const double f2 = 55.0;
    const double A1 = 1.0;
    const double A2 = 0.7;
    const double duration = 0.25;

    const double fdense = 5000.0;
    const double dt = 1.0 / fdense;
    const int Nd = static_cast<int>(duration * fdense);
    const int Ns = static_cast<int>(duration * fs);

    std::vector<double> td(Nd), xCont(Nd), xZoh(Nd);
    std::vector<double> ts(Ns), xSamp(Ns);

    // Sampled sequence
    for (int k = 0; k < Ns; ++k) {
        ts[k] = k * Ts;
        xSamp[k] = A1 * std::sin(2.0 * M_PI * f1 * ts[k]) +
                   A2 * std::sin(2.0 * M_PI * f2 * ts[k]);
    }

    // Dense reference and ZOH
    for (int i = 0; i < Nd; ++i) {
        td[i] = i * dt;
        xCont[i] = A1 * std::sin(2.0 * M_PI * f1 * td[i]) +
                   A2 * std::sin(2.0 * M_PI * f2 * td[i]);

        int k = static_cast<int>(std::floor(td[i] / Ts));
        if (k < 0) k = 0;
        if (k >= Ns) k = Ns - 1;
        xZoh[i] = xSamp[k];
    }

    // Export CSV for plotting in any tool
    std::ofstream file("Chapter16_Lesson1_cpp_output.csv");
    file << "t_dense,x_cont,x_zoh\n";
    file << std::fixed << std::setprecision(8);
    for (int i = 0; i < Nd; ++i) {
        file << td[i] << "," << xCont[i] << "," << xZoh[i] << "\n";
    }
    file << "\n";
    file << "k,ts,x_sample\n";
    for (int k = 0; k < Ns; ++k) {
        file << k << "," << ts[k] << "," << xSamp[k] << "\n";
    }
    file.close();

    std::cout << "Sampling frequency fs = " << fs << " Hz\n";
    std::cout << "Nyquist frequency = " << fs / 2.0 << " Hz\n";
    std::cout << "Input component f2 = " << f2 << " Hz aliases to "
              << aliasFrequency(f2, fs) << " Hz\n";
    std::cout << "CSV written to Chapter16_Lesson1_cpp_output.csv\n";

    return 0;
}

      

10. Java Implementation

The Java implementation mirrors the C++ workflow and is useful for students building simulation utilities without external dependencies.

Chapter16_Lesson1.java


\
/* Chapter16_Lesson1.java
   Sampling, aliasing, and zero-order hold data generation.
   Compile: javac Chapter16_Lesson1.java
   Run:     java Chapter16_Lesson1
*/

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class Chapter16_Lesson1 {

    public static double aliasFrequency(double f0, double fs) {
        long m = Math.round(f0 / fs);
        double fa = Math.abs(f0 - m * fs);
        if (fa > fs / 2.0) {
            fa = fs - fa;
        }
        return fa;
    }

    public static void main(String[] args) {
        final double fs = 80.0;
        final double Ts = 1.0 / fs;
        final double f1 = 12.0;
        final double f2 = 55.0;
        final double A1 = 1.0;
        final double A2 = 0.7;
        final double duration = 0.25;

        final double fdense = 5000.0;
        final double dt = 1.0 / fdense;
        final int Nd = (int) (duration * fdense);
        final int Ns = (int) (duration * fs);

        double[] td = new double[Nd];
        double[] xCont = new double[Nd];
        double[] xZoh = new double[Nd];
        double[] ts = new double[Ns];
        double[] xSamp = new double[Ns];

        for (int k = 0; k < Ns; k++) {
            ts[k] = k * Ts;
            xSamp[k] = A1 * Math.sin(2.0 * Math.PI * f1 * ts[k]) +
                       A2 * Math.sin(2.0 * Math.PI * f2 * ts[k]);
        }

        for (int i = 0; i < Nd; i++) {
            td[i] = i * dt;
            xCont[i] = A1 * Math.sin(2.0 * Math.PI * f1 * td[i]) +
                       A2 * Math.sin(2.0 * Math.PI * f2 * td[i]);

            int k = (int) Math.floor(td[i] / Ts);
            if (k < 0) k = 0;
            if (k >= Ns) k = Ns - 1;
            xZoh[i] = xSamp[k];
        }

        try (PrintWriter out = new PrintWriter(new FileWriter("Chapter16_Lesson1_java_output.csv"))) {
            out.println("t_dense,x_cont,x_zoh");
            for (int i = 0; i < Nd; i++) {
                out.printf("%.8f,%.8f,%.8f%n", td[i], xCont[i], xZoh[i]);
            }
            out.println();
            out.println("k,ts,x_sample");
            for (int k = 0; k < Ns; k++) {
                out.printf("%d,%.8f,%.8f%n", k, ts[k], xSamp[k]);
            }
        } catch (IOException e) {
            System.err.println("Failed to write CSV: " + e.getMessage());
            return;
        }

        System.out.printf("Sampling frequency fs = %.1f Hz%n", fs);
        System.out.printf("Nyquist frequency = %.1f Hz%n", fs / 2.0);
        System.out.printf("Input component %.1f Hz aliases to %.1f Hz%n", f2, aliasFrequency(f2, fs));
        System.out.println("CSV written to Chapter16_Lesson1_java_output.csv");
    }
}

      

11. MATLAB / Simulink Implementation

The MATLAB script below generates the same experiment and includes a short note for reproducing the pipeline in Simulink with standard blocks (Sine Wave, Sum, Zero-Order Hold, Scope, Spectrum Analyzer).

Chapter16_Lesson1.m


\
% Chapter16_Lesson1.m
% Sampling, aliasing, and zero-order hold demonstration (MATLAB/Simulink companion)

clear; clc; close all;

fs = 80;                 % sampling frequency [Hz]
Ts = 1/fs;
f1 = 12;                 % in-band component
f2 = 55;                 % aliases because f2 > fs/2
A1 = 1.0; A2 = 0.7;
duration = 0.25;

fdense = 5000;
td = 0:1/fdense:duration;
x_cont = A1*sin(2*pi*f1*td) + A2*sin(2*pi*f2*td);

n = 0:floor(duration*fs)-1;
ts = n*Ts;
x_samp = A1*sin(2*pi*f1*ts) + A2*sin(2*pi*f2*ts);

% Zero-order hold on dense grid
k = floor(td/Ts) + 1;
k(k < 1) = 1;
k(k > numel(x_samp)) = numel(x_samp);
x_zoh = x_samp(k);

% Alias frequency utility
m = round(f2/fs);
f_alias = abs(f2 - m*fs);
if f_alias > fs/2
    f_alias = fs - f_alias;
end
fprintf('Alias of %.1f Hz at fs=%.1f Hz is %.1f Hz\n', f2, fs, f_alias);

% Time-domain plot
figure;
plot(td, x_cont, 'LineWidth', 1.2); hold on;
stairs(td, x_zoh, 'LineWidth', 1.1);
plot(ts, x_samp, 'o', 'MarkerSize', 5);
grid on;
xlabel('Time [s]');
ylabel('Amplitude');
title('Sampling and Zero-Order Hold');
legend('Continuous (dense reference)', 'ZOH output', 'Samples', 'Location', 'best');

% Spectrum plot (simple FFT view)
Nfft = 4096;
Xc = fft(x_cont, Nfft);
Xs = fft(x_samp, Nfft);
fc = (0:Nfft/2) * (fdense/Nfft);
fspec = (0:Nfft/2) * (fs/Nfft);

figure;
plot(fc, abs(Xc(1:Nfft/2+1)), 'LineWidth', 1.1); hold on;
plot(fspec, abs(Xs(1:Nfft/2+1)), 'LineWidth', 1.1);
xlim([0 120]);
grid on;
xlabel('Frequency [Hz]');
ylabel('Magnitude (unnormalized)');
title('Aliasing in the Frequency Domain');
legend('Dense-grid spectrum', 'Sampled-sequence spectrum', 'Location', 'best');

% Export CSV
T1 = table(td(:), x_cont(:), x_zoh(:), 'VariableNames', {'t_dense','x_cont','x_zoh'});
T2 = table(n(:), ts(:), x_samp(:), 'VariableNames', {'k','ts','x_sample'});
writetable(T1, 'Chapter16_Lesson1_matlab_dense.csv');
writetable(T2, 'Chapter16_Lesson1_matlab_samples.csv');

% Simulink note:
% You can reproduce the same experiment with blocks:
% Sine Wave (12 Hz) + Sine Wave (55 Hz) -> Sum -> Zero-Order Hold (Ts=1/fs)
% -> Scope / Spectrum Analyzer

      

12. Wolfram Mathematica Implementation

The notebook content below computes sampled values, constructs a ZOH waveform, and visualizes continuous, sampled, and held signals.

Chapter16_Lesson1.nb


\
Notebook[{
  Cell["Chapter16_Lesson1.nb", "Title"],
  Cell["Sampling, Aliasing, and Zero-Order Hold Demo", "Section"],
  Cell[BoxData[
    RowBox[{
      RowBox[{"fs","=","80."}],";",
      RowBox[{"Ts","=","1/fs"}],";",
      RowBox[{"f1","=","12."}],";",
      RowBox[{"f2","=","55."}],";",
      RowBox[{"A1","=","1."}],";",
      RowBox[{"A2","=","0.7"}],";",
      RowBox[{"dur","=","0.25"}],";"
    }]
  ], "Input"],
  Cell[BoxData[
    RowBox[{
      RowBox[{"x","[","t_","]"}],":=",
      RowBox[{"A1"," ","Sin","[",RowBox[{"2"," ","Pi"," ","f1"," ","t"}],"]","+",
             "A2"," ","Sin","[",RowBox[{"2"," ","Pi"," ","f2"," ","t"}],"]"}]
    }]
  ], "Input"],
  Cell[BoxData[
    RowBox[{
      RowBox[{"aliasFreq","[",RowBox[{"f0_",",","fs_"}],"]"}],":=",
      RowBox[{"Module","[",
        RowBox[{
          RowBox[{"{","m,fa","}"}],",",
          RowBox[{
            RowBox[{"m","=","Round","[",RowBox[{"f0/fs"}],"]"}],";",
            RowBox[{"fa","=","Abs","[",RowBox[{"f0","-","m"," ","fs"}],"]"}],";",
            RowBox[{"If","[",RowBox[{
              RowBox[{"fa",">","fs/2"}],",",
              RowBox[{"fa","=","fs-fa"}]
            }],"]"}],";",
            "fa"
          }]
        }],"]"}]
    }]
  ], "Input"],
  Cell[BoxData[
    RowBox[{"aliasFreq","[",RowBox[{"f2",",","fs"}],"]"}]
  ], "Input"],
  Cell[BoxData[
    RowBox[{
      RowBox[{"ts","=",
        RowBox[{"Range","[",RowBox[{"0",",",RowBox[{"dur-Ts"}],",","Ts"}],"]"}]}],";",
      RowBox[{"xs","=",RowBox[{"x","/@","ts"}]}],";",
      RowBox[{"td","=",RowBox[{"Range","[",RowBox[{"0",",","dur",",","0.0002"}],"]"}]}],";"
    }]
  ], "Input"],
  Cell[BoxData[
    RowBox[{
      RowBox[{"zoh","[","t_","]"}],":=",
      RowBox[{"Module","[",
        RowBox[{
          RowBox[{"{","k","}"}],",",
          RowBox[{
            RowBox[{"k","=",
              RowBox[{"Floor","[",RowBox[{"t/Ts"}],"]","+","1"}]}],";",
            RowBox[{"k","=","Clip","[",RowBox[{"k",",",RowBox[{"{","1",",",RowBox[{"Length","[","xs","]"}],"}"}]}],"]"}],";",
            RowBox[{"xs","[","[","k","]","]"}]
          }]
        }],"]"}]
    }]
  ], "Input"],
  Cell[BoxData[
    RowBox[{
      RowBox[{"xz","=",RowBox[{"zoh","/@","td"}]}],";",
      RowBox[{"xc","=",RowBox[{"x","/@","td"}]}],";"
    }]
  ], "Input"],
  Cell[BoxData[
    RowBox[{"Show","[",
      RowBox[{
        RowBox[{"ListLinePlot","[",RowBox[{
          RowBox[{"Transpose","[",RowBox[{"{","td,xc","}"}],"]"}],",",
          RowBox[{"PlotLegends","->",RowBox[{"{","\"Continuous\"","}"}]}],",",
          RowBox[{"AxesLabel","->",RowBox[{"{","\"t [s]\""," , ","\"x\"","}"}]}]
        }],"]"}],",",
        RowBox[{"ListStepPlot","[",RowBox[{
          RowBox[{"Transpose","[",RowBox[{"{","td,xz","}"}],"]"}],",",
          RowBox[{"PlotLegends","->",RowBox[{"{","\"ZOH\"","}"}]}]
        }],"]"}],",",
        RowBox[{"ListPlot","[",RowBox[{
          RowBox[{"Transpose","[",RowBox[{"{","ts,xs","}"}],"]"}],",",
          RowBox[{"PlotStyle","->","Red"}],",",
          RowBox[{"PlotLegends","->",RowBox[{"{","\"Samples\"","}"}]}]
        }],"]"}]
      }],"]"}]
  ], "Input"],
  Cell["Use Fourier[] or Periodogram[] on xs to inspect aliasing peaks near 12 Hz and 25 Hz.", "Text"]
}]

      

13. Problems and Solutions

Problem 1 (Alias Frequency of a Single Tone): A sinusoid has frequency \( f_0=70\,\text{Hz} \) and is sampled at \( f_s=100\,\text{Hz} \). Determine the apparent frequency in the Nyquist interval.

Solution: The Nyquist frequency is \( f_s/2=50\,\text{Hz} \), so 70 Hz is above it. Use frequency folding:

\[ f_a = |f_0 - m f_s|, \quad m=\operatorname{round}\!\left(\frac{f_0}{f_s}\right)=1. \]

\[ f_a = |70-1\cdot 100| = 30\,\text{Hz}. \]

Hence the sampled data are indistinguishable from a 30 Hz sinusoid (up to phase/sign conventions).

Problem 2 (Nyquist Condition with Design Margin): A measurement signal has useful content up to \( B_{sig}=180\,\text{Hz} \). The anti-alias filter needs a 40 Hz transition band before strong attenuation is reached. Choose a minimum practical sampling frequency.

Solution: The theorem alone gives \( f_s > 2B_{sig} = 360\,\text{Hz} \), but the transition band requires additional margin. To place the Nyquist frequency at least 40 Hz above the signal band edge:

\[ \frac{f_s}{2} \geq 180 + 40 = 220 \quad \Longrightarrow \quad f_s \geq 440\,\text{Hz}. \]

A practical engineering choice would be 500 Hz (or 1 kHz if additional robustness is desired).

Problem 3 (Derive the ZOH Transfer Function): Starting from \( h_0(t)=\mathbf{u}(t)-\mathbf{u}(t-T_s) \), derive \( H_0(s) \).

Solution: By direct integration over the hold window,

\[ H_0(s)=\int_0^{T_s} e^{-st}\,dt =\left[-\frac{1}{s}e^{-st}\right]_0^{T_s} =\frac{1-e^{-sT_s}}{s}. \]

This is the standard ZOH transfer function used in sampled-data modeling.

Problem 4 (Hold Droop at a Given Frequency): Using the normalized ZOH frequency response \( \tilde{H}_0(j\omega)=e^{-j\omega T_s/2}\operatorname{sinc}_u(\omega T_s/2) \), compute the magnitude at \( \omega = 0.4\omega_s \).

Solution: Since \( \omega_s=2\pi/T_s \),

\[ \frac{\omega T_s}{2} = \frac{0.4\omega_s T_s}{2} = \frac{0.4(2\pi)}{2} = 0.4\pi. \]

The magnitude is

\[ \left|\tilde{H}_0(j\omega)\right| = \left|\operatorname{sinc}_u(0.4\pi)\right| = \frac{\sin(0.4\pi)}{0.4\pi} \approx \frac{0.9511}{1.2566} \approx 0.757. \]

So the ZOH alone attenuates the component to about 75.7% of its low-frequency amplitude.

Problem 5 (Aliasing from Two Components): A signal contains components at 18 Hz and 92 Hz. If sampled at 100 Hz, what components appear in the Nyquist interval?

Solution: The Nyquist interval is \( [0,50] \) Hz. The 18 Hz component is already in range. For 92 Hz:

\[ f_a = |92 - 1\cdot 100| = 8\,\text{Hz}. \]

Therefore, the sampled signal contains apparent components at 18 Hz and 8 Hz. If both existed originally, their amplitudes and phases combine in the sampled sequence.

14. Summary

We modeled ideal sampling using an impulse train, proved that sampling replicates the signal spectrum, and identified aliasing as overlap of spectral images. We then derived the zero-order hold kernel and transfer function, showing how hold devices shape intersample signals and induce frequency-dependent droop and phase lag. These results form the foundation for discrete-time state and transfer-function models in the next lessons.

15. References

  1. Nyquist, H. (1928). Certain topics in telegraph transmission theory. Transactions of the American Institute of Electrical Engineers, 47(2), 617–644.
  2. Shannon, C.E. (1949). Communication in the presence of noise. Proceedings of the IRE, 37(1), 10–21.
  3. Whittaker, E.T. (1915). On the functions which are represented by the expansions of the interpolation-theory. Proceedings of the Royal Society of Edinburgh, 35, 181–194.
  4. Whittaker, J.M. (1927). On the cardinal function of interpolation theory. Proceedings of the Edinburgh Mathematical Society, 1(1), 41–46.
  5. Linvill, W.K. (1951). Sampled-data control systems studied through comparison of sampling with amplitude modulation. Transactions of the American Institute of Electrical Engineers, 70 (Part II), 1779–1788.
  6. Ragazzini, J.R., & Zadeh, L.A. (1952). The analysis of sampled-data systems. Transactions of the American Institute of Electrical Engineers, 71 (Part II), 225–234.
  7. Kalman, R.E., & Bertram, J.E. (1959). A unified approach to the theory of sampling systems. Journal of the Franklin Institute, 267(5), 405–436.
  8. Bennett, W.R. (1948). Spectra of quantized signals. Bell System Technical Journal, 27(3), 446–472.
  9. Bertram, J.E. (1958). The effect of quantization in sampled-feedback systems. Transactions of the American Institute of Electrical Engineers, 77 (Part II), 177–182.
  10. Widrow, B. (1961). Statistical analysis of amplitude-quantized sampled-data systems. Transactions of the American Institute of Electrical Engineers, Part II: Applications and Industry, 79, 555–568.