@@ -275,6 +275,215 @@ NACS_EXPORT() uint32_t Manager::ExpSeq::refresh_device_restart(const char *dname
275275 return uint32_t (-1 );
276276}
277277
278+ NACS_EXPORT () Manager::ExpSeq::ChnOutput* Manager::ExpSeq::get_nominal_output (uint64_t pts_per_ramp, size_t *out_sz)
279+ {
280+ // pts_per_ramp includes the first and last point, so it must be at least 2. It will be set to 2 if not the case.
281+ // Temporary insofar as keeping the start value, and pulses for this channel.
282+ struct FilledPulse {
283+ FilledPulse (uint32_t id, int64_t time, int64_t len, double (*ramp_func)(double , void *),
284+ double endvalue, bool cond)
285+ : id(id), time(time), len(len), ramp_func(ramp_func),
286+ endvalue (endvalue), cond(cond)
287+ {
288+ // empty
289+ }
290+ uint32_t id;
291+
292+ int64_t time;
293+ int64_t len; // 0 for a set pulse and nonzero for a ramp.
294+ double (*ramp_func)(double , void *);
295+
296+ double endvalue;
297+ bool cond;
298+ };
299+ struct TempChnOutput {
300+ TempChnOutput (std::string name, double start_val)
301+ : m_name(name),
302+ m_start_val (start_val)
303+ {
304+ //
305+ }
306+ std::string m_name;
307+ double m_start_val;
308+ std::vector<FilledPulse> m_pulses;
309+ std::vector<int64_t > times;
310+ std::vector<double > values;
311+ std::vector<uint32_t > pulse_ids;
312+ };
313+ uint32_t nchannels = host_seq.nchannels;
314+ auto seq_idx = host_seq.cur_seq_idx();
315+ if (nchannels == 0 || seq_idx == uint32_t (-1 )) {
316+ *out_sz = 0 ;
317+ return nullptr ;
318+ }
319+
320+ std::map<uint32_t ,TempChnOutput> temp_chn_outputs;
321+ std::vector<Manager::ExpSeq::ChnOutput> outputs;
322+
323+ // Initialize the temp output for each channel.
324+ for (auto &[name, chn_id]: m_chn_map) {
325+ std::string chn_name = name;
326+ double start_val = host_seq.start_values [chn_id - 1 ].f64 ;
327+ printf (" Channel %s (ID: %d) start value: %f\n " , chn_name.c_str (), chn_id, start_val);
328+ temp_chn_outputs.try_emplace (chn_id - 1 , chn_name, start_val);
329+ }
330+ // Collect the pulses along with their filled values for each channel.
331+ auto &bseq = host_seq.seqs[seq_idx];
332+ // Keep track of the max time
333+ int64_t max_time = 0 ;
334+ for (auto &pulse: bseq.pulses) {
335+ if (pulse.is_measure ())
336+ continue ;
337+ int64_t time = host_seq.get_time (pulse.time );
338+ // Using this call double get_value(uint32_t i, uint32_t seq_idx) const
339+ int64_t len = pulse.len == uint32_t (-1 ) ? 0 : round<int64_t >(host_seq.get_value (pulse.len , seq_idx));
340+ double endvalue = host_seq.get_value (pulse.endvalue , seq_idx);
341+ bool cond = pulse.cond == uint32_t (-1 ) ? true : (bool ) host_seq.get_value (pulse.cond , seq_idx);
342+ printf (" Pulse ID: %d, Channel: %d, Time: %ld, Length: %ld, End Value: %f, Condition: %d\n " ,
343+ pulse.id , pulse.chn , time, len, endvalue, cond);
344+ auto it = temp_chn_outputs.find (pulse.chn - 1 );
345+ if (it == temp_chn_outputs.end ()) {
346+ throw std::runtime_error (" Missing channel ID" );
347+ } else {
348+ auto &temp_output = it->second ;
349+ temp_output.m_pulses .emplace_back (pulse.id , time, len, pulse.ramp_func , endvalue, cond);
350+ }
351+ }
352+ // With all the pulses, we now build our output.
353+ for (auto &[chn_id, temp_output]: temp_chn_outputs) {
354+ // First we sort them.
355+ auto &pulses = temp_output.m_pulses ;
356+ std::sort (pulses.begin (), pulses.end (), [] (auto &p1, auto &p2) {
357+ if (p1.time < p2.time )
358+ return true ;
359+ if (p1.time > p2.time )
360+ return false ;
361+ return p1.id < p2.id ;
362+ });
363+ // Start building the output with the start value
364+ auto × = temp_output.times ;
365+ auto &values = temp_output.values ;
366+ auto &pulse_ids = temp_output.pulse_ids ;
367+ times.push_back (0 );
368+ values.push_back (temp_output.m_start_val );
369+ pulse_ids.push_back (uint32_t (-1 )); // the start value is not identified with any particular pulse.
370+ auto npulses = pulses.size ();
371+ for (uint32_t i = 0 ; i < npulses;) {
372+ auto &pulse = pulses[i];
373+ // This check should rarely execute, except if the first pulse is disabled.
374+ if (!pulse.cond ) {
375+ // Skip disabled pulse
376+ i++;
377+ continue ;
378+ }
379+ // Find next pulse which is important for ensuring that this set pulse will take place and
380+ // determines the end time for a potentially interrupted ramp.
381+ bool found = false ;
382+ FilledPulse *next_pulse_ptr = nullptr ;
383+ i++; // Move to the next pulse
384+ while (!found) {
385+ if (i >= npulses) {
386+ found = true ;
387+ }
388+ else {
389+ if (pulses[i].cond ) {
390+ next_pulse_ptr = &pulses[i];
391+ found = true ;
392+ } else {
393+ // Skip disabled pulse
394+ i++;
395+ }
396+ }
397+ }
398+ // Below here, i does not need to be advanced!
399+ if (pulse.len == 0 ) {
400+ // Set pulse
401+ // Only valid if next pulse is not at the same time.
402+ if (next_pulse_ptr && (next_pulse_ptr->time <= pulse.time )) {
403+ // Skip current pulse
404+ continue ;
405+ }
406+ // If previous point is not at the current time, then add a point with the previous value.
407+ if (times.back () != pulse.time ) {
408+ times.push_back (pulse.time );
409+ values.push_back (values.back ()); // Use the last value.
410+ pulse_ids.push_back (pulse_ids.back ()); // No pulse ID for this point.
411+ }
412+ times.push_back (pulse.time );
413+ values.push_back (pulse.endvalue );
414+ pulse_ids.push_back (pulse.id );
415+ } else {
416+ // Ramp pulse
417+ // Get end time by looking for the next pulse.
418+ int64_t end_time;
419+ if (next_pulse_ptr) {
420+ end_time = min (next_pulse_ptr->time , pulse.time + pulse.len );
421+ }
422+ else {
423+ end_time = pulse.time + pulse.len ;
424+ }
425+ int64_t ramp_time = end_time - pulse.time ;
426+ assert (ramp_time >= 0 );
427+ if (ramp_time == 0 ) {
428+ // If the ramp time is 0, we let the next pulse dictate what happens.
429+ continue ;
430+ }
431+ // If previous point is not at the current time, then add a point with the previous value.
432+ if (times.back () != pulse.time ) {
433+ times.push_back (pulse.time );
434+ values.push_back (values.back ()); // Use the last value.
435+ pulse_ids.push_back (pulse_ids.back ()); // No pulse ID for this point.
436+ }
437+ // Calculate the ramp with at least pts_per_ramp points.
438+ uint64_t npts = max (2 , min ((uint64_t ) ramp_time, pts_per_ramp));
439+ for (uint64_t j = 0 ; j < npts; j++) {
440+ int64_t this_pt = round<int64_t >(j * ramp_time / (npts - 1 ));
441+ int64_t this_time = pulse.time + this_pt;
442+ times.push_back (this_time);
443+ values.push_back (pulse.ramp_func (this_pt, host_seq.values .data ()));
444+ pulse_ids.push_back (pulse.id );
445+ }
446+ }
447+ }
448+ int64_t last_time = times.back ();
449+ if (last_time > max_time) {
450+ max_time = last_time;
451+ }
452+ }
453+
454+ // Now, we have built our output for each channel, we need to allocate some memory to pass it onto the next user.
455+ // We also append an extra point if not already at the max time
456+ for (auto &[chn_id, temp_output]: temp_chn_outputs) {
457+ auto × = temp_output.times ;
458+ auto &values = temp_output.values ;
459+ auto &pulse_ids = temp_output.pulse_ids ;
460+ // If the last time is not the max time, we add an extra point with the last value.
461+ if (times.back () != max_time) {
462+ times.push_back (max_time);
463+ values.push_back (values.back ());
464+ pulse_ids.push_back (pulse_ids.back ());
465+ }
466+ auto names_ptr = (char *) malloc (temp_output.m_name .size () + 1 );
467+ auto times_ptr = (int64_t *) malloc (times.size () * sizeof (int64_t ));
468+ auto values_ptr = (double *) malloc (values.size () * sizeof (double ));
469+ auto pulse_ids_ptr = (uint32_t *) malloc (pulse_ids.size () * sizeof (uint32_t ));
470+ size_t out_pts = times.size (); // All sizes should be the same.
471+ memcpy (names_ptr, temp_output.m_name .data (), temp_output.m_name .size () + 1 );
472+ memcpy (times_ptr, times.data (), out_pts * sizeof (int64_t ));
473+ memcpy (values_ptr, values.data (), out_pts * sizeof (double ));
474+ memcpy (pulse_ids_ptr, pulse_ids.data (), out_pts * sizeof (uint32_t ));
475+ // Create the output object
476+ outputs.emplace_back (names_ptr, temp_output.m_name .size () + 1 , times_ptr, values_ptr, pulse_ids_ptr, out_pts);
477+ }
478+
479+ // Last allocation of memory for the outputs array, which carries a bunch of addresses.
480+ auto outputs_ptr = (Manager::ExpSeq::ChnOutput*) malloc(outputs.size() * sizeof (Manager::ExpSeq::ChnOutput));
481+ memcpy (outputs_ptr, outputs.data(), outputs.size() * sizeof (Manager::ExpSeq::ChnOutput));
482+ *out_sz = outputs.size();
483+
484+ return outputs_ptr;
485+ }
486+
278487NACS_EXPORT_ Manager::Manager ()
279488{
280489}
@@ -319,6 +528,7 @@ NACS_EXPORT() Manager::ExpSeq *Manager::create_sequence(const uint8_t *data, siz
319528 if (dev->check_noramp (chn_id, chn_name)) {
320529 builder.noramp_chns .push_back (chn_id);
321530 }
531+ expseq->m_chn_map .emplace (name, chn_id);
322532 }
323533 add_debug (" Building sequence\n " );
324534 builder.buildseq ();
@@ -883,4 +1093,14 @@ NACS_EXPORT() uint32_t nacs_seq_manager_expseq_refresh_device_restart(Manager::E
8831093 }, uint32_t (-1 ));
8841094}
8851095
1096+ NACS_EXPORT () Manager::ExpSeq::ChnOutput*
1097+ nacs_seq_manager_expseq_get_nominal_output (
1098+ Manager::ExpSeq *expseq, uint64_t pts_per_ramp, size_t *sz)
1099+ {
1100+ return expseq->mgr ().call_guarded ([&] () -> Manager::ExpSeq::ChnOutput* {
1101+ return expseq->get_nominal_output (pts_per_ramp, sz);
1102+ }, nullptr );
1103+ }
1104+
1105+
8861106}
0 commit comments