@@ -72,6 +72,7 @@ void MHO_BasicPlotVisitor::ConfigureSubplots()
7272 fSubplotConfig [" mbd_plot" ] = subplot_parameters (2 *nrows, ncols, 3 , 3 , 12 , 46 );
7373 fSubplotConfig [" mbd_title" ] = subplot_parameters (2 *nrows, ncols, 2 , 2 , 1 , 48 );
7474 fSubplotConfig [" mbd_amp_ytitle" ] = subplot_parameters (2 *nrows, 2 *ncols, 5 , 4 , 8 , 1 );
75+ fSubplotConfig [" dlyrate_amp_ytitle" ] = subplot_parameters (2 *nrows, 2 *ncols, 5 , 106 , 8 , 2 );
7576 fSubplotConfig [" delay_rate_xtitle" ] = subplot_parameters (2 *nrows, ncols, 2 , 2 , 15 , 48 );
7677 fSubplotConfig [" sbd_plot" ] = subplot_parameters (2 *nrows, ncols, 19 , 3 , 8 , 21 );
7778 fSubplotConfig [" sbd_amp_ytitle" ] = subplot_parameters (2 *nrows, 2 *ncols, 19 , 4 , 8 , 1 );
@@ -379,12 +380,45 @@ void MHO_BasicPlotVisitor::make_dr_mbd_plot(const mho_json& plot_dict)
379380 x_max = mbd_x.back ();
380381 }
381382
383+
384+
385+ // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
386+ // figure out if we need scientific notation for the y-axis labels, compute min/max and exponent
387+ int N = 0 ;
388+ double ymax = std::numeric_limits<double >::min ();
389+ double ymin = std::numeric_limits<double >::max ();
390+ for (std::size_t i=0 ; i<dlyrate.size (); i++)
391+ {
392+ if (dlyrate[i] > ymax){ymax = dlyrate[i];}
393+ if (dlyrate[i] < ymin){ymin = dlyrate[i];}
394+ }
395+ for (std::size_t i=0 ; i<mbd_amp.size (); i++)
396+ {
397+ if (mbd_amp[i] > ymax){ymax = mbd_amp[i];}
398+ if (mbd_amp[i] < ymin){ymin = mbd_amp[i];}
399+ }
400+ double yabsmax = std::max (std::fabs (ymax), std::fabs (ymin));
401+ if (dlyrate.empty ()){N = 0 ;}
402+ else
403+ {
404+ N = (yabsmax > 0 ) ? static_cast <int >(std::floor (std::log10 (yabsmax))) : 0 ;
405+ }
406+ double scale = std::pow (10.0 , N);
407+
408+ // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
409+
382410 // Capture the proper y-limits before any auto-expansion occurs
383411 std::array< double , 2 > proper_y_limits;
384412
385413 // Plot delay rate data (red) - use original x-axis
386414 if (!dlyrate.empty () && !dly_x.empty ())
387415 {
416+ // rescale the y-axis if needed
417+ for (std::size_t i=0 ; i<dlyrate.size (); i++)
418+ {
419+ dlyrate[i] /= scale;
420+ }
421+
388422 auto line1 = matplot::plot (dly_x, dlyrate, " r-" );
389423 line1->line_width (0 .5f );
390424
@@ -396,6 +430,7 @@ void MHO_BasicPlotVisitor::make_dr_mbd_plot(const mho_json& plot_dict)
396430 msg_debug (" plot" , " Captured original Y limits: " << proper_y_limits[0 ] << " to " << proper_y_limits[1 ] << eom);
397431 }
398432
433+
399434 // Plot MBD data (blue) - rescale x-axis to match delay rate range
400435 if (!mbd_amp.empty () && !mbd_x.empty ())
401436 {
@@ -420,6 +455,9 @@ void MHO_BasicPlotVisitor::make_dr_mbd_plot(const mho_json& plot_dict)
420455 mbd_x_rescaled[0 ] = (x_min + x_max) / 2.0 ;
421456 }
422457
458+ // scale the amplitude
459+ for (std::size_t i=0 ; i<mbd_amp.size (); i++){mbd_amp[i] /= scale;}
460+
423461 auto line2 = matplot::plot (mbd_x_rescaled, mbd_amp, " b-" );
424462 line2->line_width (0 .5f );
425463 line2->use_y2 (true );
@@ -549,7 +587,13 @@ void MHO_BasicPlotVisitor::make_dr_mbd_plot(const mho_json& plot_dict)
549587 ConstructXTitle (fSubplotConfig [" mbd_title" ], " multiband delay ({/Symbol m})" , " blue" , 9 , 0.5 , 0.5 , true );
550588 }
551589
552- ConstructYTitle (fSubplotConfig [" mbd_amp_ytitle" ], " amplitude" , " red" , 8 );
590+ std::stringstream ss;
591+ if (N != 0 ){ ss << " amplitude (x10^{" << N << " })" ; }
592+ else { ss << " amplitude" ; }
593+ ConstructYTitle (fSubplotConfig [" mbd_amp_ytitle" ], ss.str (), " blue" , 8 );
594+ ConstructYTitle (fSubplotConfig [" dlyrate_amp_ytitle" ], ss.str (), " red" , 8 );
595+
596+
553597 ConstructXTitle (fSubplotConfig [" delay_rate_xtitle" ], " delay rate (ns/s)" , " red" , 9 , 0.5 , 0.0 , true );
554598
555599}
@@ -639,13 +683,39 @@ void MHO_BasicPlotVisitor::make_sbd_dtec_plot(const mho_json& plot_dict)
639683 }
640684 }
641685
642- // Use the combined y-limits we calculated earlier (before any auto-expansion)
643- std::array< double , 2 > proper_y_limits = {y_min, y_max};
686+
687+ // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
688+
689+ // figure out if we need scientific notation for the y-axis labels, compute min/max and exponent
690+ int N = 0 ;
691+ double yabsmax = std::max (std::fabs (y_max), std::fabs (y_min));
692+ if (sbd_amp.empty ()){N = 0 ;}
693+ else
694+ {
695+ N = (yabsmax > 0 ) ? static_cast <int >(std::floor (std::log10 (yabsmax))) : 0 ;
696+ }
697+ // scale the amplitudes
698+ double scale = std::pow (10.0 , N);
699+ for (std::size_t i=0 ; i<sbd_amp.size (); i++)
700+ {
701+ sbd_amp[i] /= scale;
702+ }
703+
704+ for (std::size_t i=0 ; i<dtec_y.size (); i++)
705+ {
706+ dtec_y[i] /= scale;
707+ }
708+
709+ // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
710+
711+ // Use the combined y-limits we calculated earlier (before any auto-expansion) but rescaled
712+ std::array< double , 2 > proper_y_limits = {y_min/scale, y_max/scale};
644713
645714 // Plot SBD data (green) - use original x-axis
646715 auto sbd_line = matplot::plot (sbd_x, sbd_amp, " g-" );
647716 sbd_line->line_width (0 .5f );
648717 sbd_line->color ({34 /255.0 , 139 /255.0 , 34 /255.0 }); // forest green
718+ // if(N !=0){ fLastAxis->ytickformat("%.3g"); }
649719
650720 msg_debug (" plot" , " Using combined SBD+dTEC Y limits: " << proper_y_limits[0 ] << " to " << proper_y_limits[1 ] << eom);
651721
@@ -666,7 +736,6 @@ void MHO_BasicPlotVisitor::make_sbd_dtec_plot(const mho_json& plot_dict)
666736 // Set y-axis to original data limits
667737 matplot::ylim ({proper_y_limits[0 ], proper_y_limits[1 ]});
668738 current_ax->y_axis ().limits_mode_auto (false );
669-
670739 msg_debug (" plot" , " Set SBD/dTEC Y axis to combined data limits: " << proper_y_limits[0 ] << " to "
671740 << proper_y_limits[1 ] << eom);
672741 }
@@ -765,7 +834,14 @@ void MHO_BasicPlotVisitor::make_sbd_dtec_plot(const mho_json& plot_dict)
765834 }
766835 }
767836
768- ConstructYTitle (fSubplotConfig [" sbd_amp_ytitle" ], " amplitude" , " #228B22" , 8 );
837+ // format the y-axis and the labels/title
838+ std::stringstream ss;
839+ if (N != 0 ){ ss << " amplitude (x10^{" << N << " })" ; }
840+ else { ss << " amplitude" ; }
841+ // ax->ylabel(ss.str());
842+
843+ ConstructYTitle (fSubplotConfig [" sbd_amp_ytitle" ], ss.str (), " #228B22" , 8 );
844+ // ConstructYTitle(fSubplotConfig["sbd_amp_ytitle"], "amplitude", "#228B22", 8);
769845
770846 // Configure axis properties
771847 auto ax_handle = fLastAxis ;// matplot::gca();
@@ -808,22 +884,46 @@ void MHO_BasicPlotVisitor::make_xpower_plot(const mho_json& plot_dict)
808884 size_t data_len = std::min (xpspec_abs.size (), xpow_x.size ());
809885 std::vector< double > xpspec_abs_trunc (xpspec_abs.begin (), xpspec_abs.begin () + data_len);
810886 std::vector< double > xpspec_arg_trunc (xpspec_arg.begin (), xpspec_arg.begin () + data_len);
887+
888+ // figure out if we need scientific notation for the y-axis labels, compute min/max and exponent
889+ int N = 0 ;
890+ double ymax = std::numeric_limits<double >::min ();
891+ double ymin = std::numeric_limits<double >::max ();
892+ for (std::size_t i=0 ; i<xpspec_abs_trunc.size (); i++)
893+ {
894+ if (xpspec_abs_trunc[i] > ymax){ymax = xpspec_abs_trunc[i];}
895+ if (xpspec_abs_trunc[i] < ymin){ymin = xpspec_abs_trunc[i];}
896+ }
897+ double yabsmax = std::max (std::fabs (ymax), std::fabs (ymin));
898+ if (xpspec_abs_trunc.empty ()){N = 0 ;}
899+ else
900+ {
901+ N = (yabsmax > 0 ) ? static_cast <int >(std::floor (std::log10 (yabsmax))) : 0 ;
902+ }
903+ // scale the amplitude
904+ double scale = std::pow (10.0 , N);
905+ for (std::size_t i=0 ; i<xpspec_abs_trunc.size (); i++){xpspec_abs_trunc[i] /= scale;}
811906
812907 // Plot amplitude (blue circles)
813908 auto amp_line = matplot::plot (xpow_x, xpspec_abs_trunc, " co-" );
814909 amp_line->marker_size (2 .0f );
815910 amp_line->line_width (0 .5f );
816911 amp_line->marker_color (" blue" );
817-
818912 matplot::xlim ({xpow_x.front (), xpow_x.back ()});
819- matplot::ylabel (" amplitude" );
820913
914+ // format the y-axis and the labels/title
915+ ax->ytickformat (" %.2g" ); // 2 digits mantissa
916+ std::stringstream ss;
917+ if (N != 0 ){ ss << " amplitude (x10^{" << N << " })" ; }
918+ else { ss << " amplitude" ; }
919+ ax->ylabel (ss.str ());
920+
821921 // change the label font sizes
822922 ax->font_size (8 );
823923 ax->y_axis ().label_font_size (8 );
824924 ax->x_axis ().label_font_size (8 );
825925
826- // Configure axis properties - enable minor grid and rotate y-tick labels
926+ // Configure axis properties - enable minor grid
827927 auto ax_handle = matplot::gca ();
828928 ax_handle->minor_grid (true ); // Enable minor grid lines as substitute for minor ticks
829929
@@ -870,14 +970,20 @@ void MHO_BasicPlotVisitor::make_channel_segment_plots(const mho_json& plot_dict)
870970 std::vector< double > seg_phs_deg (seg_phs.size ());
871971 std::transform (seg_phs.begin (), seg_phs.end (), seg_phs_deg.begin (), [](double rad) { return rad * 180.0 / M_PI; });
872972
873- // Get amplitude scaling factor
874- double amp_scale = 3.0 ; // Default scaling factor
973+
974+ // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
975+ // figure out if we need scientific notation for the y-axis labels, compute min/max and exponent
976+ int N = 0 ;
977+ double scale = 1.0 ; // re-scale amplitude
978+ double amp_scale = 3.0 ;
875979 if (plot_dict.contains (" Amp" ))
876980 {
877981 try
878982 {
879983 double amp_val = MHO_PlotDataExtractor::extract_double (plot_dict, " Amp" , 1.0 );
880- amp_scale = amp_val * 3.0 ;
984+ N = static_cast <int >(std::floor (std::log10 (amp_val)));
985+ scale = std::pow (10.0 , N);
986+ amp_scale = amp_val * 3.0 /scale;
881987 }
882988 catch (const std::exception& e)
883989 {
@@ -932,7 +1038,7 @@ void MHO_BasicPlotVisitor::make_channel_segment_plots(const mho_json& plot_dict)
9321038 int idx = seg * n_plots + ch;
9331039 if (idx < static_cast < int >(seg_amp.size ()))
9341040 {
935- ch_amp[seg] = seg_amp[idx];
1041+ ch_amp[seg] = seg_amp[idx]/scale ;
9361042 }
9371043 if (idx < static_cast < int >(seg_phs_deg.size ()))
9381044 {
@@ -1022,7 +1128,12 @@ void MHO_BasicPlotVisitor::make_channel_segment_plots(const mho_json& plot_dict)
10221128
10231129
10241130 ConstructYTitle (fSubplotConfig [" channel_phase_ytitle" ], " phase [deg]" , " red" , 8 , true );
1025- ConstructYTitle (fSubplotConfig [" channel_amp_ytitle" ], " amplitude" , " blue" , 8 );
1131+
1132+ std::stringstream ss;
1133+ if (N != 0 ){ ss << " amplitude (x10^{" << N << " })" ; }
1134+ else { ss << " amplitude" ; }
1135+
1136+ ConstructYTitle (fSubplotConfig [" channel_amp_ytitle" ], ss.str (), " blue" , 8 );
10261137 }
10271138 }
10281139}
@@ -1522,19 +1633,29 @@ void MHO_BasicPlotVisitor::make_basic_info_text(const mho_json& plot_dict)
15221633 green_label (fLastAxis , y_pos, " RA, Dec (J2000)" );
15231634
15241635 // Lambda function to create right-justified text
1525- auto right_justify_text = [](matplot::axes_handle ax, double right_x, double y, const std::string& text)
1636+ auto right_justify_text = [](matplot::axes_handle ax, double right_x, double y, const std::string& text, std::string color= " " )
15261637 {
15271638 auto txt = ax->text (right_x, y, text);
15281639 txt->font_size (9 ); // Smaller font size
15291640 txt->font (" monospace" );
1641+ if (color != " " )
1642+ {
1643+ txt->color (color);
1644+ }
15301645 txt->alignment (matplot::labels::alignment::right);
15311646 };
15321647
15331648 // Create values column (right-aligned)
15341649 y_pos = y_start;
15351650
15361651 right_justify_text (fLastAxis , value_x, y_pos, quality);
1537- y_pos -= y_step * 2 ;
1652+ y_pos -= y_step;
1653+ // Add error code if present (red text, right-justified)
1654+ if (!error_code.empty () && error_code != " " )
1655+ {
1656+ right_justify_text (fLastAxis , value_x, y_pos, " Error code " + error_code, " red" );
1657+ }
1658+ y_pos -= y_step;
15381659 right_justify_text (fLastAxis , value_x, y_pos, snr);
15391660 y_pos -= y_step;
15401661 right_justify_text (fLastAxis , value_x, y_pos, intg_time);
@@ -1581,11 +1702,7 @@ void MHO_BasicPlotVisitor::make_basic_info_text(const mho_json& plot_dict)
15811702 y_pos -= y_step;
15821703 right_justify_text (fLastAxis , value_x, y_pos, dec);
15831704
1584- // Add error code if present (red text, right-justified)
1585- if (!error_code.empty () && error_code != " " )
1586- {
1587- right_justify_text (fLastAxis , value_x, 0.02 , " Error code " + error_code);
1588- }
1705+
15891706}
15901707
15911708// ============================================================================
0 commit comments