1 //
2 // Copyright 2016 Ettus Research LLC
3 // Copyright 2018 Ettus Research, a National Instruments Company
4 //
5 // SPDX-License-Identifier: GPL-3.0-or-later
6 //
7
8 #ifndef INCLUDED_DBOARD_TWINRX_EXPERTS_HPP
9 #define INCLUDED_DBOARD_TWINRX_EXPERTS_HPP
10
11 #include "twinrx_ctrl.hpp"
12 #include <uhd/utils/math.hpp>
13 #include <uhdlib/experts/expert_nodes.hpp>
14
15 namespace uhd { namespace usrp { namespace dboard { namespace twinrx {
16
17 //---------------------------------------------------------
18 // Misc types and definitions
19 //---------------------------------------------------------
20
21 struct rf_freq_abs_t : public uhd::math::fp_compare::fp_compare_delta<double>
22 {
rf_freq_abs_tuhd::usrp::dboard::twinrx::rf_freq_abs_t23 rf_freq_abs_t(double freq = 0.0, double epsilon = 1.0 /* 1Hz epsilon */)
24 : uhd::math::fp_compare::fp_compare_delta<double>(freq, epsilon)
25 {
26 }
getuhd::usrp::dboard::twinrx::rf_freq_abs_t27 inline double get() const
28 {
29 return _value;
30 }
31 };
32
33 struct rf_freq_ppm_t : public rf_freq_abs_t
34 {
rf_freq_ppm_tuhd::usrp::dboard::twinrx::rf_freq_ppm_t35 rf_freq_ppm_t(double freq = 0.0, double epsilon_ppm = 0.1 /* 1PPM epsilon */)
36 : rf_freq_abs_t(freq, 1e-6 * freq * epsilon_ppm)
37 {
38 }
39 };
40
41 enum lo_stage_t { STAGE_LO1, STAGE_LO2 };
42 enum lo_inj_side_t { INJ_LOW_SIDE, INJ_HIGH_SIDE };
43 enum lo_synth_mapping_t { MAPPING_NONE, MAPPING_CH0, MAPPING_CH1, MAPPING_SHARED };
44
prepend_ch(std::string name,const std::string & ch)45 static const std::string prepend_ch(std::string name, const std::string& ch)
46 {
47 return ch + "/" + name;
48 }
49
lo_stage_str(lo_stage_t stage,bool lower=false)50 static const std::string lo_stage_str(lo_stage_t stage, bool lower = false)
51 {
52 std::string prefix = lower ? "lo" : "LO";
53 return prefix + ((stage == STAGE_LO1) ? "1" : "2");
54 }
55
56
57 /*!---------------------------------------------------------
58 * twinrx_scheduling_expert
59 *
60 * This expert is responsible for scheduling time sensitive actions
61 * in other experts. It responds to changes in the command time and
62 * selectively causes experts to run in order to ensure a synchronized
63 * system.
64 *
65 * ---------------------------------------------------------
66 */
67 class twinrx_scheduling_expert : public experts::worker_node_t
68 {
69 public:
twinrx_scheduling_expert(const experts::node_retriever_t & db,std::string ch)70 twinrx_scheduling_expert(const experts::node_retriever_t& db, std::string ch)
71 : experts::worker_node_t(prepend_ch("twinrx_scheduling_expert", ch))
72 , _command_time(db, prepend_ch("time/cmd", ch))
73 , _rx_frontend_time(db, prepend_ch("time/rx_frontend", ch))
74 {
75 bind_accessor(_command_time);
76 bind_accessor(_rx_frontend_time);
77 }
78
79 private:
80 virtual void resolve();
81
82 // Inputs
83 experts::data_reader_t<time_spec_t> _command_time;
84
85 // Outputs
86 experts::data_writer_t<time_spec_t> _rx_frontend_time;
87 };
88
89 /*!---------------------------------------------------------
90 * twinrx_freq_path_expert
91 *
92 * This expert is responsble for translating a user-specified
93 * RF and IF center frequency into TwinRX specific settings
94 * like band, preselector path, LO frequency and injection
95 * sides for both the LO stages.
96 *
97 * One instance of this expert is required for each channel
98 * ---------------------------------------------------------
99 */
100 class twinrx_freq_path_expert : public experts::worker_node_t
101 {
102 public:
twinrx_freq_path_expert(const experts::node_retriever_t & db,std::string ch)103 twinrx_freq_path_expert(const experts::node_retriever_t& db, std::string ch)
104 : experts::worker_node_t(prepend_ch("twinrx_freq_path_expert", ch))
105 , _rf_freq_d(db, prepend_ch("freq/desired", ch))
106 , _if_freq_d(db, prepend_ch("if_freq/desired", ch))
107 , _signal_path(db, prepend_ch("ch/signal_path", ch))
108 , _lb_presel(db, prepend_ch("ch/lb_presel", ch))
109 , _hb_presel(db, prepend_ch("ch/hb_presel", ch))
110 , _lb_preamp_presel(db, prepend_ch("ch/lb_preamp_presel", ch))
111 , _lo1_freq_d(db, prepend_ch("los/LO1/freq/desired", ch))
112 , _lo2_freq_d(db, prepend_ch("los/LO2/freq/desired", ch))
113 , _lo1_inj_side(db, prepend_ch("ch/LO1/inj_side", ch))
114 , _lo2_inj_side(db, prepend_ch("ch/LO2/inj_side", ch))
115 {
116 bind_accessor(_rf_freq_d);
117 bind_accessor(_if_freq_d);
118 bind_accessor(_signal_path);
119 bind_accessor(_lb_presel);
120 bind_accessor(_hb_presel);
121 bind_accessor(_lb_preamp_presel);
122 bind_accessor(_lo1_freq_d);
123 bind_accessor(_lo2_freq_d);
124 bind_accessor(_lo1_inj_side);
125 bind_accessor(_lo2_inj_side);
126 }
127
128 private:
129 virtual void resolve();
130 static lo_inj_side_t _compute_lo2_inj_side(
131 double lo1_freq, double if1_freq, double if2_freq, double bandwidth);
132 static bool _has_mixer_spurs(double lo1_freq,
133 double lo2_freq,
134 double if2_freq,
135 double bandwidth,
136 int spur_order);
137
138 // Inputs
139 experts::data_reader_t<double> _rf_freq_d;
140 experts::data_reader_t<double> _if_freq_d;
141 // Outputs
142 experts::data_writer_t<twinrx_ctrl::signal_path_t> _signal_path;
143 experts::data_writer_t<twinrx_ctrl::preselector_path_t> _lb_presel;
144 experts::data_writer_t<twinrx_ctrl::preselector_path_t> _hb_presel;
145 experts::data_writer_t<bool> _lb_preamp_presel;
146 experts::data_writer_t<double> _lo1_freq_d;
147 experts::data_writer_t<double> _lo2_freq_d;
148 experts::data_writer_t<lo_inj_side_t> _lo1_inj_side;
149 experts::data_writer_t<lo_inj_side_t> _lo2_inj_side;
150 };
151
152 /*!---------------------------------------------------------
153 * twinrx_lo_config_expert
154 *
155 * This expert is responsible for translating high level
156 * channel-scoped LO source and export settings to low-level
157 * channel-scoped settings. The expert only deals with
158 * the source and export attributes, not frequency.
159 *
160 * One instance of this expert is required for all channels
161 * ---------------------------------------------------------
162 */
163 class twinrx_lo_config_expert : public experts::worker_node_t
164 {
165 public:
twinrx_lo_config_expert(const experts::node_retriever_t & db)166 twinrx_lo_config_expert(const experts::node_retriever_t& db)
167 : experts::worker_node_t("twinrx_lo_config_expert")
168 , _lo_source_ch0(db, prepend_ch("los/all/source", "0"))
169 , _lo_source_ch1(db, prepend_ch("los/all/source", "1"))
170 , _lo_export_ch0(db, prepend_ch("los/all/export", "0"))
171 , _lo_export_ch1(db, prepend_ch("los/all/export", "1"))
172 , _lo1_src_ch0(db, prepend_ch("ch/LO1/source", "0"))
173 , _lo1_src_ch1(db, prepend_ch("ch/LO1/source", "1"))
174 , _lo2_src_ch0(db, prepend_ch("ch/LO2/source", "0"))
175 , _lo2_src_ch1(db, prepend_ch("ch/LO2/source", "1"))
176 , _lo1_export_src(db, "com/LO1/export_source")
177 , _lo2_export_src(db, "com/LO2/export_source")
178 {
179 bind_accessor(_lo_source_ch0);
180 bind_accessor(_lo_source_ch1);
181 bind_accessor(_lo_export_ch0);
182 bind_accessor(_lo_export_ch1);
183 bind_accessor(_lo1_src_ch0);
184 bind_accessor(_lo1_src_ch1);
185 bind_accessor(_lo2_src_ch0);
186 bind_accessor(_lo2_src_ch1);
187 bind_accessor(_lo1_export_src);
188 bind_accessor(_lo2_export_src);
189 }
190
191 private:
192 virtual void resolve();
193
194 // Inputs
195 experts::data_reader_t<std::string> _lo_source_ch0;
196 experts::data_reader_t<std::string> _lo_source_ch1;
197 experts::data_reader_t<bool> _lo_export_ch0;
198 experts::data_reader_t<bool> _lo_export_ch1;
199 // Outputs
200 experts::data_writer_t<twinrx_ctrl::lo_source_t> _lo1_src_ch0;
201 experts::data_writer_t<twinrx_ctrl::lo_source_t> _lo1_src_ch1;
202 experts::data_writer_t<twinrx_ctrl::lo_source_t> _lo2_src_ch0;
203 experts::data_writer_t<twinrx_ctrl::lo_source_t> _lo2_src_ch1;
204 experts::data_writer_t<twinrx_ctrl::lo_export_source_t> _lo1_export_src;
205 experts::data_writer_t<twinrx_ctrl::lo_export_source_t> _lo2_export_src;
206 };
207
208 /*!---------------------------------------------------------
209 * twinrx_lo_mapping_expert
210 *
211 * This expert is responsible for translating low-level
212 * channel-scoped LO source and export settings to low-level
213 * synthesizer-scoped settings. The expert deals with the
214 * extremely flexible channel->synthesizer mapping and handles
215 * frequency hopping modes.
216 *
217 * One instance of this expert is required for each LO stage
218 * ---------------------------------------------------------
219 */
220 class twinrx_lo_mapping_expert : public experts::worker_node_t
221 {
222 public:
twinrx_lo_mapping_expert(const experts::node_retriever_t & db,lo_stage_t stage)223 twinrx_lo_mapping_expert(const experts::node_retriever_t& db, lo_stage_t stage)
224 : experts::worker_node_t(
225 "twinrx_" + lo_stage_str(stage, true) + "_mapping_expert")
226 , _lox_src_ch0(db, prepend_ch("ch/" + lo_stage_str(stage) + "/source", "0"))
227 , _lox_src_ch1(db, prepend_ch("ch/" + lo_stage_str(stage) + "/source", "1"))
228 , _lox_mapping_synth0(
229 db, prepend_ch("synth/" + lo_stage_str(stage) + "/mapping", "0"))
230 , _lox_mapping_synth1(
231 db, prepend_ch("synth/" + lo_stage_str(stage) + "/mapping", "1"))
232 , _lox_hopping_enabled(
233 db, "com/synth/" + lo_stage_str(stage) + "/hopping_enabled")
234 {
235 bind_accessor(_lox_src_ch0);
236 bind_accessor(_lox_src_ch1);
237 bind_accessor(_lox_mapping_synth0);
238 bind_accessor(_lox_mapping_synth1);
239 bind_accessor(_lox_hopping_enabled);
240 }
241
242 private:
243 virtual void resolve();
244
245 // Inputs
246 experts::data_reader_t<twinrx_ctrl::lo_source_t> _lox_src_ch0;
247 experts::data_reader_t<twinrx_ctrl::lo_source_t> _lox_src_ch1;
248 // Outputs
249 experts::data_writer_t<lo_synth_mapping_t> _lox_mapping_synth0;
250 experts::data_writer_t<lo_synth_mapping_t> _lox_mapping_synth1;
251 experts::data_writer_t<bool> _lox_hopping_enabled;
252 };
253
254 /*!---------------------------------------------------------
255 * twinrx_freq_coercion_expert
256 *
257 * This expert is responsible for calculating the coerced
258 * RF frequency after most settings and modes have been
259 * resolved.
260 *
261 * One instance of this expert is required for each channel
262 * ---------------------------------------------------------
263 */
264 class twinrx_freq_coercion_expert : public experts::worker_node_t
265 {
266 public:
twinrx_freq_coercion_expert(const experts::node_retriever_t & db,std::string ch)267 twinrx_freq_coercion_expert(const experts::node_retriever_t& db, std::string ch)
268 : experts::worker_node_t(prepend_ch("twinrx_freq_coercion_expert", ch))
269 , _lo1_freq_c(db, prepend_ch("los/LO1/freq/coerced", ch))
270 , _lo2_freq_c(db, prepend_ch("los/LO2/freq/coerced", ch))
271 , _if_freq_d(db, prepend_ch("if_freq/desired", ch))
272 , _lo1_inj_side(db, prepend_ch("ch/LO1/inj_side", ch))
273 , _lo2_inj_side(db, prepend_ch("ch/LO2/inj_side", ch))
274 , _rf_freq_c(db, prepend_ch("freq/coerced", ch))
275 {
276 bind_accessor(_lo1_freq_c);
277 bind_accessor(_lo2_freq_c);
278 bind_accessor(_if_freq_d);
279 bind_accessor(_lo1_inj_side);
280 bind_accessor(_lo2_inj_side);
281 bind_accessor(_rf_freq_c);
282 }
283
284 private:
285 virtual void resolve();
286
287 // Inputs
288 experts::data_reader_t<double> _lo1_freq_c;
289 experts::data_reader_t<double> _lo2_freq_c;
290 experts::data_reader_t<double> _if_freq_d;
291 experts::data_reader_t<lo_inj_side_t> _lo1_inj_side;
292 experts::data_reader_t<lo_inj_side_t> _lo2_inj_side;
293 // Outputs
294 experts::data_writer_t<double> _rf_freq_c;
295 };
296
297 /*!---------------------------------------------------------
298 * twinrx_nyquist_expert
299 *
300 * This expert is responsible for figuring out the DSP
301 * front-end settings required for each channel
302 *
303 * One instance of this expert is required for each channel
304 * ---------------------------------------------------------
305 */
306 class twinrx_nyquist_expert : public experts::worker_node_t
307 {
308 public:
twinrx_nyquist_expert(const experts::node_retriever_t & db,std::string ch,dboard_iface::sptr db_iface)309 twinrx_nyquist_expert(
310 const experts::node_retriever_t& db, std::string ch, dboard_iface::sptr db_iface)
311 : experts::worker_node_t(prepend_ch("twinrx_nyquist_expert", ch))
312 , _channel(ch)
313 , _codec_conn(ch == "0" ? "II" : "QQ")
314 , // Ch->ADC Port mapping
315 _lo1_freq_d(db, prepend_ch("los/LO1/freq/desired", ch))
316 , _lo2_freq_d(db, prepend_ch("los/LO2/freq/desired", ch))
317 , _if_freq_d(db, prepend_ch("if_freq/desired", ch))
318 , _lo1_inj_side(db, prepend_ch("ch/LO1/inj_side", ch))
319 , _lo2_inj_side(db, prepend_ch("ch/LO2/inj_side", ch))
320 , _rx_frontend_time(db, prepend_ch("time/rx_frontend", ch))
321 , _if_freq_c(db, prepend_ch("if_freq/coerced", ch))
322 , _db_iface(db_iface)
323 {
324 bind_accessor(_lo1_freq_d);
325 bind_accessor(_lo2_freq_d);
326 bind_accessor(_if_freq_d);
327 bind_accessor(_lo1_inj_side);
328 bind_accessor(_lo2_inj_side);
329 bind_accessor(_if_freq_c);
330 bind_accessor(_rx_frontend_time);
331 }
332
333 private:
334 virtual void resolve();
335
336 // Inputs
337 const std::string _channel;
338 const std::string _codec_conn;
339 experts::data_reader_t<double> _lo1_freq_d;
340 experts::data_reader_t<double> _lo2_freq_d;
341 experts::data_reader_t<double> _if_freq_d;
342 experts::data_reader_t<lo_inj_side_t> _lo1_inj_side;
343 experts::data_reader_t<lo_inj_side_t> _lo2_inj_side;
344 experts::data_reader_t<time_spec_t> _rx_frontend_time;
345
346 // Outputs
347 experts::data_writer_t<double> _if_freq_c;
348 dboard_iface::sptr _db_iface;
349
350 // Misc
351 time_spec_t _cached_cmd_time;
352 };
353
354 /*!---------------------------------------------------------
355 * twinrx_antenna_expert
356 *
357 * This expert is responsible for translating high-level
358 * antenna selection settings and channel enables to low-level
359 * switch configurations.
360 *
361 * One instance of this expert is required for all channels
362 * ---------------------------------------------------------
363 */
364 class twinrx_antenna_expert : public experts::worker_node_t
365 {
366 public:
twinrx_antenna_expert(const experts::node_retriever_t & db)367 twinrx_antenna_expert(const experts::node_retriever_t& db)
368 : experts::worker_node_t("twinrx_antenna_expert")
369 , _antenna_ch0(db, prepend_ch("antenna", "0"))
370 , _antenna_ch1(db, prepend_ch("antenna", "1"))
371 , _enabled_ch0(db, prepend_ch("enabled", "0"))
372 , _enabled_ch1(db, prepend_ch("enabled", "1"))
373 , _lo_export_ch0(db, prepend_ch("los/all/export", "0"))
374 , _lo_export_ch1(db, prepend_ch("los/all/export", "1"))
375 , _ant_mapping(db, "com/ant_mapping")
376 , _cal_mode(db, "com/cal_mode")
377 , _id_ch0(db, prepend_ch("id", "0"))
378 , _id_ch1(db, prepend_ch("id", "1"))
379 {
380 bind_accessor(_antenna_ch0);
381 bind_accessor(_antenna_ch1);
382 bind_accessor(_enabled_ch0);
383 bind_accessor(_enabled_ch1);
384 bind_accessor(_lo_export_ch0);
385 bind_accessor(_lo_export_ch1);
386 bind_accessor(_ant_mapping);
387 bind_accessor(_cal_mode);
388 bind_accessor(_id_ch0);
389 bind_accessor(_id_ch1);
390 }
391
392 private:
393 virtual void resolve();
394
395 // Inputs
396 experts::data_reader_t<std::string> _antenna_ch0;
397 experts::data_reader_t<std::string> _antenna_ch1;
398 experts::data_reader_t<bool> _enabled_ch0;
399 experts::data_reader_t<bool> _enabled_ch1;
400 experts::data_reader_t<bool> _lo_export_ch0;
401 experts::data_reader_t<bool> _lo_export_ch1;
402 // Outputs
403 experts::data_writer_t<twinrx_ctrl::antenna_mapping_t> _ant_mapping;
404 experts::data_writer_t<twinrx_ctrl::cal_mode_t> _cal_mode;
405
406 experts::data_writer_t<std::string> _id_ch0;
407 experts::data_writer_t<std::string> _id_ch1;
408 };
409
410 /*!---------------------------------------------------------
411 * twinrx_chan_gain_expert
412 *
413 * This expert is responsible for mapping high-level channel
414 * gain settings to individual attenuator and amp configurations
415 * that are also channel-scoped. This expert will implement
416 * the gain distribution strategy.
417 *
418 * One instance of this expert is required for each channel
419 * ---------------------------------------------------------
420 */
421 class twinrx_chan_gain_expert : public experts::worker_node_t
422 {
423 public:
twinrx_chan_gain_expert(const experts::node_retriever_t & db,std::string ch)424 twinrx_chan_gain_expert(const experts::node_retriever_t& db, std::string ch)
425 : experts::worker_node_t(prepend_ch("twinrx_chan_gain_expert", ch))
426 , _gain(db, prepend_ch("gain", ch))
427 , _gain_profile(db, prepend_ch("gain_profile", ch))
428 , _signal_path(db, prepend_ch("ch/signal_path", ch))
429 , _lb_presel(db, prepend_ch("ch/lb_presel", ch))
430 , _hb_presel(db, prepend_ch("ch/hb_presel", ch))
431 , _ant_mapping(db, "com/ant_mapping")
432 , _input_atten(db, prepend_ch("ch/input_atten", ch))
433 , _lb_atten(db, prepend_ch("ch/lb_atten", ch))
434 , _hb_atten(db, prepend_ch("ch/hb_atten", ch))
435 , _preamp1(db, prepend_ch("ch/preamp1", ch))
436 , _preamp2(db, prepend_ch("ch/preamp2", ch))
437 {
438 bind_accessor(_gain);
439 bind_accessor(_gain_profile);
440 bind_accessor(_signal_path);
441 bind_accessor(_lb_presel);
442 bind_accessor(_hb_presel);
443 bind_accessor(_ant_mapping);
444 bind_accessor(_input_atten);
445 bind_accessor(_lb_atten);
446 bind_accessor(_hb_atten);
447 bind_accessor(_preamp1);
448 bind_accessor(_preamp2);
449 }
450
451 private:
452 virtual void resolve();
453
454 // Inputs
455 experts::data_reader_t<double> _gain;
456 experts::data_reader_t<std::string> _gain_profile;
457 experts::data_reader_t<twinrx_ctrl::signal_path_t> _signal_path;
458 experts::data_reader_t<twinrx_ctrl::preselector_path_t> _lb_presel;
459 experts::data_reader_t<twinrx_ctrl::preselector_path_t> _hb_presel;
460 experts::data_reader_t<twinrx_ctrl::antenna_mapping_t> _ant_mapping;
461 // Outputs
462 experts::data_writer_t<uint8_t> _input_atten;
463 experts::data_writer_t<uint8_t> _lb_atten;
464 experts::data_writer_t<uint8_t> _hb_atten;
465 experts::data_writer_t<twinrx_ctrl::preamp_state_t> _preamp1;
466 experts::data_writer_t<bool> _preamp2;
467 };
468
469 /*!---------------------------------------------------------
470 * twinrx_ant_gain_expert
471 *
472 * This expert is responsible for translating between the
473 * channel-scoped low-level gain settings to antenna-scoped
474 * gain settings.
475 *
476 * One instance of this expert is required for all channels
477 * ---------------------------------------------------------
478 */
479 class twinrx_ant_gain_expert : public experts::worker_node_t
480 {
481 public:
twinrx_ant_gain_expert(const experts::node_retriever_t & db)482 twinrx_ant_gain_expert(const experts::node_retriever_t& db)
483 : experts::worker_node_t("twinrx_ant_gain_expert")
484 , _ant_mapping(db, "com/ant_mapping")
485 , _ch0_input_atten(db, prepend_ch("ch/input_atten", "0"))
486 , _ch0_preamp1(db, prepend_ch("ch/preamp1", "0"))
487 , _ch0_preamp2(db, prepend_ch("ch/preamp2", "0"))
488 , _ch0_lb_preamp_presel(db, prepend_ch("ch/lb_preamp_presel", "0"))
489 , _ch1_input_atten(db, prepend_ch("ch/input_atten", "1"))
490 , _ch1_preamp1(db, prepend_ch("ch/preamp1", "1"))
491 , _ch1_preamp2(db, prepend_ch("ch/preamp2", "1"))
492 , _ch1_lb_preamp_presel(db, prepend_ch("ch/lb_preamp_presel", "1"))
493 , _ant0_input_atten(db, prepend_ch("ant/input_atten", "0"))
494 , _ant0_preamp1(db, prepend_ch("ant/preamp1", "0"))
495 , _ant0_preamp2(db, prepend_ch("ant/preamp2", "0"))
496 , _ant0_lb_preamp_presel(db, prepend_ch("ant/lb_preamp_presel", "0"))
497 , _ant1_input_atten(db, prepend_ch("ant/input_atten", "1"))
498 , _ant1_preamp1(db, prepend_ch("ant/preamp1", "1"))
499 , _ant1_preamp2(db, prepend_ch("ant/preamp2", "1"))
500 , _ant1_lb_preamp_presel(db, prepend_ch("ant/lb_preamp_presel", "1"))
501 {
502 bind_accessor(_ant_mapping);
503 bind_accessor(_ch0_input_atten);
504 bind_accessor(_ch0_preamp1);
505 bind_accessor(_ch0_preamp2);
506 bind_accessor(_ch0_lb_preamp_presel);
507 bind_accessor(_ch1_input_atten);
508 bind_accessor(_ch1_preamp1);
509 bind_accessor(_ch1_preamp2);
510 bind_accessor(_ch1_lb_preamp_presel);
511 bind_accessor(_ant0_input_atten);
512 bind_accessor(_ant0_preamp1);
513 bind_accessor(_ant0_preamp2);
514 bind_accessor(_ant0_lb_preamp_presel);
515 bind_accessor(_ant1_input_atten);
516 bind_accessor(_ant1_preamp1);
517 bind_accessor(_ant1_preamp2);
518 bind_accessor(_ant1_lb_preamp_presel);
519 }
520
521 private:
522 virtual void resolve();
523
524 // Inputs
525 experts::data_reader_t<twinrx_ctrl::antenna_mapping_t> _ant_mapping;
526 experts::data_reader_t<uint8_t> _ch0_input_atten;
527 experts::data_reader_t<twinrx_ctrl::preamp_state_t> _ch0_preamp1;
528 experts::data_reader_t<bool> _ch0_preamp2;
529 experts::data_reader_t<bool> _ch0_lb_preamp_presel;
530 experts::data_reader_t<uint8_t> _ch1_input_atten;
531 experts::data_reader_t<twinrx_ctrl::preamp_state_t> _ch1_preamp1;
532 experts::data_reader_t<bool> _ch1_preamp2;
533 experts::data_reader_t<bool> _ch1_lb_preamp_presel;
534
535 // Outputs
536 experts::data_writer_t<uint8_t> _ant0_input_atten;
537 experts::data_writer_t<twinrx_ctrl::preamp_state_t> _ant0_preamp1;
538 experts::data_writer_t<bool> _ant0_preamp2;
539 experts::data_writer_t<bool> _ant0_lb_preamp_presel;
540 experts::data_writer_t<uint8_t> _ant1_input_atten;
541 experts::data_writer_t<twinrx_ctrl::preamp_state_t> _ant1_preamp1;
542 experts::data_writer_t<bool> _ant1_preamp2;
543 experts::data_writer_t<bool> _ant1_lb_preamp_presel;
544 };
545
546 /*!---------------------------------------------------------
547 * twinrx_settings_expert
548 *
549 * This expert is responsible for gathering all low-level
550 * settings and writing them to hardware. All LO frequency
551 * settings are cached with a hysteresis. All other settings
552 * are always written to twinrx_ctrl and rely on register
553 * level caching.
554 *
555 * One instance of this expert is required for all channels
556 * ---------------------------------------------------------
557 */
558 class twinrx_settings_expert : public experts::worker_node_t
559 {
560 public:
twinrx_settings_expert(const experts::node_retriever_t & db,twinrx_ctrl::sptr ctrl)561 twinrx_settings_expert(const experts::node_retriever_t& db, twinrx_ctrl::sptr ctrl)
562 : experts::worker_node_t("twinrx_settings_expert")
563 , _ctrl(ctrl)
564 , _ch0(db, "0")
565 , _ch1(db, "1")
566 , _lo1_synth0_mapping(db, "0/synth/LO1/mapping")
567 , _lo1_synth1_mapping(db, "1/synth/LO1/mapping")
568 , _lo2_synth0_mapping(db, "0/synth/LO2/mapping")
569 , _lo2_synth1_mapping(db, "1/synth/LO2/mapping")
570 , _lo1_hopping_enabled(db, "com/synth/LO1/hopping_enabled")
571 , _lo2_hopping_enabled(db, "com/synth/LO2/hopping_enabled")
572 , _lo1_export_src(db, "com/LO1/export_source")
573 , _lo2_export_src(db, "com/LO2/export_source")
574 , _ant_mapping(db, "com/ant_mapping")
575 , _cal_mode(db, "com/cal_mode")
576 {
577 for (size_t i = 0; i < 2; i++) {
578 ch_settings& ch = (i == 1) ? _ch1 : _ch0;
579 bind_accessor(ch.chan_enabled);
580 bind_accessor(ch.preamp1);
581 bind_accessor(ch.preamp2);
582 bind_accessor(ch.lb_preamp_presel);
583 bind_accessor(ch.signal_path);
584 bind_accessor(ch.lb_presel);
585 bind_accessor(ch.hb_presel);
586 bind_accessor(ch.input_atten);
587 bind_accessor(ch.lb_atten);
588 bind_accessor(ch.hb_atten);
589 bind_accessor(ch.lo1_source);
590 bind_accessor(ch.lo2_source);
591 bind_accessor(ch.lo1_freq_d);
592 bind_accessor(ch.lo2_freq_d);
593 bind_accessor(ch.lo1_freq_c);
594 bind_accessor(ch.lo2_freq_c);
595 bind_accessor(ch.lo1_charge_pump_c);
596 bind_accessor(ch.lo2_charge_pump_c);
597 bind_accessor(ch.lo1_charge_pump_d);
598 bind_accessor(ch.lo2_charge_pump_d);
599 }
600 bind_accessor(_lo1_synth0_mapping);
601 bind_accessor(_lo1_synth1_mapping);
602 bind_accessor(_lo2_synth0_mapping);
603 bind_accessor(_lo2_synth1_mapping);
604 bind_accessor(_lo1_hopping_enabled);
605 bind_accessor(_lo2_hopping_enabled);
606 bind_accessor(_lo1_export_src);
607 bind_accessor(_lo2_export_src);
608 bind_accessor(_ant_mapping);
609 bind_accessor(_cal_mode);
610 }
611
612 private:
613 virtual void resolve();
614 void _resolve_lox_freq(lo_stage_t lo_stage,
615 experts::data_reader_t<double>& ch0_freq_d,
616 experts::data_reader_t<double>& ch1_freq_d,
617 experts::data_writer_t<double>& ch0_freq_c,
618 experts::data_writer_t<double>& ch1_freq_c,
619 twinrx_ctrl::lo_source_t ch0_lo_source,
620 twinrx_ctrl::lo_source_t ch1_lo_source,
621 lo_synth_mapping_t synth0_mapping,
622 lo_synth_mapping_t synth1_mapping,
623 bool hopping_enabled);
624 double _set_lox_synth_freq(lo_stage_t stage, twinrx_ctrl::channel_t ch, double freq);
625
626 class ch_settings
627 {
628 public:
ch_settings(const experts::node_retriever_t & db,const std::string & ch)629 ch_settings(const experts::node_retriever_t& db, const std::string& ch)
630 : chan_enabled(db, prepend_ch("enabled", ch))
631 , preamp1(db, prepend_ch("ant/preamp1", ch))
632 , preamp2(db, prepend_ch("ant/preamp2", ch))
633 , lb_preamp_presel(db, prepend_ch("ant/lb_preamp_presel", ch))
634 , signal_path(db, prepend_ch("ch/signal_path", ch))
635 , lb_presel(db, prepend_ch("ch/lb_presel", ch))
636 , hb_presel(db, prepend_ch("ch/hb_presel", ch))
637 , input_atten(db, prepend_ch("ant/input_atten", ch))
638 , lb_atten(db, prepend_ch("ch/lb_atten", ch))
639 , hb_atten(db, prepend_ch("ch/hb_atten", ch))
640 , lo1_source(db, prepend_ch("ch/LO1/source", ch))
641 , lo2_source(db, prepend_ch("ch/LO2/source", ch))
642 , lo1_freq_d(db, prepend_ch("los/LO1/freq/desired", ch))
643 , lo2_freq_d(db, prepend_ch("los/LO2/freq/desired", ch))
644 , lo1_charge_pump_d(db, prepend_ch("los/LO1/charge_pump/desired", ch))
645 , lo2_charge_pump_d(db, prepend_ch("los/LO2/charge_pump/desired", ch))
646 , lo1_freq_c(db, prepend_ch("los/LO1/freq/coerced", ch))
647 , lo2_freq_c(db, prepend_ch("los/LO2/freq/coerced", ch))
648 , lo1_charge_pump_c(db, prepend_ch("los/LO1/charge_pump/coerced", ch))
649 , lo2_charge_pump_c(db, prepend_ch("los/LO2/charge_pump/coerced", ch))
650 {
651 }
652
653 // Inputs (channel specific)
654 experts::data_reader_t<bool> chan_enabled;
655 experts::data_reader_t<twinrx_ctrl::preamp_state_t> preamp1;
656 experts::data_reader_t<bool> preamp2;
657 experts::data_reader_t<bool> lb_preamp_presel;
658 experts::data_reader_t<twinrx_ctrl::signal_path_t> signal_path;
659 experts::data_reader_t<twinrx_ctrl::preselector_path_t> lb_presel;
660 experts::data_reader_t<twinrx_ctrl::preselector_path_t> hb_presel;
661 experts::data_reader_t<uint8_t> input_atten;
662 experts::data_reader_t<uint8_t> lb_atten;
663 experts::data_reader_t<uint8_t> hb_atten;
664 experts::data_reader_t<twinrx_ctrl::lo_source_t> lo1_source;
665 experts::data_reader_t<twinrx_ctrl::lo_source_t> lo2_source;
666 experts::data_reader_t<double> lo1_freq_d;
667 experts::data_reader_t<double> lo2_freq_d;
668 experts::data_reader_t<double> lo1_charge_pump_d;
669 experts::data_reader_t<double> lo2_charge_pump_d;
670
671 // Output (channel specific)
672 experts::data_writer_t<double> lo1_freq_c;
673 experts::data_writer_t<double> lo2_freq_c;
674 experts::data_writer_t<double> lo1_charge_pump_c;
675 experts::data_writer_t<double> lo2_charge_pump_c;
676 };
677
678 // External interface
679 twinrx_ctrl::sptr _ctrl;
680
681 // Inputs (channel agnostic)
682 ch_settings _ch0;
683 ch_settings _ch1;
684 experts::data_reader_t<lo_synth_mapping_t> _lo1_synth0_mapping;
685 experts::data_reader_t<lo_synth_mapping_t> _lo1_synth1_mapping;
686 experts::data_reader_t<lo_synth_mapping_t> _lo2_synth0_mapping;
687 experts::data_reader_t<lo_synth_mapping_t> _lo2_synth1_mapping;
688 experts::data_reader_t<bool> _lo1_hopping_enabled;
689 experts::data_reader_t<bool> _lo2_hopping_enabled;
690 experts::data_reader_t<twinrx_ctrl::lo_export_source_t> _lo1_export_src;
691 experts::data_reader_t<twinrx_ctrl::lo_export_source_t> _lo2_export_src;
692 experts::data_reader_t<twinrx_ctrl::antenna_mapping_t> _ant_mapping;
693 experts::data_reader_t<twinrx_ctrl::cal_mode_t> _cal_mode;
694
695 // Outputs (channel agnostic)
696 // None
697
698 // Misc
699 struct lo_freq_cache_t
700 {
701 rf_freq_ppm_t desired;
702 double coerced;
703 };
704 lo_freq_cache_t _cached_lo1_synth0_freq;
705 lo_freq_cache_t _cached_lo2_synth0_freq;
706 lo_freq_cache_t _cached_lo1_synth1_freq;
707 lo_freq_cache_t _cached_lo2_synth1_freq;
708
709 static const bool FORCE_COMMIT;
710 };
711
712
713 }}}} // namespace uhd::usrp::dboard::twinrx
714
715 #endif /* INCLUDED_DBOARD_TWINRX_EXPERTS_HPP */
716