AM Wave Generation and Plotting with Matplotlib Python: A Detailed Guide
Amplitude Modulation (AM) is a type of analog communication in which the amplitude of a carrier signal is varied in …
Building upon our foundational guide on AM Wave Generation and Plotting with Matplotlib, this bonus article explores advanced AM modulation analysis techniques that are essential for real-world signal processing applications. While the original post covered basic AM generation and plotting, this advanced guide delves into modulation index analysis, sideband examination, frequency domain visualization, and practical applications that professionals encounter in radio communication, broadcasting, and signal analysis.
If you haven’t read our basic AM generation guide yet, we recommend starting there to understand the fundamentals of amplitude modulation, carrier signals, and basic plotting techniques before diving into these advanced concepts.
1import matplotlib.pyplot as plt
2import numpy as np
3from scipy import signal
4from scipy.fft import fft, fftfreq
5import matplotlib.gridspec as gridspec
6
7class AdvancedAMAnalyzer:
8 """Advanced AM signal analysis and visualization framework."""
9
10 def __init__(self, sampling_rate=10000, duration=1.0):
11 self.sampling_rate = sampling_rate
12 self.duration = duration
13 self.time = np.linspace(0, duration, int(sampling_rate * duration))
14
15 def generate_am_signal(self, carrier_freq, message_freq, modulation_index,
16 message_amplitude=1.0, carrier_amplitude=1.0,
17 noise_level=0.0):
18 """Generate AM signal with specified parameters and optional noise."""
19
20 # Generate carrier and message signals
21 carrier = carrier_amplitude * np.sin(2 * np.pi * carrier_freq * self.time)
22 message = message_amplitude * np.sin(2 * np.pi * message_freq * self.time)
23
24 # Apply modulation index
25 am_signal = carrier * (1 + modulation_index * message)
26
27 # Add noise if specified
28 if noise_level > 0:
29 noise = noise_level * np.random.normal(0, 1, len(self.time))
30 am_signal += noise
31
32 return am_signal, carrier, message
33
34 def calculate_modulation_index(self, am_signal, carrier_signal):
35 """Calculate modulation index from signal analysis."""
36 # Envelope detection
37 envelope = np.abs(signal.hilbert(am_signal))
38
39 # Calculate modulation index
40 max_envelope = np.max(envelope)
41 min_envelope = np.min(envelope)
42
43 modulation_index = (max_envelope - min_envelope) / (max_envelope + min_envelope)
44 return modulation_index, envelope
45
46 def analyze_sidebands(self, am_signal, carrier_freq, message_freq):
47 """Analyze sideband components and power distribution."""
48 # Perform FFT
49 fft_signal = fft(am_signal)
50 freqs = fftfreq(len(self.time), 1/self.sampling_rate)
51
52 # Find carrier and sideband frequencies
53 carrier_idx = np.argmin(np.abs(freqs - carrier_freq))
54 upper_sideband_idx = np.argmin(np.abs(freqs - (carrier_freq + message_freq)))
55 lower_sideband_idx = np.argmin(np.abs(freqs - (carrier_freq - message_freq)))
56
57 # Calculate power in each component
58 carrier_power = np.abs(fft_signal[carrier_idx])**2
59 upper_sideband_power = np.abs(fft_signal[upper_sideband_idx])**2
60 lower_sideband_power = np.abs(fft_signal[lower_sideband_idx])**2
61
62 return {
63 'frequencies': freqs,
64 'fft_signal': fft_signal,
65 'carrier_power': carrier_power,
66 'upper_sideband_power': upper_sideband_power,
67 'lower_sideband_power': lower_sideband_power,
68 'total_power': carrier_power + upper_sideband_power + lower_sideband_power
69 }
70
71 def calculate_signal_quality_metrics(self, am_signal, original_message):
72 """Calculate signal quality metrics including SNR and distortion."""
73 # Calculate SNR
74 signal_power = np.mean(am_signal**2)
75 noise_power = np.mean((am_signal - original_message)**2)
76 snr_db = 10 * np.log10(signal_power / noise_power) if noise_power > 0 else float('inf')
77
78 # Calculate distortion
79 correlation = np.corrcoef(am_signal, original_message)[0, 1]
80 distortion = 1 - abs(correlation)
81
82 return {
83 'snr_db': snr_db,
84 'distortion': distortion,
85 'signal_power': signal_power,
86 'noise_power': noise_power
87 }
88
89# Initialize analyzer
90analyzer = AdvancedAMAnalyzer(sampling_rate=10000, duration=1.0)
1def analyze_modulation_depth_examples():
2 """Demonstrate different modulation indices and their effects."""
3
4 # Test different modulation indices
5 modulation_indices = [0.25, 0.5, 0.75, 1.0, 1.25]
6 carrier_freq, message_freq = 1000, 100
7
8 fig, axes = plt.subplots(len(modulation_indices), 2, figsize=(15, 12))
9 fig.suptitle('AM Signal Analysis: Different Modulation Indices', fontsize=16)
10
11 for i, mi in enumerate(modulation_indices):
12 # Generate AM signal
13 am_signal, carrier, message = analyzer.generate_am_signal(
14 carrier_freq, message_freq, mi
15 )
16
17 # Calculate actual modulation index
18 calculated_mi, envelope = analyzer.calculate_modulation_index(am_signal, carrier)
19
20 # Time domain plot
21 axes[i, 0].plot(analyzer.time[:1000], am_signal[:1000], 'b-', label=f'AM Signal (MI={mi:.2f})')
22 axes[i, 0].plot(analyzer.time[:1000], envelope[:1000], 'r--', label=f'Envelope (Calculated MI={calculated_mi:.3f})')
23 axes[i, 0].set_title(f'Modulation Index: {mi}')
24 axes[i, 0].set_xlabel('Time (s)')
25 axes[i, 0].set_ylabel('Amplitude')
26 axes[i, 0].legend()
27 axes[i, 0].grid(True)
28
29 # Frequency domain plot
30 sideband_analysis = analyzer.analyze_sidebands(am_signal, carrier_freq, message_freq)
31 freqs = sideband_analysis['frequencies']
32 fft_signal = sideband_analysis['fft_signal']
33
34 # Plot only positive frequencies
35 positive_freqs = freqs[freqs >= 0]
36 positive_fft = np.abs(fft_signal[freqs >= 0])
37
38 axes[i, 1].plot(positive_freqs, positive_fft, 'g-')
39 axes[i, 1].set_title(f'Frequency Spectrum (MI={mi})')
40 axes[i, 1].set_xlabel('Frequency (Hz)')
41 axes[i, 1].set_ylabel('Magnitude')
42 axes[i, 1].set_xlim(0, 2000)
43 axes[i, 1].grid(True)
44
45 plt.tight_layout()
46 plt.show()
47
48# Run the analysis
49analyze_modulation_depth_examples()
1def sideband_power_analysis():
2 """Analyze power distribution in carrier and sidebands."""
3
4 carrier_freq, message_freq = 1000, 100
5 modulation_indices = np.linspace(0.1, 1.5, 20)
6
7 carrier_powers = []
8 sideband_powers = []
9 total_powers = []
10
11 for mi in modulation_indices:
12 am_signal, _, _ = analyzer.generate_am_signal(carrier_freq, message_freq, mi)
13 analysis = analyzer.analyze_sidebands(am_signal, carrier_freq, message_freq)
14
15 carrier_powers.append(analysis['carrier_power'])
16 sideband_powers.append(analysis['upper_sideband_power'] + analysis['lower_sideband_power'])
17 total_powers.append(analysis['total_power'])
18
19 # Create comprehensive visualization
20 fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
21 fig.suptitle('Advanced AM Signal Power Analysis', fontsize=16)
22
23 # Power vs Modulation Index
24 ax1.plot(modulation_indices, carrier_powers, 'b-', label='Carrier Power', linewidth=2)
25 ax1.plot(modulation_indices, sideband_powers, 'r-', label='Sideband Power', linewidth=2)
26 ax1.plot(modulation_indices, total_powers, 'g-', label='Total Power', linewidth=2)
27 ax1.set_xlabel('Modulation Index')
28 ax1.set_ylabel('Power')
29 ax1.set_title('Power Distribution vs Modulation Index')
30 ax1.legend()
31 ax1.grid(True)
32
33 # Power Efficiency
34 efficiency = np.array(sideband_powers) / np.array(total_powers) * 100
35 ax2.plot(modulation_indices, efficiency, 'purple', linewidth=2)
36 ax2.set_xlabel('Modulation Index')
37 ax2.set_ylabel('Sideband Efficiency (%)')
38 ax2.set_title('Power Efficiency in Sidebands')
39 ax2.grid(True)
40
41 # Detailed spectrum for specific modulation index
42 mi_optimal = 1.0
43 am_signal, _, _ = analyzer.generate_am_signal(carrier_freq, message_freq, mi_optimal)
44 analysis = analyzer.analyze_sidebands(am_signal, carrier_freq, message_freq)
45
46 freqs = analysis['frequencies']
47 fft_signal = analysis['fft_signal']
48
49 # Plot detailed spectrum
50 positive_freqs = freqs[freqs >= 0]
51 positive_fft = np.abs(fft_signal[freqs >= 0])
52
53 ax3.plot(positive_freqs, positive_fft, 'b-', linewidth=1.5)
54 ax3.set_xlabel('Frequency (Hz)')
55 ax3.set_ylabel('Magnitude')
56 ax3.set_title(f'Detailed Spectrum (MI={mi_optimal})')
57 ax3.set_xlim(800, 1200)
58 ax3.grid(True)
59
60 # Annotate key frequencies
61 ax3.axvline(x=carrier_freq, color='red', linestyle='--', alpha=0.7, label='Carrier')
62 ax3.axvline(x=carrier_freq + message_freq, color='green', linestyle='--', alpha=0.7, label='Upper Sideband')
63 ax3.axvline(x=carrier_freq - message_freq, color='orange', linestyle='--', alpha=0.7, label='Lower Sideband')
64 ax3.legend()
65
66 # 3D visualization of power distribution
67 from mpl_toolkits.mplot3d import Axes3D
68
69 # Create meshgrid for 3D plot
70 mi_mesh, freq_mesh = np.meshgrid(modulation_indices, positive_freqs[::10])
71
72 # Calculate power distribution for each combination
73 power_mesh = np.zeros_like(mi_mesh)
74 for i, mi in enumerate(modulation_indices):
75 am_signal, _, _ = analyzer.generate_am_signal(carrier_freq, message_freq, mi)
76 analysis = analyzer.analyze_sidebands(am_signal, carrier_freq, message_freq)
77 power_mesh[:, i] = np.abs(analysis['fft_signal'][freqs >= 0][::10])
78
79 ax4 = fig.add_subplot(2, 2, 4, projection='3d')
80 surf = ax4.plot_surface(mi_mesh, freq_mesh, power_mesh, cmap='viridis', alpha=0.8)
81 ax4.set_xlabel('Modulation Index')
82 ax4.set_ylabel('Frequency (Hz)')
83 ax4.set_zlabel('Power')
84 ax4.set_title('3D Power Distribution')
85
86 plt.tight_layout()
87 plt.show()
88
89# Run sideband analysis
90sideband_power_analysis()
1def am_broadcasting_analysis():
2 """Simulate and analyze AM broadcasting signals with real-world parameters."""
3
4 # AM broadcasting parameters (typical values)
5 broadcast_params = {
6 'carrier_freq': 1000000, # 1 MHz carrier
7 'audio_freq': 1000, # 1 kHz audio tone
8 'modulation_index': 0.8, # 80% modulation
9 'sampling_rate': 10000000 # 10 MHz sampling for high fidelity
10 }
11
12 # Create high-fidelity analyzer
13 broadcast_analyzer = AdvancedAMAnalyzer(
14 sampling_rate=broadcast_params['sampling_rate'],
15 duration=0.01 # 10ms for detailed analysis
16 )
17
18 # Generate broadcast signal
19 am_signal, carrier, audio = broadcast_analyzer.generate_am_signal(
20 broadcast_params['carrier_freq'],
21 broadcast_params['audio_freq'],
22 broadcast_params['modulation_index']
23 )
24
25 # Analyze signal quality
26 quality_metrics = broadcast_analyzer.calculate_signal_quality_metrics(am_signal, audio)
27
28 # Create comprehensive broadcast analysis
29 fig = plt.figure(figsize=(16, 12))
30 gs = gridspec.GridSpec(3, 3, figure=fig)
31
32 # Time domain - full signal
33 ax1 = fig.add_subplot(gs[0, :])
34 ax1.plot(broadcast_analyzer.time, am_signal, 'b-', linewidth=0.5)
35 ax1.set_title('AM Broadcast Signal - Time Domain')
36 ax1.set_xlabel('Time (s)')
37 ax1.set_ylabel('Amplitude')
38 ax1.grid(True)
39
40 # Time domain - zoomed
41 ax2 = fig.add_subplot(gs[1, 0])
42 zoom_samples = int(0.001 * broadcast_params['sampling_rate']) # 1ms
43 ax2.plot(broadcast_analyzer.time[:zoom_samples], am_signal[:zoom_samples], 'r-')
44 ax2.set_title('Zoomed View (1ms)')
45 ax2.set_xlabel('Time (s)')
46 ax2.set_ylabel('Amplitude')
47 ax2.grid(True)
48
49 # Frequency domain
50 ax3 = fig.add_subplot(gs[1, 1])
51 analysis = broadcast_analyzer.analyze_sidebands(am_signal,
52 broadcast_params['carrier_freq'],
53 broadcast_params['audio_freq'])
54 freqs = analysis['frequencies']
55 fft_signal = analysis['fft_signal']
56
57 # Plot around carrier frequency
58 carrier_range = (freqs >= broadcast_params['carrier_freq'] - 5000) & \
59 (freqs <= broadcast_params['carrier_freq'] + 5000)
60 ax3.plot(freqs[carrier_range], np.abs(fft_signal[carrier_range]), 'g-')
61 ax3.set_title('Frequency Domain (Carrier ±5kHz)')
62 ax3.set_xlabel('Frequency (Hz)')
63 ax3.set_ylabel('Magnitude')
64 ax3.grid(True)
65
66 # Signal quality metrics
67 ax4 = fig.add_subplot(gs[1, 2])
68 metrics = ['SNR (dB)', 'Distortion', 'Signal Power', 'Noise Power']
69 values = [quality_metrics['snr_db'], quality_metrics['distortion'],
70 quality_metrics['signal_power'], quality_metrics['noise_power']]
71
72 bars = ax4.bar(metrics, values, color=['blue', 'red', 'green', 'orange'])
73 ax4.set_title('Signal Quality Metrics')
74 ax4.set_ylabel('Value')
75 ax4.tick_params(axis='x', rotation=45)
76
77 # Add value labels on bars
78 for bar, value in zip(bars, values):
79 height = bar.get_height()
80 ax4.text(bar.get_x() + bar.get_width()/2., height,
81 f'{value:.2f}', ha='center', va='bottom')
82
83 # Power distribution pie chart
84 ax5 = fig.add_subplot(gs[2, 0])
85 powers = [analysis['carrier_power'],
86 analysis['upper_sideband_power'],
87 analysis['lower_sideband_power']]
88 labels = ['Carrier', 'Upper Sideband', 'Lower Sideband']
89 colors = ['lightblue', 'lightgreen', 'lightcoral']
90
91 ax5.pie(powers, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)
92 ax5.set_title('Power Distribution')
93
94 # Modulation depth analysis
95 ax6 = fig.add_subplot(gs[2, 1])
96 calculated_mi, envelope = broadcast_analyzer.calculate_modulation_index(am_signal, carrier)
97
98 ax6.plot(broadcast_analyzer.time[:zoom_samples], am_signal[:zoom_samples], 'b-', alpha=0.7)
99 ax6.plot(broadcast_analyzer.time[:zoom_samples], envelope[:zoom_samples], 'r--', linewidth=2)
100 ax6.fill_between(broadcast_analyzer.time[:zoom_samples],
101 envelope[:zoom_samples], -envelope[:zoom_samples],
102 alpha=0.2, color='red')
103 ax6.set_title(f'Modulation Depth Analysis\nCalculated MI: {calculated_mi:.3f}')
104 ax6.set_xlabel('Time (s)')
105 ax6.set_ylabel('Amplitude')
106 ax6.grid(True)
107
108 # Bandwidth analysis
109 ax7 = fig.add_subplot(gs[2, 2])
110 bandwidth = 2 * broadcast_params['audio_freq'] # AM bandwidth = 2 * message frequency
111 ax7.axvspan(broadcast_params['carrier_freq'] - bandwidth/2,
112 broadcast_params['carrier_freq'] + bandwidth/2,
113 alpha=0.3, color='yellow', label=f'Bandwidth: {bandwidth} Hz')
114 ax7.plot(freqs[carrier_range], np.abs(fft_signal[carrier_range]), 'b-')
115 ax7.set_title('Bandwidth Analysis')
116 ax7.set_xlabel('Frequency (Hz)')
117 ax7.set_ylabel('Magnitude')
118 ax7.legend()
119 ax7.grid(True)
120
121 plt.tight_layout()
122 plt.show()
123
124 # Print analysis summary
125 print("=== AM Broadcasting Signal Analysis ===")
126 print(f"Carrier Frequency: {broadcast_params['carrier_freq']/1000:.1f} kHz")
127 print(f"Audio Frequency: {broadcast_params['audio_freq']} Hz")
128 print(f"Modulation Index: {broadcast_params['modulation_index']}")
129 print(f"Calculated Modulation Index: {calculated_mi:.3f}")
130 print(f"Signal-to-Noise Ratio: {quality_metrics['snr_db']:.2f} dB")
131 print(f"Signal Distortion: {quality_metrics['distortion']:.3f}")
132 print(f"Bandwidth: {bandwidth} Hz")
133 print(f"Carrier Power: {analysis['carrier_power']:.2e}")
134 print(f"Sideband Power: {analysis['upper_sideband_power'] + analysis['lower_sideband_power']:.2e}")
135
136# Run broadcasting analysis
137am_broadcasting_analysis()
1def noise_analysis_and_filtering():
2 """Analyze AM signal behavior under different noise conditions and apply filtering."""
3
4 # Generate clean AM signal
5 clean_signal, carrier, message = analyzer.generate_am_signal(
6 carrier_freq=1000, message_freq=100, modulation_index=0.8
7 )
8
9 # Add different types of noise
10 noise_levels = [0.0, 0.1, 0.3, 0.5]
11 noise_types = ['Gaussian', 'Impulse', 'Sinusoidal']
12
13 fig, axes = plt.subplots(len(noise_levels), len(noise_types) + 1, figsize=(20, 12))
14 fig.suptitle('AM Signal Analysis Under Different Noise Conditions', fontsize=16)
15
16 for i, noise_level in enumerate(noise_levels):
17 # Clean signal (first column)
18 axes[i, 0].plot(analyzer.time[:500], clean_signal[:500], 'b-')
19 axes[i, 0].set_title(f'Clean Signal (Noise: {noise_level})')
20 axes[i, 0].set_ylabel('Amplitude')
21 axes[i, 0].grid(True)
22
23 # Different noise types
24 for j, noise_type in enumerate(noise_types):
25 if noise_level == 0:
26 noisy_signal = clean_signal
27 else:
28 if noise_type == 'Gaussian':
29 noise = noise_level * np.random.normal(0, 1, len(clean_signal))
30 elif noise_type == 'Impulse':
31 noise = noise_level * np.random.laplace(0, 1, len(clean_signal))
32 else: # Sinusoidal
33 noise = noise_level * np.sin(2 * np.pi * 500 * analyzer.time)
34
35 noisy_signal = clean_signal + noise
36
37 # Apply simple low-pass filter
38 if noise_level > 0:
39 # Design Butterworth low-pass filter
40 cutoff_freq = 200 # Hz
41 nyquist = analyzer.sampling_rate / 2
42 normal_cutoff = cutoff_freq / nyquist
43 b, a = signal.butter(4, normal_cutoff, btype='low')
44 filtered_signal = signal.filtfilt(b, a, noisy_signal)
45 else:
46 filtered_signal = noisy_signal
47
48 # Plot noisy and filtered signals
49 axes[i, j+1].plot(analyzer.time[:500], noisy_signal[:500], 'r-', alpha=0.7, label='Noisy')
50 axes[i, j+1].plot(analyzer.time[:500], filtered_signal[:500], 'g-', label='Filtered')
51 axes[i, j+1].set_title(f'{noise_type} Noise (Level: {noise_level})')
52 axes[i, j+1].legend()
53 axes[i, j+1].grid(True)
54
55 plt.tight_layout()
56 plt.show()
57
58 # Calculate and display noise impact metrics
59 print("\n=== Noise Impact Analysis ===")
60 for noise_level in noise_levels[1:]: # Skip clean signal
61 for noise_type in noise_types:
62 if noise_type == 'Gaussian':
63 noise = noise_level * np.random.normal(0, 1, len(clean_signal))
64 elif noise_type == 'Impulse':
65 noise = noise_level * np.random.laplace(0, 1, len(clean_signal))
66 else:
67 noise = noise_level * np.sin(2 * np.pi * 500 * analyzer.time)
68
69 noisy_signal = clean_signal + noise
70
71 # Calculate metrics
72 snr = 10 * np.log10(np.var(clean_signal) / np.var(noise))
73 correlation = np.corrcoef(clean_signal, noisy_signal)[0, 1]
74
75 print(f"{noise_type} Noise (Level {noise_level}): SNR = {snr:.2f} dB, Correlation = {correlation:.3f}")
76
77# Run noise analysis
78noise_analysis_and_filtering()
1class AMSignalQualityMonitor:
2 """Real-time AM signal quality monitoring system."""
3
4 def __init__(self, target_carrier_freq, target_message_freq):
5 self.target_carrier_freq = target_carrier_freq
6 self.target_message_freq = target_message_freq
7 self.analyzer = AdvancedAMAnalyzer()
8
9 def monitor_signal_quality(self, am_signal, carrier_signal, message_signal):
10 """Monitor various quality metrics of AM signal."""
11
12 # Calculate modulation index
13 mi, envelope = self.analyzer.calculate_modulation_index(am_signal, carrier_signal)
14
15 # Analyze sidebands
16 sideband_analysis = self.analyzer.analyze_sidebands(
17 am_signal, self.target_carrier_freq, self.target_message_freq
18 )
19
20 # Calculate quality metrics
21 quality_metrics = self.analyzer.calculate_signal_quality_metrics(am_signal, message_signal)
22
23 # Create quality report
24 report = {
25 'modulation_index': mi,
26 'modulation_depth_percent': mi * 100,
27 'carrier_power': sideband_analysis['carrier_power'],
28 'sideband_power': sideband_analysis['upper_sideband_power'] + sideband_analysis['lower_sideband_power'],
29 'total_power': sideband_analysis['total_power'],
30 'power_efficiency': (sideband_analysis['upper_sideband_power'] + sideband_analysis['lower_sideband_power']) / sideband_analysis['total_power'] * 100,
31 'snr_db': quality_metrics['snr_db'],
32 'distortion': quality_metrics['distortion'],
33 'signal_quality_score': self._calculate_quality_score(mi, quality_metrics['snr_db'], quality_metrics['distortion'])
34 }
35
36 return report, envelope
37
38 def _calculate_quality_score(self, mi, snr_db, distortion):
39 """Calculate overall signal quality score (0-100)."""
40 # Optimal modulation index is 1.0
41 mi_score = max(0, 100 - abs(mi - 1.0) * 50)
42
43 # SNR score (higher is better)
44 snr_score = min(100, max(0, snr_db + 20)) # Assume -20dB is minimum acceptable
45
46 # Distortion score (lower is better)
47 distortion_score = max(0, 100 - distortion * 100)
48
49 # Weighted average
50 overall_score = (mi_score * 0.3 + snr_score * 0.4 + distortion_score * 0.3)
51 return overall_score
52
53 def generate_quality_report(self, am_signal, carrier_signal, message_signal):
54 """Generate comprehensive quality report with visualization."""
55
56 report, envelope = self.monitor_signal_quality(am_signal, carrier_signal, message_signal)
57
58 # Create comprehensive report visualization
59 fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
60 fig.suptitle('AM Signal Quality Monitor Report', fontsize=16)
61
62 # Time domain with envelope
63 ax1.plot(self.analyzer.time[:1000], am_signal[:1000], 'b-', alpha=0.7, label='AM Signal')
64 ax1.plot(self.analyzer.time[:1000], envelope[:1000], 'r--', linewidth=2, label='Envelope')
65 ax1.fill_between(self.analyzer.time[:1000], envelope[:1000], -envelope[:1000], alpha=0.1, color='red')
66 ax1.set_title('Time Domain Analysis')
67 ax1.set_xlabel('Time (s)')
68 ax1.set_ylabel('Amplitude')
69 ax1.legend()
70 ax1.grid(True)
71
72 # Quality metrics radar chart
73 ax2 = fig.add_subplot(2, 2, 2, projection='polar')
74 categories = ['Modulation\nIndex', 'SNR', 'Power\nEfficiency', 'Signal\nQuality']
75 values = [report['modulation_depth_percent'],
76 min(100, report['snr_db'] + 20),
77 report['power_efficiency'],
78 report['signal_quality_score']]
79
80 angles = np.linspace(0, 2 * np.pi, len(categories), endpoint=False).tolist()
81 values += values[:1] # Complete the circle
82 angles += angles[:1]
83
84 ax2.plot(angles, values, 'o-', linewidth=2, color='blue')
85 ax2.fill(angles, values, alpha=0.25, color='blue')
86 ax2.set_xticks(angles[:-1])
87 ax2.set_xticklabels(categories)
88 ax2.set_ylim(0, 100)
89 ax2.set_title('Quality Metrics Radar Chart')
90
91 # Power distribution
92 ax3.bar(['Carrier', 'Sidebands', 'Total'],
93 [report['carrier_power'], report['sideband_power'], report['total_power']],
94 color=['lightblue', 'lightgreen', 'lightcoral'])
95 ax3.set_title('Power Distribution')
96 ax3.set_ylabel('Power')
97 ax3.grid(True, axis='y')
98
99 # Quality score gauge
100 ax4 = fig.add_subplot(2, 2, 4, projection='polar')
101 score_angle = (report['signal_quality_score'] / 100) * np.pi
102 ax4.bar(0, 1, width=score_angle, color='green', alpha=0.7)
103 ax4.bar(0, 1, width=2*np.pi-score_angle, color='red', alpha=0.3)
104 ax4.set_title(f'Overall Quality Score: {report["signal_quality_score"]:.1f}/100')
105 ax4.set_xticks([])
106 ax4.set_yticks([])
107
108 plt.tight_layout()
109 plt.show()
110
111 # Print detailed report
112 print("\n" + "="*50)
113 print("AM SIGNAL QUALITY MONITOR REPORT")
114 print("="*50)
115 print(f"Modulation Index: {report['modulation_index']:.3f} ({report['modulation_depth_percent']:.1f}%)")
116 print(f"Signal-to-Noise Ratio: {report['snr_db']:.2f} dB")
117 print(f"Signal Distortion: {report['distortion']:.3f}")
118 print(f"Power Efficiency: {report['power_efficiency']:.1f}%")
119 print(f"Overall Quality Score: {report['signal_quality_score']:.1f}/100")
120 print(f"Carrier Power: {report['carrier_power']:.2e}")
121 print(f"Sideband Power: {report['sideband_power']:.2e}")
122 print(f"Total Power: {report['total_power']:.2e}")
123
124 # Quality assessment
125 if report['signal_quality_score'] >= 80:
126 assessment = "EXCELLENT"
127 elif report['signal_quality_score'] >= 60:
128 assessment = "GOOD"
129 elif report['signal_quality_score'] >= 40:
130 assessment = "FAIR"
131 else:
132 assessment = "POOR"
133
134 print(f"\nQuality Assessment: {assessment}")
135 print("="*50)
136
137# Example usage of the quality monitor
138def demonstrate_quality_monitor():
139 """Demonstrate the AM signal quality monitoring system."""
140
141 # Generate test signals
142 test_analyzer = AdvancedAMAnalyzer()
143 am_signal, carrier, message = test_analyzer.generate_am_signal(
144 carrier_freq=1000, message_freq=100, modulation_index=0.85
145 )
146
147 # Create and run quality monitor
148 monitor = AMSignalQualityMonitor(target_carrier_freq=1000, target_message_freq=100)
149 monitor.generate_quality_report(am_signal, carrier, message)
150
151# Run quality monitor demonstration
152demonstrate_quality_monitor()
This advanced AM modulation analysis guide builds upon the foundational concepts from our original post, providing you with sophisticated tools and techniques for real-world signal processing applications. The comprehensive analysis framework, real-world case studies, and practical implementation examples demonstrate how to move beyond basic AM generation to professional-grade signal analysis.
Key takeaways from this advanced guide include:
These advanced techniques are essential for professionals working in radio communication, broadcasting, signal processing, and related fields where precise AM signal analysis is critical for system performance and compliance.
For further exploration, consider applying these techniques to other modulation schemes (FM, PM) or extending the analysis to include digital modulation techniques. The mathematical foundations and visualization approaches developed here provide a solid foundation for more complex signal processing applications.