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 check the Help Center for known solutions.
Need technical support from FlexRadio? It's as simple as Creating a HelpDesk ticket.

Working on a Python API

Mark Erbaugh
Mark Erbaugh Member
edited February 2020 in SmartSDR API
I've had some success in using the  FlexLib API .DLLs with Python 2.7.9 and PythonDotNet (not IronPython), but have come up against two issues:

1) I am trying to set the Panadapter Band property, but it doesn't seem to work.

2) I am trying to write a callback handler for the DataReady event on the IQStream and can't figure out the parameter calling convention in Python. I have successfully written other callback routines, but I think this is the first one that passes and array of data from the C# side. No matter how I declare the Python parameters to the callback, the Python program crashes with a Python exception. It may be that the Python Dot Net add-on is not capable of handling arrays in parameter passing.

I don't think these are issues with the FlexLib API, but rather with Python Dot Net. Hopefully, someone on the forum has some experience with this.

Answers

  • N7BCP
    N7BCP Member ✭✭
    edited May 2019
    Would you be wikmg to share your code? GitHub? I'd like to poke around a bit.
  • Mark Erbaugh
    Mark Erbaugh Member
    edited February 2020
    Here's the code. It's designed to be used interactively from the Python prompt. I use the IDLE development environment which is based on Tcl/Tk. TextOutputWindow.py provides an output window.

    *** API.PY ***

    import clr
    clr.AddReference("FlexLib")

    from Flex.Smoothlake.FlexLib import API
    from TextOutputWindow import TextOutputWindow
    from System.Windows import Size

    # ----------------------------------------
    # ----- API Event Handlers ---------------
    # ----------------------------------------

    def RadioAdded(radio):
        radio.MessageReceived += MessageReceived
        radio.PropertyChanged += PropertyChanged
        radio.IQStreamAdded += IQStreamAdded
        radio.Connect()
        msg_win.add('Radio Added: ' + radio.IP.ToString())
       

    def RadioRemoved(radio):
        msg_win.add('Radio Removed: ' + radio.IP.ToString())

    # ----------------------------------------
    # ----- Radio Event Handlers -------------
    # ----------------------------------------

    def MessageReceived(severity, msg):
        msg_win.add('Message: ' + msg)

    def PropertyChanged(sender, event):
        msg_win.add('Change: %s' % event.PropertyName)

    def IQStreamAdded(stream):
        stream.DataReady = DataReady
        msg_win.add('IQ Stream Added')

    # ----------------------------------------
    # ----- Panadapter Event Handlers --------
    # ----------------------------------------


    def DataReady(stream, data):
        stream.DataReady = None
        print('stream Data')
        
    # ----------------------------------------
    # ----- Utility Routines -----------------
    # ----------------------------------------

    sz = Size(400.0, 100.0)

    def createPA():
        radio = API.RadioList[0]
        pa = radio.CreatePanadapter(sz)
        pa.RequestPanadapterFromRadio()
        pa.Band = "40"
        return pa

    def createIQStream():
        radio = API.RadioList[0]
        iq = radio.CreateIQStream(1)
        iq.RequestIQStreamFromRadio()
        return iq
       


    # ----------------------------------------
    # ----- main -----------------------------
    # ----------------------------------------

       
    if __name__ == '__main__':
        msg_win = TextOutputWindow()
        API.ProgramName = "Python"
        API.RadioAdded += RadioAdded
        API.RadioRemoved += RadioRemoved
        API.Init()

       
       

    *** TextOutputWindow.py ***

    from Tkinter import *
    import ScrolledText
    import threading
    import Queue

    class TextOutputWindow(threading.Thread):
        def __init__(self):
            self.quit = 0
            self.clear = 0
            self.queue = Queue.Queue()
            threading.Thread.__init__(self)
            self.root = Tk()
            self.root.title("FlexRadio Output")
            self.start()
            self.root.after(100, self.periodicCall)

        def run(self):
            self.root.protocol("WM_DELETE_WINDOW", self.quit_cb)
            self.text = ScrolledText.ScrolledText(self.root)
            self.text.pack(expand=YES, fill=BOTH)
            self.root.mainloop()

        def periodicCall(self):
            if self.quit:
                self.root.quit()
            else:
                if self.clear:
                    self.clear = 0
                    self.text.delete('1.0', END)
                while self.queue.qsize():
                    try:
                        msg = self.queue.get()
                        self.text.insert(END, msg)
                        self.text.see(END)
                    except Queuy.Empty:
                        pass
                self.root.after(100, self.periodicCall)

        def quit_cb(self):
           self.root.quit()

        def add(self, message):
            self.queue.put(message + '
    ')






  • N7BCP
    N7BCP Member ✭✭
    edited July 2017
    Thanks Mark!  I downloaded Python.Net from http://sourceforge.net/projects/pythonnet/?source=typ_redirect and had to force npython.exe into 32 bitness (using corflags) as I'm on a 64 bit version of windows 8.1.  Once that was done I hit this issue https://github.com/pythonnet/pythonnet/issues/3 which seems to be an incompatability with .Net 4.5. I'm going to get the python.net source and see if I can compile it as someone has fixed this issue already. 
  • N7BCP
    N7BCP Member ✭✭
    edited July 2017
    pythonnet compiled and your code is running - is this the error you're seeing when you try to hook up the IQ events?

    Unhandled Exception: Python.Runtime.PythonException: TypeError : cannot set event attributes   at Python.Runtime.Dispatcher.Dispatch(ArrayList args)
       at __Flex_Smoothlake_FlexLib_Radio_IQStreamAddedEventHandlerDispatcher.Invoke(IQStream )
       at Flex.Smoothlake.FlexLib.Radio.OnIQStreamAdded(IQStream iq_stream) in d:GitHubFlexWebRemoteExternalFlexLib_v1.3.9FlexLibRadio.cs:line 2768
       at Flex.Smoothlake.FlexLib.IQStream.StatusUpdate(String s) in d:GitHubFlexWebRemoteExternalFlexLib_v1.3.9FlexLibIQStream.cs:line 460
       at Flex.Smoothlake.FlexLib.Radio.ParseStatus(String s) in d:GitHubFlexWebRemoteExternalFlexLib_v1.3.9FlexLibRadio.cs:line 1458
       at Flex.Smoothlake.FlexLib.Radio.ParseRead(String s) in d:GitHubFlexWebRemoteExternalFlexLib_v1.3.9FlexLibRadio.cs:line 952
       at Flex.Smoothlake.FlexLib.Radio.ProcessReadBuffer() in d:GitHubFlexWebRemoteExternalFlexLib_v1.3.9FlexLibRadio.cs:line 922
       at Flex.Smoothlake.FlexLib.Radio.TCPReadCallback(IAsyncResult ar) in d:GitHubFlexWebRemoteExternalFlexLib_v1.3.9FlexLibRadio.cs:line 891
  • Jay Nation
    Jay Nation Member
    edited August 2016
    Larry 

    Why not setup a Community repo on GitHub anyway? Create an Organization and a Team giving anyone with a Verifiable Callsign and/or Community Login Read and Write access
    And see what happens.
    Keep it public and open and just Flow with the Go.
    Beats copy pasting Community posts the Community Search can't always seem to filter out.
     Not sure how to handle Admin though. Don't want it to be ruined by bad planning.

    What do you think?
    Anyone else?
    Post some feedback.

    Jay - NO5J
  • Jay Nation
    Jay Nation Member
    edited August 2016
    Im thinking
    Community-Development for the name of the organization.
    FlexCoders for the team name
    Callsign repos/folders for Sub projects from team members.
    Maybe allow Admin from anyone a chosen few.
    Not sure how we select the chosen few.

     
  • philip.theis
    philip.theis Member
    edited February 2015
    Hi Mark,
    Good deal.  I have my python code running on a BBB and it's switching my bands successfully.  But now I am going back and rewriting it in PHP, I want to do a lot of interfacing with port 80, so I believe that makes sense for me.   It was a blast starting to learn Python.
    73,
    Phil
  • N7BCP
    N7BCP Member ✭✭
    edited February 2020
    Good idea Jay - I ran with it and hope that my deed goes unpunished.  I've avoided using the name Flex Radio so not to confuse people that this repo is managed by Flex Radio.  I'm a novice Python coder and FlexLib user but hope to learn from you all.  The Git repo is here: https://github.com/FlexCommunityDevelopment/FlexPythonAPI and I'm happy to add anyone from this list who would like to contribute.  The repo is public so you can git it without permission.  I've uploaded a modified version of Mark's Python code.  It now supports IQ stream as he wanted (I think) and as well, pan and waterfall streams.  Read the readme in the repo for hints on how to get it running.  It *should* run with any Flex 6000 - I tested with a 6500.  I experienced some strange behavior running this code while SmartSDR was running on the same PC - no doubt because I don't fully understand the API.  Comments and criticisms welcome.
  • Jay Nation
    Jay Nation Member
    edited August 2016
    Larry 

    I am real glad you set us on this path. Having never had Admin or ownership of a repo on GitHub I was reluctant, to jump in and set it up myself without feedback. I'll help out, if you need some spare idle hands, I'm sometimes considered trainable on most things software and know how to find my own answers. And work well in "No training budget" situations. I'm also a big fan of learn by doing. I really appreciate all the individuals doing that already. I hope anyone else that is interested in being part of a "Team" will explore the concept of jumping in too.

    Remember folks it's just software you can't really break anything permanently. You don't even have to take the cover off. Take a backup "have a spare" and tweak it till it works almost the same as it did, "only better".

    Now we have someplace we can look at code, find out what needs to be learned to play with the code, let others see what we try to code, and tell us what we did wrong.

    Who knows, what's on the other side of that mountain, we will if we go and have a look, Thanks Larry somebody had point the way. I'll help carrying the gear.

    Join the team. We might even get colander hats. image 
     
    Lets all GitFlexible!

    Jay - NO5J
     
  • N7BCP
    N7BCP Member ✭✭
    edited November 2016
    you can't really break anything permanently
    I hope that is true - I'm sure I abused my Flex 6500 with some of this code but it didn't brick - yet :)
  • Jay Nation
    Jay Nation Member
    edited August 2016
    If you write code that  forces keydown and disables the front panel reboot button. You might brick it with software. Easier to just use a hammer. Coding Hard!, Hammer Easy! I find uses for bricks too.  

    Jay - NO5J
  • Jay Nation
    Jay Nation Member
    edited February 2020
    Mark or Larry

    Starting from scratch on Win 8.1 64bit. I need to install Python 2.7.9.
    I'm wondering about the 32/64 bit issue for the Python Install though.
    IOW  Install Python 2.7.9.  Which version inflicts the least pain?

    73, Jay - NO5J
  • Mark Erbaugh
    Mark Erbaugh Member
    edited February 2015
    I'm running Wndows 7 64-bit. I installed the 64-bit version of Python. The only issue I've had so far was that the original PythonDotNet project only worked with 32-bit, but I did find a version that worked with 64 bit.
  • Mark Erbaugh
    Mark Erbaugh Member
    edited February 2015
    Larry,

    I think I got your code running.  I run Example.py and see data in the text window. One issue. When I close the text window, it seems to take the radio off-line to where SmartSDR can't see any radios. So far the only thing I've found to fix that is a power cycle.

    Also, I was seeing the error you found. What did you do the get rid of that?

    FWIW, I may not have much time to devote to this project until the weekend, so ignore any silence from me.
  • Jay Nation
    Jay Nation Member
    edited August 2016
    Mark
    Weekends are ok by me. Now retired so every day is saturday and every night feels like .... Oh yeah no more NightLife! Sux to be old! Besides if your busy all week I get more time to learn all the stuff I used to avoid and now wish I'd taken seriously. 

     GitFlexAble

    73, Jay - NO5J

  • Mark Erbaugh
    Mark Erbaugh Member
    edited February 2020
    I have modified Larry's code (his modification of my code) slightly and it seems more stable.

    I added two lines to the bottom of the Quit() function:

        API.RadioAdded -= RadioAdded
        msg_win.quit = 1


    I was having a problem that with the original code, once I quit, I had to power down the radio to get it to respond to the Python code or SmartSDR. It would respond to a ping, but that was all.

    I noticed that the message window showed a Radio Removed message followed by a connecting sequence.  I think what was messing up the radio was that the RadioAdded callback was still alive and it started processing the connection then the app terminated.

    The msg_win.quit = 1 line merely closes the FlexRadio Output window.







  • Jay Nation
    Jay Nation Member
    edited May 2019
    It's probably just a coincidence, in trying to get set up to play with the code, starting out about the time I tried installing  Python.Net, and Python 2.7.9, and shortly after having received an update from Microsoft of .NET Framework 4.5.1 Multi-Targeting Pack. things began to go south. I can't lay the blame on anything. I have things working again, and I'm looking into updateing my backup disc images. Which I have a habit of not doing.image So I'll get caught up with you guys soon I hope.

    Not really all that big of an issue, It's just the Flex-Box. Once i figure out how to set up the file/access permissions I'm going to setup a cron job on one of my linux boxes to rsync an incremental backup image to the TimeMachine disk attached to my wifes Imac. 

    So backup a Window 8.1 box on an Apple with a Linux box. Doesn't that sound like a no-brainer? TimeMachine for Windows how hard can that be to setup? It worked before.

    I just never included the Flex-Box when it arrived.

    73, Jay - NO5J
  • Mark Erbaugh
    Mark Erbaugh Member
    edited February 2020
    Here's my update for Thursday. I've managed to get things sort of working. I can connect to the Flex and create a Panadapter. For some reason, creating a Panadapter automatically creates a slice. My callback that prints "Slice Created" actually gets called 3 times when I call radio.RequestPanafall. If I check SlicesAvailable and PanadaptersAvailable after that, both show 0. If I close the Panadapter, both show 2 (I have a 6300).

    Some times during my testing, when I connect to the radio, it would automatically create a Panadapter as well. I cycled the power and that stopped. I wonder if the automatic creation of Panadapters and Slices has something to do with the Persistence database in the radio?
  • Jay Nation
    Jay Nation Member
    edited May 2019
    This is the sort of information I was hoping to get. Play with the API and Post what you see.

    What?, Why?, and How?, are much more interesting than, When?
     
    73, Jay - NO5J
  • Eric-KE5DTO
    Eric-KE5DTO Administrator, FlexRadio Employee admin
    edited December 2016
    I suspect that the radio is treating the Panadapter creation as a persistence event which triggers a Slice to be created.  Each of these is probably taking it's own radio resource and thus exhausts the total supply (2) on a FLEX-6300.

    We have some work to do to enable better API handling for the Panadapters to prevent this sort of issue.
  • Mark Erbaugh
    Mark Erbaugh Member
    edited February 2015
    If it is an issue with the radio, how does SmartSDR get around it?

    Is there a workaround that will allow FlexLib API to be used with the 6300 and more than one Panadapter and Slice?
  • Eric-KE5DTO
    Eric-KE5DTO Administrator, FlexRadio Employee admin
    edited December 2016
    The way the radio makes decisions about how to use the resources depends on the application name being used.  Today the Panadapters do something different if connected to SmartSDR-Win.  This will obviously need to change to enable more clients to use the display objects.
  • Mark Erbaugh
    Mark Erbaugh Member
    edited February 2015
    Eric,

    Thanks for the info. I learn something new every day! Would it make sense for my Python application to impersonate SmartSDR-Win? Is that even possible?

    73 and have a good weekend,
    Mark
  • James Whiteway
    edited February 2015
    Not sure if this idea would help, but, if you ID the radio first, ( 6300,6500,6700) and then set a maximum number of pans/slices for each radio according to model, then you should be able to query the api for the current number of open pans/slices and keep things under control. That is what I have had in mind since I was not sure if the software on the radio kept track of that and kept an outside application from attempting to open more pans/slices than a particular model of radio supports.
    Just a thought, and if not what you were looking for, sorry!
    james
    WD5GWY

  • Steve-N5AC
    Steve-N5AC Community Manager admin
    edited February 2017
    Things operate differently with SmartSDR on Windows than with other clients because of how the GUI needs to operate.   Now that we have some folks wanting to do the same things and create GUI clients of their own, we discussed this thread and made a change today to support this.  Before calling API.Init() you will want to set IsGUI to true:
    API.ProgramName = "your program";
    API.IsGUI = true;
    API.Init();
    At this time, there will be only a single GUI allowed so you will not be able to run your application along side SmartSDR if you have used IsGUI (there can be only one).  This change has been checked into the v1.4 codebase so it will be available starting with v1.4.
  • James Whiteway
    edited February 2015
    Good to hear Steve! I have been SLOWLY working on a very simple program to control the radio, independent of SmartSDR, but, have had a few issues getting the proper initialization sequence right to set the radio up. This looks like it will make that easier. (once I get all the setting right!)
    james
    WD5GWY
     
  • N7BCP
    N7BCP Member ✭✭
    edited November 2016
    Good catch!
  • N7BCP
    N7BCP Member ✭✭
    edited November 2016
    Mark - I did some research on Fatal Python error: PyImport_GetModuleDict: no module dictionary! and I haven't found a hint at what is causing it.
  • N7BCP
    N7BCP Member ✭✭
    edited November 2016
    Mark - I can confirm the behavior that Eric describes above.  Uppon connecting to a 6500 we have four pans and four slices available as expected.  When ProgramName = 'SmartSDR-Win' and you call Radio.RequestPanfall or Waterfall.RequestWaterfallFromRadio, the radio reports three pans and and three slices available once the pan and slice are created - as expected.  When the API.ProgramName is anything else, there are only two slices and two pans available after initial call to RequestPanfall or RequestWaterfallFromRadio.  It seems that Panadaper.RequestPanadapterFromRadio(), once the panadapter has been added, will cause the pan's DataReady events to fire. Another interesting thing to note is that when ProgramName = 'SmartSDR-Win', when I add a slice the number of available pans does not reduce.  After creating a single pan and adding four slices to it I still had three pans available and I was able to create them albeit with no slices.  When ProgramName is anything else, as I add slices to an existing pan, the number of pans reduces.

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.