Welcome to the new FlexRadio Community! Please review the new Community Rules and other important new Community information on the Message Board.
If you are having a problem, please refer to the product documentation or check the Help Center for known solutions.
Need technical support from FlexRadio? It's as simple as Creating a HelpDesk ticket.

.NET API "Hello World?"

n8wrl
n8wrl Member ✭✭

Is there a "Hello World" for using the .NET API?

I've combed thru the forums and googled and found enough to cause me some trouble... I can compile but not run. I won't bore you with the details... specific issues are:

I'm building for .NET 7 with C# - ok?

What architecture should I target? I see warnings about the DLL's being built for x86 and I'm targeting "Any CPU" and the errors I get at run-time indicate that it can't find the DLL's to load them.

I am sure there are individual answers to these questions, but I'm sure I'll run into the next problem (i.e. I learned to find a radio via the API static class - who knew?)

A very brief sample project that imports the correct DLL's, builds, and, maybe, finds a radio would be very helpful!

Thanks, and 73!

-Brian n8wrl

«1

Answers

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Hi Brian, I put a sample out on GitHub. I think you can find it by searching GitHub for my call.

    It discovers a Flex radio on the network and connects as a non-GUI client. It binds to the first GUI client it finds but allows you to bind to the other station if one is connected.

    It is just a sampler of things that I tried to help me understand Flexlib and C#.

    Let me know if you have any problems finding it or getting it to work.

  • n8wrl
    n8wrl Member ✭✭

    I am getting closer, many thanks to Len and access to the source.

    I can Get a "Radio" but I may be confused about what that radio represents.

    I am running SmartSDR at the same time, and I want my code to manipulate what SmartSDR is doing. That is, I want to mute/unmute audio, but nothing happens, I want to get the "active slice" and do things, but the ActiveSlice property is always null.

    I suspect the API is giving me my own private connection to the radio to do as I please, but what I really want is to manipulate the radio as SmartSDR sees it.

    What am I missing?

    73 and TIA

    -Brian n8wrl

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Hi Brian, I think that I had problems with the Active Slice construct as well. Was a while ago, so I kind of forget...

    In all of this, I did not use Active Slice, just slice0 and slice1 (if I had a 6700, I would have coded for all eight possible slices). The concept of an active slice is used by SmartSDR so that it knows which slice to apply mouse wheel frequency changes to. So for what I do, and what I think that you will be doing, the active slice is not really important.

    Did you get the program that I wrote working? It might provide some insight. You should get a screen something like this:

    Note that the controls on the left always correspond to slice0 and on the right, slice1. In this case they are named A and B respectively. The A and B designators came from the API, not my code. Since I only have one GUI client connected, "KD0RC Big Laptop" shows up as the station connected and that is reflected above the frequency boxes. If I had another GUI client connected, then both would be labelled "Slice A", but would have the corresponding station name displayed. The first one connected is slice0 and the second is slice1.


    In the main form code, you need to set up the API:

        public frmMain()
        {
          InitializeComponent();
    
          API.RadioAdded += API_RadioAdded;
          API.RadioRemoved += API_RadioRemoved;
          API.ProgramName = "KD0RC mini utility";
          API.Init();
    
          mainSpeechSynthesizer.SelectVoiceByHints(System.Speech.Synthesis.VoiceGender.Female);
          TimeIt.Start();
        }
    


    In the radio added routine, you set up the vectors to the various API events:

    private void API_RadioAdded(Radio radio)
        {
          _thisRadio = radio;
    
          SerNum = radio.Serial;
    
          // The properties, such as Nickname and Callsign, can't be read
          // successfully until you've received a PropertyChanged event
          // for that property... this is done after connect.
          //
          radio.PropertyChanged += radio_PropertyChanged;
          radio.SliceAdded += new Radio.SliceAddedEventHandler(radio_SliceAdded);
          radio.SliceRemoved += new Radio.SliceRemovedEventHandler(radio_SliceRemoved);
          radio.VoltsDataReady += new Radio.MeterDataReadyEventHandler(_thisRadio_VoltsDataReady);
          radio.PATempDataReady += new Radio.MeterDataReadyEventHandler(_thisRadio_PATempDataReady);
          radio.GUIClientAdded += new Radio.GUIClientAddedEventHandler(_thisRadio_GUIClientAdded);
          radio.GUIClientUpdated += new Radio.GUIClientUpdatedEventHandler(_thisRadio_GUIClientUpdated);
          radio.GUIClientRemoved += new Radio.GUIClientRemovedEventHandler(_thisRadio_GUIClientRemoved);
    
          radio.Connect();
    
          form1.txtDebug.Text += "Status: " + radio.Status + NL;
          form1.txtDebug.Text += _thisRadio.GuiClientHosts + NL;
    
          ParseConfig();
        }
    


    In the radio_SliceAdded routine, I add an event handler for each of the two slices if and when they are added.

        private void radio_SliceAdded(Slice slice)
        {
          //txtDebug.Text = "Slice: " + slice.Index.ToString() + "  " + slice.ClientHandle.ToString() + NL;
           
          if (slice.Index == 0)
          {
            slice0 = slice;
            slice0.PropertyChanged += new PropertyChangedEventHandler(slice0_PropertyChanged);
            //txtDebug.Text += slice0.ClientHandle.ToString() + NL;
    
            lblSlice0.Text = "Slice " + slice0.Letter + "  ";
    
            txtFreq0.Text = slice0.Freq.ToString("0.000 000");
            txtFreq0.BackColor = Color.White;
    <snip>
    

    So, after all that... I now can control slices 0 and 1 independently of which is "active". I also get the same slice designator (A, B, etc.) that SmartSDR is using, even if there are two different SmartSDR instances controlling the radio (MultiFlex).

    Hopefully that helps...

  • n8wrl
    n8wrl Member ✭✭

    Thanks Len, I figured it out.

    First, I have to tell the radio to Connect()

    Then, I have to wait for slices to be added - monitor the event.

    THEN everything is there for the mucking!!

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Exactly! I'm glad that you got it sorted out.

  • n8wrl
    n8wrl Member ✭✭

    Yup. Even after a connect(), the slices aren't there. You get around that with your call to TimeIt.Start(). After at least one slice is "added", ActiveSlice works.

    It was similar with the API waiting for radios to be discovered. After Init() I had to monitor the radio-added event.

    I did this in a loop, sleeping between checks, with an overall timeout to throw an exception with the bad news if radios or slices never appear.

    Thanks again for the help.

    73

    -Brian n8wrl

  • Alan
    Alan Member ✭✭✭✭

    All

    The slice subscription, status message gives the slice VFO frequency and a flag, for that slice, for both "active" slice and "tx" slice. The flag is 0/1, for each slice: active/tx combination. You need to use some logic to know what type of slice frequency you see in the slice status message, for each open slice.

    Alan. WA9WUD

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Hi Brian, TimeIt is only there for the speech synthesizer. A blind ham in the Boulder Amateur Radio Club asked me if any of my programming was blind-friendly, so I added a way to speak the frequency. I had to time things so that I did not get queued up calls to the synthesizer (crude, but effective...). If this is fixing your code, then it is just luck of the draw that the timing worked out OK.

    I think that the actual thing going on is that you can't do any slice things until the radio has communicated that it has a slice, and then that the specific data that you want has been presented (Freq, Mute, etc.). I don't try to do a main loop waiting for data. It is just events firing and being serviced based on returned data.

    In the case of slices, first you need the slice added event. Within that event handler, you create another event handler for the slice that was just added (e.g. slice0_PropertyChanged). Now within slice0_PropertyChanged, you can query e.PropertyName to see if the data that you want is there. Depending on the event returned parameters (e.PropertyName), I am populating the frequency text box for slice 0 or changing the slice 0 mute status on the display. There are many others, I just chose these two to provide an example.

        private void slice0_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
          CheckOOB(0, slice0.Freq, slice0.DemodMode, slice0.IsTransmitSlice);
    
          if (e.PropertyName.Equals("Freq"))
          {
            txtFreq0.Text = slice0.Freq.ToString("0.000 000");
            txtFreq0.BackColor = Color.White;
    <snip>
          }
    
          if (e.PropertyName.Equals("Mute"))
           {
               if(slice0 == null)
               {
                   return;
               }
    
               if (slice0.Mute)
               {
                   btnMute0.Text = "Un-Mute";
               }
               else if (!slice0.Mute)
               {
                   btnMute0.Text = "Mute";
               }
           }
    <snip>
    

    The event handlers do the waiting for you. When one fires off, you can act on the data received.

    It is a different way to look at things, but once you see how the data arrives asynchronously via the event handler, you will have a good grip on it.

    I hope that explanation made sense.

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Hi Alan, Brian is using FlexLib with C#, not the raw text-based API. He is not dealing with subscriptions directly. FlexLib is doing all the parsing of status, response and message data and returning that data in a different way than what you are used to with the plain API.

  • Alan
    Alan Member ✭✭✭✭

    Len, thanks. As you can guess, I am not familiar with the .NET.

    Alan. WA9WUD

  • n8wrl
    n8wrl Member ✭✭

    Thank you both!

    Len, yes I am very used to event-based programming, much prefer it. I think your timer "fixes it" for you. In effect, I am doing the same thing with my short sleeps to allow events to percolate.

    I'm off and running now. Thanks all!

    73

    -Brian n8wrl

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Hi Brian, I'm glad you have it working! One thing that you will want to do as you get more into it is to be sure to bind your non-GUI client to the GUI Client (typically SmartSDR or a Maestro). This way, things like CW speed and RF power will work properly. Otherwise, you will always get 30 WPM for CW Speed and some value for RF power that does not follow SmartSDR. Check out the _thisRadio_GUIClientUpdated routine in my code where I grab the client handle and use it to get the ClientID and from there do the bind:

    _thisRadio.BindGUIClient(ClientID);

    This is a very misunderstood requirement, as quite a few things will work fine without it. A soon as you try to display or change one of the items (like CW Speed or RF Power), you will get the wrong value unless you have done the bind. Note that this only holds true if you are operating as a non-GUI Client (i.e. no panadapter via UDP). If you wanted to be a GUI client, you would need to execute the command to do that instead of the bind to GUI command (haven't researched that yet...).

    As far as the TimeIt timer goes, it was only added recently for the speech. The rest of the code has been working very well without it. I have no main loop since it is a C# forms-based program. It is purely event-driven. In your case, the short sleeps in your main loop just let the event queue drain so that you can poll the data.

    Be sure to share your Flex Control interface progress. It is always interesting and informative to see how people are interfacing additional gear to their Flex radios.

  • n8wrl
    n8wrl Member ✭✭

    Len this was very helpful! Thank you.

    As a proof-of-concept, I can toggle code in and out to control overall radio volume or active slice frequency by turning the FlexControl.

    And now I have a new problem I have to research.

    As you turn the FlexControl that SmartSDR knows about, the "VFO box" moves across the Panadapter display as the frequency changes up or down. As the "VFO box" gets close to the edges, the whole Panadapter scrolls horizontally as frequency continues to change.

    My code is simply adjusting the frequency of the slice. As a result, the "VFO box" moves across the Panadapter, but when it gets to the edge it "snaps" back to the middle. The frequency changed correctly, and the frequency legend along the bottom of the Panadapter has changed too, so the display is correct. It just doesn't smoothly scroll left or right.

    Gotta figure that out...

    73!

    -Brian n8wrl

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Hi Brian, yes, I have that same behavior. If I tune using the mouse wheel, the panadapter scrolls left or right when I get close to the edge. If I tune using the knob on my TeensyMaestro, it snaps to center when approaching the edge of the panadapter.

    I am OK with that behavior, so I never tried to change it, but now you have me curious as to what to do to make it a smooth scroll instead of a re-center.

    If I figure it out, I will post back here.

  • n8wrl
    n8wrl Member ✭✭

    Slice.AutoPan isn't the fix!

    While it seems like a display-preference the user of SmartSDR might have set, it doesn't make sense that the two devices would behave differently manipulating the frequency of the same radio. I much prefer the smooth scrolling, and since SmartSDR knows what to do with a FlexControl, there must be a way to do it!

    73

    -Brian n8wrl

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Well, I found this, but so far I can't make it work:

    slice0.Panadapter.AutoCenter = false;

    It compiles fine, but I am missing something...

  • n8wrl
    n8wrl Member ✭✭

    Looking at the source for Panadapter I see that AutoCenter defaults to false, just like slice.AutoPan defaults to true, so setting either one to what "seems sane" won't change anything.

    This feels like a GUI preference thing - something that the user of the GUI should be in control of, and if that's the case then we shouldn't be able to alter it via the API.

    Except that it behaves differently depending on the source of the frequency change!

    Still digging...

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    If you change slice.AutoPan to false, you get the expected behavior; the flag scrolls off the page and you get the slice indicator that shows you an arrow pointing the direction of the flag.

    Changing slice.panadapter.AutoCenter has no effect. I think the issue is the fact that AutoPan is a slice parameter and AutoCenter is a panadapter parameter.

    I don't know how to use that info yet (or if it is even valid...).

    Maybe the thing to do is to do a WireShark capture of what happens when the mouse wheel moves the frequency near the pan edge. Perhaps SmartSDR is setting the panadapter center frequency by the step size when it is near the pan edge.

  • n8wrl
    n8wrl Member ✭✭

    Good sleuthing.

    I have shelved this for now. I am working on a generic way to configure the buttons and knob for various function-assignments. Right now I have a working hard-coded config that, turning the knob adjusts the volume, if button1 was pressed (and so the LED is on) turning the knob adjusts the volume.

    Yeah, SmartSDR and other tools do this and much more now. The big payoff is going to be supporting multiple FlexControls, which I'm doing.

    :)

    The behavior of the GUI for frequency changes surely feels user-configurable to me, not something we should be adjusting via the API. What if I don't want it to auto-center? Etc.

    73

    -Brian n8wrl

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Well in all this playing around, I finally figured out what I was doing wrong with the PropertyChangedEventHandler...

     //radio.PropertyChanged += radio_PropertyChanged;
       radio.PropertyChanged += new PropertyChangedEventHandler(radio_PropertyChanged);
    

    I originally just appended the routine name (commented out, above). It worked, but I wondered why that one statement looked different from the rest.

    I found the actual event handler and used it along with the new keyword, just like the rest of them. Works great. I wonder what bad things were going on in the background that this fixes...

  • n8wrl
    n8wrl Member ✭✭

    Over the years c# added features and more concise ways of doing things. Your new statement is an example of the old school delegate syntax which will still work... Your first, commented out statement is the simpler way to do the same thing.

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Ahhh... Thanks for that Brian. My PL/I, Fortran, COBOL accent is showing through...

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Hi Brian, I think figured out how SmartSDR scrolls the panadapter when you approach the edge of the screen while tuning with the mouse wheel.

    When you get within a certain distance from the edge of the panadapter display, it scrolls the display by the amount of the step size. That prevents the flag from going past the edge of the screen which either triggers a re-center event, or simply moves off screen (depending on slice.AutoPan).

    These are the messages returned by SmartSDR in the case of going past the threshold distance from the edge. This shows that SmartSDR resets the pan center frequency to avoid the re-centering.

    S6BDA2526|display pan 0x40000000 center=7.019190
    S6BDA2526|display waterfall 0x42000000 center=7.019190
    


    So here is the code that I tested it out on, and it works better than I expected! This routine moves the frequency down one step when the frequency down button (on my form) for slice 0 is clicked:

         private void btnFreq0Dn_Click(object sender, EventArgs e)
        {
          double Freq = 0;
    
          if (ConvertFreqToDouble(txtFreq0.Text, ref Freq))
          {
            if(slice0.Freq <= (slice0.Panadapter.CenterFreq - (slice0.Panadapter.Bandwidth / 2)) + ((slice0.Panadapter.Bandwidth / 2) * .21))
            {
              slice0.Panadapter.CenterFreq -= slice0.TuneStep / 1_000_000.0;
            }
             
            Freq -= (TuneStep0 / 1_000_000.0);
    
            slice0.Freq = Freq;
            Freq = 0;
            txtFreq0.BackColor = Color.White;
    
            //form1.txtDebug.Text += slice0.Panadapter.CenterFreq + NL;
            //form1.txtDebug.Text += slice0.TuneStep / 1_000_000.0 + NL;
            //form1.txtDebug.Text += slice0.Panadapter.Bandwidth + NL;
          }
    

    The complicated if statement finds the left edge of the panadapter, then finds the threshold point (21%) to the right of that, then compares to see if the threshold has been passed and scrolls the center freq by the amount of the step. I should probably add step size to the calc in the if statement, as big steps could probably scoot you past the edge of the screen.

    The test for the right panadapter edge would be similar, but with addition and subtraction reversed for some of the variables.

    I think this little proof of concept will help you to obtain what you are trying to achieve, but let me know if it does not.

    Thanks for asking about this Brian, I learned a few new things! I may even apply this to my TeensyMaestro with a menu item that allows choosing the desired action - scroll or re-center when tuning past panadapter edges.

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    OK, here is the final version with TuneStep taken into account. This allowed me to change the percentage to 20% of the distance from the edge plus StepSize as the threshold point for scrolling the panadapter.

    I also removed the global variable TuneStep0 and just use slice0.TuneStep. The divide by a million converts TuneStep in Hz to MHz so that it can be added to or subtracted from Freq which is in MHz.

        private void btnFreq0Dn_Click(object sender, EventArgs e)
        {
          double Freq = 0;
    
          if (ConvertFreqToDouble(txtFreq0.Text, ref Freq))
          {
            if(slice0.Freq <= (slice0.Panadapter.CenterFreq - (slice0.Panadapter.Bandwidth / 2)) + ((slice0.Panadapter.Bandwidth / 2) * .20) + (slice0.TuneStep / 1_000_000.0))
            {
              slice0.Panadapter.CenterFreq -= slice0.TuneStep / 1_000_000.0;
            }
             
            Freq -= (slice0.TuneStep / 1_000_000.0);
            slice0.Freq = Freq;
            Freq = 0;
            txtFreq0.BackColor = Color.White;
    
            //form1.txtDebug.Text += slice0.Panadapter.CenterFreq + NL;
            //form1.txtDebug.Text += slice0.TuneStep / 1_000_000.0 + NL;
            //form1.txtDebug.Text += slice0.Panadapter.Bandwidth + NL;
          }
          else
          {
            txtFreq0.BackColor = Color.Red;
          }
        }
    

    Thanks again for posting here Brian, this was a fun exercise and definitely improved my understanding of the Flex API, FlexLib and C#.

  • n8wrl
    n8wrl Member ✭✭

    This is good stuff Ken! Your logic makes perfect sense and the code goes a long way towards helping us all understand it.

    Please understand that I am very grateful by the effort you put into this, and I in no way mean to criticize your work. There is quite a bit of "code smell" here to me. In this case, a magic number - 21. While your 21% logic makes perfect sense, it is based on knowledge of the UI and of the width of the display. Someone else may have a wider display and/or Flex may come out with a different VFO-"flag" UI for SmartSDR in the future. Heck, your teensy-maestro probably has different proportions. All of which makes 21% a guess this time.

    I think there is something more fundamental going on here. "Separation of concerns" means decisions about scrolling and such is made by the UI (and the users of that UI), not by something like my code sending the radio messages. I am going to send a note to Flex support to see if they have any ideas.

    Thanks again Len! Nice sleuthing!

    73

    -Brian n8wrl

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Yep, 20% was determined empirically based on approximately where I saw the API switch over to centering the display. The width of the display does not factor in. It is solely based on the displayed bandwidth of the panadapter. That way, the behavior will be the same for any sized display.

    This code does indeed depend on knowledge of the SmartSDR UI. It is specifically being used to drive that UI. If this were a GUI client (like SmartSDR), then that would be different.

    Flex may tell you what their formula is for calculating the change-over point, but I would be surprised. It feels like slice.Panadapter.AutoCenter was created for this purpose, so maybe Flex can enlighten us on this construct and how to use it.

    The TeensyMaestro is a non-GUI client and does not display panadapter data, so it is more like what you are doing with your FlexControl.

  • Eric-KE5DTO
    Eric-KE5DTO Administrator, FlexRadio Employee admin

    So here's the story -- if we drive these changes through the radio, the user experience is less than smooth due to the way the commands and status updates interact and the inherent delays getting messages to the radio and back to the client.

    This flow just isn't suitable for smooth GUI operation:

    Slice Tune -> Radio

    Radio adjusts Pan -> Status back to client

    Client reacts to status


    So we end up handling as much of this as we can in the client directly while still supporting some mechanisms in the radio to ensure that tuning from CAT or other 3rd party applications keep the Slice visible in the Panadapter. The AutoPan and AutoCenter properties are the implementation for 3rd party apps.

    I'm happy to share more about how we improved the performance in SmartSDR as needed, but it is similar to what KD0RC showed above -- just adjusting the Panadapter CenterFreq locally as needed to keep the Slice on screen.

  • KD0RC
    KD0RC Member, Super Elmer Moderator

    Thanks for the explanation Eric, I really appreciate it.

  • Alan
    Alan Member ✭✭✭✭

    Erik - Question

    First, thanks for explaining the Flex philosophy on using status messages for client updates.

    Question. You say that Flex uses this technique, so the client updates itself rather than waiting for a status message from Flex Radio.

    For my applications, I have been using the Flex response message (hex zero or OK) as confirmation before I update the local status of the commanded parameter.

    If I understand your explanation, SmartSDR, when making such commands, is doing so "in the blind," not waiting for a return confirmation of the command before updating the local parameter.

    Alan. WA9WUD

  • n8wrl
    n8wrl Member ✭✭

    Okaaay, with Eric's assistance and pure-plagiarism of Len's code, I've put something together that works pretty well. Before I post the whole method, some notes:

    1. This is a class library I'm working on, and the class this method lives in provides simplified access to a Flex Slice object. If you provide that (m_theSlice, in my case) you should be able to use this anywhere.
    2. I like to do math with decimals. I've done a lot of work with catalogs and other financial systems where rounding oddness from the floating point formats is unacceptable. The more manipulation you do, the worse it gets, so I convert to decimal and back to double after all the fiddly stuff.
    3. I'd like this to be a bit "smarter" about bandwidth, the side of the VFO the passband is on, etc. So I may post more later.

    For now, here it is!

            /// <summary>
            /// Set the slice frequency and adjust UI if necessary
            /// </summary>
            private void SetFrequency(decimal newFreqMhz)
            {
                // Very unlikely...
                decimal curFreqMHz = GetFrequencyMHz();
                if (newFreqMhz == curFreqMHz)
                    return;
    
    
                // Get our Panadapter
                var pan = m_theSlice.Panadapter;
                if (null != pan)
                {
                    // With the Panadapter that this Slice is rendered on, decide if it needs to scroll with
                    // the frequency change.
    
    
                    decimal panCenterFreqMhz = (decimal)pan.CenterFreq;
                    decimal panBandwidthMhz = (decimal)pan.Bandwidth;
                    decimal halfPanBandwidthMHz = panBandwidthMhz / 2;
    
    
                    // Is the slice is visible within the panadapter?
                    if (   (curFreqMHz >= panCenterFreqMhz - halfPanBandwidthMHz)
                        && (curFreqMHz <= panCenterFreqMhz + halfPanBandwidthMHz))
                    {
                        // Are we about to push the slice off either end of the panadapter?
    
    
                        // Percentage to apply to panadapter bandwidth to decide if we need to scroll
                        decimal edgePercentage = 0.15m /* % */;  // Scroll if we're within 15% of the edge
                        decimal edgeDeltaMhz = halfPanBandwidthMHz * (1 - edgePercentage * 2);
                        bool scrollPan = false;
    
    
                        // Which direction are we going?
                        if (newFreqMhz < curFreqMHz)
                        {
                            // Down, or to the left
                            scrollPan = (newFreqMhz < (panCenterFreqMhz - edgeDeltaMhz));
                        }
                        else
                        {
                            // Up, or to the right
                            scrollPan = (newFreqMhz > (panCenterFreqMhz + edgeDeltaMhz));
                        }
    
    
                        // Need to scroll?
                        if (scrollPan)
                        {
                            // TODO: Should we restore these after setting slice-freq?
                            pan.AutoCenter = false;
                            m_theSlice.AutoPan = false;
    
    
                            // We want to move the panadapter in the *same* direction the slice frequency
                            // is moving so that the "VFO line" stays on the pan
                            decimal deltaMHz = newFreqMhz - curFreqMHz;
                            pan.CenterFreq += (double)deltaMHz;
                        }
                    }
                }
    
    
                // Now send the new frequency to the slice
                m_theSlice.Freq = (double)newFreqMhz;
            }
    
    
    

    73!

    -Brian n8wrl

Leave a Comment

Rich Text Editor. To edit a paragraph's style, hit tab to get to the paragraph menu. From there you will be able to pick one style. Nothing defaults to paragraph. An inline formatting menu will show up when you select text. Hit tab to get into that menu. Some elements, such as rich link embeds, images, loading indicators, and error messages may get inserted into the editor. You may navigate to these using the arrow keys inside of the editor and delete them with the delete or backspace key.