From ebc6597f8c637cd4095b0c2f9702cdff29f74f20 Mon Sep 17 00:00:00 2001
From: sldesnoo-Delft <s.l.desnoo@tudelft.nl>
Date: Mon, 6 Feb 2023 09:26:22 +0100
Subject: [PATCH] Added uploader.actual_acquisition_points() to get correct
 number of acquired samples and their interval

---
 pulse_lib/keysight/M3202A_uploader.py | 13 +++++++++++--
 pulse_lib/keysight/qs_uploader.py     |  2 +-
 pulse_lib/qblox/pulsar_uploader.py    | 25 +++++++++++--------------
 pulse_lib/sequencer.py                | 13 +++++++++----
 4 files changed, 32 insertions(+), 21 deletions(-)

diff --git a/pulse_lib/keysight/M3202A_uploader.py b/pulse_lib/keysight/M3202A_uploader.py
index af26465f..eed7020f 100644
--- a/pulse_lib/keysight/M3202A_uploader.py
+++ b/pulse_lib/keysight/M3202A_uploader.py
@@ -78,10 +78,19 @@ class M3202A_Uploader:
         awg = list(self.AWGs.values())[0]
         return awg.convert_prescaler_to_sample_rate(awg.convert_sample_rate_to_prescaler(sample_rate))
 
-    def get_num_samples(self, acquisition_channel, t_measure, sample_rate):
+    def actual_acquisition_points(self, acquisition_channel, t_measure, sample_rate):
+        '''
+        Returns the actual number of points and interval of an acquisition.
+        '''
         dig_ch = self.digitizer_channels[acquisition_channel]
         digitizer = self.digitizers[dig_ch.module_name]
-        return digitizer.get_samples_per_measurement(t_measure, sample_rate)
+        if hasattr(digitizer, 'actual_acquisition_points'):
+            n_samples, interval = digitizer.actual_acquisition_points(dig_ch, t_measure, sample_rate)
+        else:
+            # use old function and assume the digitizer is NOT in MODES.NORMAL
+            n_samples = digitizer.get_samples_per_measurement(t_measure, sample_rate)
+            interval = int(max(1, round(100e6 / sample_rate))) * 10
+        return n_samples, interval
 
     def create_job(self, sequence, index, seq_id, n_rep, sample_rate, neutralize=True, alignment=None):
         # TODO @@@ implement alignment
diff --git a/pulse_lib/keysight/qs_uploader.py b/pulse_lib/keysight/qs_uploader.py
index e94c3116..b0369d5c 100644
--- a/pulse_lib/keysight/qs_uploader.py
+++ b/pulse_lib/keysight/qs_uploader.py
@@ -75,7 +75,7 @@ class QsUploader:
         awg = list(self.AWGs.values())[0]
         return awg.convert_prescaler_to_sample_rate(awg.convert_sample_rate_to_prescaler(sample_rate))
 
-    def get_num_samples(self, acquisition_channel, t_measure, sample_rate):
+    def actual_acquisition_points(self, acquisition_channel, t_measure, sample_rate):
         raise NotImplementedError()
 
     def get_roundtrip_latency(self):
diff --git a/pulse_lib/qblox/pulsar_uploader.py b/pulse_lib/qblox/pulsar_uploader.py
index 503bfa6f..63113d50 100644
--- a/pulse_lib/qblox/pulsar_uploader.py
+++ b/pulse_lib/qblox/pulsar_uploader.py
@@ -151,13 +151,8 @@ class PulsarUploader:
         """
         return 1e9
 
-    def get_num_samples(self, acquisition_channel, t_measure, sample_rate):
-        # todo: remove code duplication with add_acquisition_channel
-        trigger_period = PulsarConfig.align(1e9/sample_rate)
-        n_samples = max(1, iround(t_measure / trigger_period))
-        # @@@ n_repeat not taken into account
-        # @@@ always minimum 1 sample: UploadAggregator raises an exception for 0 cycles.
-        return n_samples
+    def actual_acquisition_points(self, acquisition_channel, t_measure, sample_rate):
+        return _actual_acquisition_points(t_measure, sample_rate)
 
     def create_job(self, sequence, index, seq_id, n_rep, sample_rate,
                    neutralize=True, alignment=None):
@@ -449,6 +444,13 @@ class SegmentRenderInfo:
         return self.t_start + self.npt
 
 
+def _actual_acquisition_points(t_measure, sample_rate):
+    trigger_period = PulsarConfig.align(1e9/sample_rate)
+    t_measure = PulsarConfig.align(t_measure)
+    n_samples = t_measure // trigger_period
+    return n_samples, trigger_period
+
+
 class UploadAggregator:
     verbose = False
 
@@ -750,11 +752,6 @@ class UploadAggregator:
 
         acq_conf = job.acquisition_conf
 
-        if acq_conf.sample_rate is not None:
-            trigger_period = PulsarConfig.align(1e9/acq_conf.sample_rate)
-        else:
-            trigger_period = None
-
         if acq_conf.average_repetitions or not job.n_rep:
             n_rep = 1
         else:
@@ -790,8 +787,8 @@ class UploadAggregator:
                                          PulsarConfig.align(acquisition.interval))
                     if acq_conf.sample_rate is not None:
                         logging.info(f'Acquisition sample_rate is ignored when n_repeat is set')
-                elif trigger_period:
-                    n_cycles = iround(t_measure / trigger_period)
+                elif acq_conf.sample_rate is not None:
+                    n_cycles, trigger_period = _actual_acquisition_points(t_measure, acq_conf.sample_rate)
                     if n_cycles < 1:
                         raise Exception(f'{channel_name} acquisition t_measure ({t_measure}) < 1/sample_rate ({trigger_period})')
                     seq.repeated_acquire(t, trigger_period, n_cycles, trigger_period)
diff --git a/pulse_lib/sequencer.py b/pulse_lib/sequencer.py
index 5030d94b..a49ae532 100644
--- a/pulse_lib/sequencer.py
+++ b/pulse_lib/sequencer.py
@@ -177,7 +177,6 @@ class sequencer():
                 effective_rate = self.uploader.get_effective_sample_rate(seg_container.sample_rate)
                 msg = f"effective sampling rate for {seg_container.name} is set to {si_format(effective_rate, precision=1)}Sa/s"
                 logging.info(msg)
-                print("Info : " + msg)
 
         # update dimensionality of all sequence objects
         logging.debug('Enter pre-rendering')
@@ -364,9 +363,15 @@ class sequencer():
                     t_measure = m.t_measure
                 else:
                     raise Exception(f't_measure must be number and not a {type(m.t_measure)} for time traces')
-                m.n_samples = self.uploader.get_num_samples(
-                        m.acquisition_channel, t_measure, sample_rate) # @@@ implement QS, Tektronix
-                m.interval = round(1e9/sample_rate)
+                # @@@ implement QS, Tektronix
+                if hasattr(self.uploader, 'actual_acquisition_points'):
+                    m.n_samples, m.interval = self.uploader.actual_acquisition_points(m.acquisition_channel,
+                                                                                      t_measure, sample_rate)
+                else:
+                    print(f'WARNING {type(self.uploader)} is missing method actual_acquisition_points(); using old computation')
+                    m.n_samples = self.uploader.get_num_samples(
+                            m.acquisition_channel, t_measure, sample_rate)
+                    m.interval = round(1e9/sample_rate)
             else:
                 m.n_samples = 1
 
-- 
GitLab