2121print ("uri: " + str (my_uri ))
2222
2323# Individual device access (for first plot demonstration)
24- hmcad15xx_dev1 = adi .hmcad15xx (uri = my_uri ,device_name = "axi_adc1_hmcad15xx" )
25- hmcad15xx_dev2 = adi .hmcad15xx (uri = my_uri ,device_name = "axi_adc2_hmcad15xx" )
26- ad5696_dev = adi .ad5686 (uri = my_uri )
24+ hmcad15xx_dev1 = adi .hmcad15xx (uri = my_uri ,device_name = "axi_adc1_hmcad15xx" )
25+ hmcad15xx_dev2 = adi .hmcad15xx (uri = my_uri ,device_name = "axi_adc2_hmcad15xx" )
2726gpio_controller = adi .one_bit_adc_dac (uri = my_uri , name = "one-bit-adc-dac" )
27+ ad5696_dev = adi .ad5686 (uri = my_uri )
28+ tddn = adi .tddn (my_uri )
2829
2930tddn_channel_on_raw = 0
3031tddn_channel_off_raw = 10
3132tddn_channel_polarity = 0
3233tddn_channel_enable = 1
33-
34- tddn = adi .tddn (my_uri )
3534tddn .burst_count = 0
3635tddn .startup_delay_ms = 0
3736tddn .frame_length_ms = 1
3837
38+
3939tddn .enable = 0
4040
4141tddn .channel [0 ].on_ms = 0
7979ramp_pattern = 0x40
8080pattern_disabled = 0x00
8181
82- hmcad15xx_dev1 .rx_buffer_size = 2 ** 18
83- hmcad15xx_dev2 .rx_buffer_size = 2 ** 18
84-
85- hmcad15xx_dev1 .rx_enabled_channels = [0 ]
86- hmcad15xx_dev2 .rx_enabled_channels = [0 ]
87-
88- print ("RX1 rx_enabled_channels: " + str (hmcad15xx_dev1 .rx_enabled_channels ))
89- print ("RX2 rx_enabled_channels: " + str (hmcad15xx_dev2 .rx_enabled_channels ))
90-
91- hmcad15xx_dev1 .hmcad15xx_register_write (0x42 , 0x0000 ) # Phase DDR
92- hmcad15xx_dev2 .hmcad15xx_register_write (0x42 , 0x0000 ) # Phase DDR
93- hmcad15xx_dev1 .hmcad15xx_register_write (0x26 , custom_pattern )
94- hmcad15xx_dev2 .hmcad15xx_register_write (0x26 , custom_pattern )
95- hmcad15xx_dev1 .hmcad15xx_register_write (0x25 , ramp_pattern )
96- hmcad15xx_dev2 .hmcad15xx_register_write (0x25 , ramp_pattern )
97-
98- #input_select_available value: IP1_IN1 IP2_IN2 IP3_IN3
99-
100- hmcad15xx_dev1 .channel [0 ].input_select = "IP1_IN1"
101- hmcad15xx_dev1 .channel [1 ].input_select = "IP1_IN1"
102- hmcad15xx_dev1 .channel [2 ].input_select = "IP1_IN1"
103- hmcad15xx_dev1 .channel [3 ].input_select = "IP1_IN1"
104-
105- hmcad15xx_dev2 .channel [0 ].input_select = "IP1_IN1"
106- hmcad15xx_dev2 .channel [1 ].input_select = "IP1_IN1"
107- hmcad15xx_dev2 .channel [2 ].input_select = "IP1_IN1"
108- hmcad15xx_dev2 .channel [3 ].input_select = "IP1_IN1"
109-
11082# Enable TDD and configure sync for synchronized multi-ADC capture
11183tddn .enable = 1
11284tddn .sync_external = False
12092
12193# Configure buffer size and enabled channels
12294multi .rx_buffer_size = 2 ** 17
123- multi .rx_enabled_channels = [0 ] # Apply to both devices
95+ multi .rx_enabled_channels = [3 ] # Apply to both devices
12496
12597print ("Multi-ADC rx_enabled_channels: " + str (multi .rx_enabled_channels ))
12698
154126plt .title ('ADC Data - Channel 0 from Both Devices (TDD Synchronized)' )
155127plt .legend ()
156128plt .grid (True )
157- plt .savefig ( "Data channels 0 of both devices - Synchronized" )
129+ plt .show ( )
158130plt .close () # Close the figure to free memory
159131
160132print ("Synchronized data capture complete!" )
161133
162- num_captures = 1000
163- phase_shifts_samples = []
164- phase_shifts_degrees = []
165- correlation_peaks = []
166-
167- # Phase Shift Analysis using Cross-Correlation
168- print ("\n --- Phase Shift Analysis ---" )
169- print (f"Performing { num_captures } captures for cross-correlation analysis..." )
170-
171- sampling_rate = multi .dev1 .sampling_rate
172- print (f"Sampling rate: { sampling_rate } Hz" )
173-
174- for i in range (num_captures ):
175- if (i + 1 ) % 10 == 0 :
176- print (f"Progress: { i + 1 } /{ num_captures } captures" )
177-
178- # Capture synchronized data
179- data1 , data2 = multi .rx ()
180-
181- # Normalize signals (remove DC offset and normalize amplitude)
182- data1_norm = (data1 - np .mean (data1 )) / np .std (data1 )
183- data2_norm = (data2 - np .mean (data2 )) / np .std (data2 )
184-
185- # Compute cross-correlation using FFT (much faster for large arrays)
186- # This is equivalent to np.correlate(data1_norm, data2_norm, mode='full')
187- # but uses FFT for O(N log N) complexity instead of O(N^2)
188- fft1 = np .fft .fft (data1_norm , n = 2 * len (data1_norm ))
189- fft2 = np .fft .fft (data2_norm , n = 2 * len (data2_norm ))
190- correlation = np .fft .ifft (fft1 * np .conj (fft2 )).real
191-
192- # Rearrange to match 'full' mode output (shift zero-lag to center)
193- correlation = np .concatenate ([correlation [len (data1 ):], correlation [:len (data1 )]])
194-
195- # Find the lag with maximum correlation
196- max_corr_idx = np .argmax (correlation )
197- lag = max_corr_idx - (len (data1 ) - 1 ) # Offset to center
198-
199- # Store results
200- phase_shifts_samples .append (lag )
201- correlation_peaks .append (correlation [max_corr_idx ])
202-
203- # Calculate phase shift in degrees (if signal is periodic)
204- # For a sinusoidal signal, we can estimate phase shift
205- # Phase shift in samples / samples per period * 360 degrees
206- # We'll store the sample shift for now and convert later if needed
207-
208- # Convert to numpy arrays for analysis
209- phase_shifts_samples = np .array (phase_shifts_samples )
210- correlation_peaks = np .array (correlation_peaks )
211-
212- # Statistical analysis
213- mean_shift = np .mean (phase_shifts_samples )
214- std_shift = np .std (phase_shifts_samples )
215- median_shift = np .median (phase_shifts_samples )
216-
217- print (f"\n Phase Shift Statistics (in samples):" )
218- print (f" Mean: { mean_shift :.3f} samples" )
219- print (f" Std Dev: { std_shift :.3f} samples" )
220- print (f" Median: { median_shift :.3f} samples" )
221- print (f" Min: { np .min (phase_shifts_samples )} samples" )
222- print (f" Max: { np .max (phase_shifts_samples )} samples" )
223-
224- # Time delay in seconds
225- time_delay_mean = mean_shift / sampling_rate
226- time_delay_std = std_shift / sampling_rate
227- print (f"\n Time Delay:" )
228- print (f" Mean: { time_delay_mean * 1e9 :.3f} ns" )
229- print (f" Std Dev: { time_delay_std * 1e9 :.3f} ns" )
230-
231- # Create comprehensive analysis plots
232- fig = plt .figure (figsize = (16 , 12 ))
233-
234- # Plot 1: Histogram of phase shifts
235- ax1 = plt .subplot (3 , 2 , 1 )
236- plt .hist (phase_shifts_samples , bins = 50 , edgecolor = 'black' , alpha = 0.7 )
237- plt .axvline (mean_shift , color = 'red' , linestyle = '--' , linewidth = 2 , label = f'Mean: { mean_shift :.2f} ' )
238- plt .axvline (median_shift , color = 'green' , linestyle = '--' , linewidth = 2 , label = f'Median: { median_shift :.2f} ' )
239- plt .xlabel ('Phase Shift (samples)' )
240- plt .ylabel ('Frequency' )
241- plt .title ('Distribution of Phase Shifts Across 1000 Captures' )
242- plt .legend ()
243- plt .grid (True , alpha = 0.3 )
244-
245- # Plot 2: Time series of phase shift
246- ax2 = plt .subplot (3 , 2 , 2 )
247- plt .plot (phase_shifts_samples , alpha = 0.6 , linewidth = 0.5 )
248- plt .axhline (mean_shift , color = 'red' , linestyle = '--' , linewidth = 2 , label = f'Mean: { mean_shift :.2f} ' )
249- plt .fill_between (range (num_captures ),
250- mean_shift - std_shift ,
251- mean_shift + std_shift ,
252- alpha = 0.2 , color = 'red' , label = f'±1σ: { std_shift :.2f} ' )
253- plt .xlabel ('Capture Number' )
254- plt .ylabel ('Phase Shift (samples)' )
255- plt .title ('Phase Shift Over Time' )
256- plt .legend ()
257- plt .grid (True , alpha = 0.3 )
258-
259- # Plot 3: Correlation peak values
260- ax3 = plt .subplot (3 , 2 , 3 )
261- plt .plot (correlation_peaks , alpha = 0.6 , linewidth = 0.5 )
262- plt .axhline (np .mean (correlation_peaks ), color = 'red' , linestyle = '--' , linewidth = 2 , label = f'Mean: { np .mean (correlation_peaks ):.2f} ' )
263- plt .xlabel ('Capture Number' )
264- plt .ylabel ('Correlation Peak Value' )
265- plt .title ('Cross-Correlation Peak Strength' )
266- plt .legend ()
267- plt .grid (True , alpha = 0.3 )
268-
269- # Plot 4: Example cross-correlation function
270- ax4 = plt .subplot (3 , 2 , 4 )
271- # Perform one final capture to show the correlation function
272- data1_ex , data2_ex = multi .rx ()
273- data1_norm_ex = (data1_ex - np .mean (data1_ex )) / np .std (data1_ex )
274- data2_norm_ex = (data2_ex - np .mean (data2_ex )) / np .std (data2_ex )
275- # Use FFT-based cross-correlation for speed
276- fft1_ex = np .fft .fft (data1_norm_ex , n = 2 * len (data1_norm_ex ))
277- fft2_ex = np .fft .fft (data2_norm_ex , n = 2 * len (data2_norm_ex ))
278- correlation_ex = np .fft .ifft (fft1_ex * np .conj (fft2_ex )).real
279- correlation_ex = np .concatenate ([correlation_ex [len (data1_ex ):], correlation_ex [:len (data1_ex )]])
280- lags_ex = np .arange (- len (data1_ex ) + 1 , len (data1_ex ))
281-
282- # Plot only around the peak for clarity
283- max_idx = np .argmax (correlation_ex )
284- window = 1000 # Show ±1000 samples around peak
285- start_idx = max (0 , max_idx - window )
286- end_idx = min (len (correlation_ex ), max_idx + window )
287-
288- plt .plot (lags_ex [start_idx :end_idx ], correlation_ex [start_idx :end_idx ], linewidth = 1 )
289- plt .axvline (lags_ex [max_idx ], color = 'red' , linestyle = '--' , linewidth = 2 , label = f'Peak at lag={ lags_ex [max_idx ]} ' )
290- plt .xlabel ('Lag (samples)' )
291- plt .ylabel ('Cross-Correlation' )
292- plt .title ('Example Cross-Correlation Function' )
293- plt .legend ()
294- plt .grid (True , alpha = 0.3 )
295-
296- # Plot 5: Example synchronized signals (zoomed in)
297- ax5 = plt .subplot (3 , 2 , 5 )
298- zoom_samples = min (1000 , len (data1_ex )) # Show first 1000 samples
299- plt .plot (data1_ex [:zoom_samples ], label = 'Device 1' , alpha = 0.7 , linewidth = 1 )
300- plt .plot (data2_ex [:zoom_samples ], label = 'Device 2' , alpha = 0.7 , linewidth = 1 )
301- plt .xlabel ('Sample Index' )
302- plt .ylabel ('ADC Value' )
303- plt .title ('Example Synchronized Signals (Zoomed)' )
304- plt .legend ()
305- plt .grid (True , alpha = 0.3 )
306-
307- # Plot 6: Phase shift distribution (box plot)
308- ax6 = plt .subplot (3 , 2 , 6 )
309- plt .boxplot (phase_shifts_samples , vert = True )
310- plt .ylabel ('Phase Shift (samples)' )
311- plt .title ('Phase Shift Distribution (Box Plot)' )
312- plt .grid (True , alpha = 0.3 , axis = 'y' )
313- plt .text (0.5 , mean_shift , f'Mean: { mean_shift :.2f} \n Std: { std_shift :.2f} ' ,
314- transform = ax6 .get_yaxis_transform (), ha = 'left' , va = 'center' ,
315- bbox = dict (boxstyle = 'round' , facecolor = 'wheat' , alpha = 0.5 ))
316-
317- plt .tight_layout ()
318- plt .savefig ("Phase_Shift_Analysis_1000_Captures" )
319- plt .close ()
320-
321- print ("\n Phase shift analysis complete!" )
322- print (f"Analysis plot saved as: Phase_Shift_Analysis_{ num_captures } _Captures.png" )
323-
324- # Summary report
325- print ("\n " + "=" * 60 )
326- print ("PHASE SHIFT ANALYSIS SUMMARY" )
327- print ("=" * 60 )
328- print (f"Number of captures: { num_captures } " )
329- print (f"Sampling rate: { sampling_rate } Hz" )
330- print (f"\n Phase alignment:" )
331- if abs (mean_shift ) < 1.0 :
332- print (f" ✓ Excellent alignment (mean shift: { mean_shift :.3f} samples)" )
333- elif abs (mean_shift ) < 5.0 :
334- print (f" ✓ Good alignment (mean shift: { mean_shift :.3f} samples)" )
335- else :
336- print (f" ⚠ Measurable shift detected (mean shift: { mean_shift :.3f} samples)" )
337-
338- print (f"\n Consistency:" )
339- if std_shift < 0.5 :
340- print (f" ✓ Very consistent (σ = { std_shift :.3f} samples)" )
341- elif std_shift < 2.0 :
342- print (f" ✓ Consistent (σ = { std_shift :.3f} samples)" )
343- else :
344- print (f" ⚠ Variable phase shift (σ = { std_shift :.3f} samples)" )
345-
346- print (f"\n Correlation quality:" )
347- mean_corr = np .mean (correlation_peaks )
348- print (f" Mean correlation peak: { mean_corr :.3f} " )
349- if mean_corr > 0.9 :
350- print (f" ✓ Excellent correlation" )
351- elif mean_corr > 0.7 :
352- print (f" ✓ Good correlation" )
353- else :
354- print (f" ⚠ Moderate correlation" )
355- print ("=" * 60 )
356134
357- # Cleanup buffers
358135multi .rx_destroy_buffer ()
0 commit comments