psychopy icon indicating copy to clipboard operation
psychopy copied to clipboard

Add EGI NetStation support to PsychoPy Python3, now with NTP support

Open jbteves opened this issue 3 years ago • 23 comments

Hi all, I recently put together a small python package to enable users to interface with EGI-Magstim amplifiers via the NetStation interface. There is a previous implementation in Python2 (egi.py), but this new package is in Python3 and adds NTP support, tested on NIH campus with @pmolfese. We would like for it to be accessible from PsychoPy to make installation for users much much simpler, as right now they have to copy the package into a folder and manually correct some imports. This is obviously not ideal.

I am happy to help integrate or add features if needed! The package in question can be found here: https://github.com/nimh-sfim/egi-pynetstation https://egi-pynetstation.readthedocs.io/en/latest/

jbteves avatar Jan 27 '22 19:01 jbteves

That's great! Yes, I did a little work years ago to try and support the old package but I don't have an EGI system to test on so I was limited what I could do and haven't touched it recently

One thing that should be trivial is for me to add this to the Standalone distribution of PsychoPy so that your users can access it from their code easily.

Questions:

  1. is this replacing fully the functionality of the old package? Is there any reason to keep any mention of the old one or should we be deprecating that completely and trying to steer people to yours?
  2. Do you have plans/thoughts on further integration (e.g. with Builder Components or similar?). We're literally just a couple of days from releasing 2022.1.0 so it won't be for this release but maybe other users would like to see this support for the summer release

Well done getting it working and helping the EGI users out there!

peircej avatar Jan 28 '22 16:01 peircej

Awesome!

  1. No, we don't implement the "simple" clock, though I believe that the current one just doesn't work as it's Python2.
  2. No, not at this time, I'm more or less moving on to other projects but will revisit if there's demand down the line.

Thanks! Looking forward to seeing this make its way in.

jbteves avatar Jan 28 '22 16:01 jbteves

I should rephrase: we do implement a simple clock, but since that's not our use case, we have not tested it as extensively as the NTP clock. If users are interested in testing for me, I'm happy to make changes.

jbteves avatar Jan 28 '22 16:01 jbteves

@peircej just wanted to follow up and see what I can do to help with this.

jbteves avatar Feb 23 '22 14:02 jbteves

Dear developers, I recently had contact with the developers from the egi_pynetstation plugin since I was having issues getting it to run in PsychoPy. The current workaround involves "For reference the egi-pynetstation folder should exist in the python folder of psychopy. On my computer it's: /Applications/PsychoPy.app/Contents/Resources/lib/python3.6/egi-pynetstation. So you would add ntplib to that folder with the rest of egi-pynetstation." which is not that user friendly in my opinion. Would it be possible for you to make it so that everyone can easily install the plugin without people having to resort to this workaround? I do have access to a NetStation amplifier so I can be of assistance if you need me to test anything. Best, Kenneth

KTZ228 avatar Mar 18 '22 08:03 KTZ228

Hi there,

We've been working with PsychoPy and egi netstation and the issue our end appears (I think) to be coming from the NetStation module of egi_pynetstation, rather than the PsychoPy code. When we install ntplib independantly we get this error:

0.7667 WARNING Monitor specification not found. Creating a temporary one... File "C:\Users\work\Desktop\Drones\Drones_EEG_White_Mar23_withtrigs_newEGI_lastrun.py", line 116, in ns.begin_rec() File "C:\Users\work\Desktop\Drones\egi_pynetstation[NetStation.py](http://netstation.py/)", line 102, in wrapper func(*args, **kwargs) File "C:\Users\work\Desktop\Drones\egi_pynetstation[NetStation.py](http://netstation.py/)", line 192, in begin_rec self.ntpsync() File "C:\Users\work\Desktop\Drones\egi_pynetstation[NetStation.py](http://netstation.py/)", line 102, in wrapper func(*args, **kwargs) File "C:\Users\work\Desktop\Drones\egi_pynetstation[NetStation.py](http://netstation.py/)", line 152, in ntpsync response = c.request(self._ntp_ip, version=3) File "C:\Users\work\Desktop\Drones[ntplib.py](http://ntplib.py/)", line 311, in request response_packet, src_addr = s.recvfrom(256) ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host

If anyone has any suggestions they would be very much appreciated!

Thanks!

RebeccaHirst avatar Mar 24 '22 16:03 RebeccaHirst

Hi Rebecca, this means that for some reason NetStation is forcibly closing a connection after it's been initiated. I am not sure why this would be the case... Any ideas @pmolfese ?

jbteves avatar Mar 24 '22 16:03 jbteves

Hi @RebeccaHirst - happy to take a look at it. But I'll need a copy of your experiment and some other info:

  1. What version of Net Station are you running?
  2. Which amplifier are you using? 300 or 400 series?
  3. Are you using the most recent version of PsychoPy

This plus a copy of your experiment (feel free to pull out stimuli or swap in dummy images/sounds) should help me identify the issue. I have some time this afternoon or plenty of time next week.

-Peter

p.s. we're still hopeful to get it officially into PsychoPy distro, but we're also working on an install guide.

pmolfese avatar Mar 24 '22 16:03 pmolfese

@RebeccaHirst - also can you get the Amplifier to play nicely with the example.py distributed with the package?

pmolfese avatar Mar 24 '22 16:03 pmolfese

Hi @pmolfese and @jbteves thanks so much for responding so quickly ! I'll get the info on the Amplifier asap, for the other questions:

  1. What version of NetStaton are you using?: We downloaded this repository (last week so most recent version I think) and imported this directly into our experiment. We also initially got an ntplib missing error so we also downloaded ntplib.py here.
  2. Will get the amplifier type asap (we're working remotely with a lab)
  3. Are you using the most recent version of PsychoPy - yes 2022.1.1 - but we're not using the currently packaged egi module as suspected it needs an update (we're actually part of the PsychoPy team so we were figuring this out before integrating, so it's great to hear that you're working towards integration! if you're working on an install guide we'de be happy to help with docs if that would be helpful?).

You can access our psychopy file here: https://drive.google.com/file/d/14fz6a-GS64yc8ry6FIZRMTV16WSzqwku/view?usp=sharing we just put the egi_pynetstation folder and ntplib.py in the same location as the .psyexp file so they could be accessed by the experiment.

For the example.py - I hadn't noticed that until exploring these conversations a bit more - it's on our list to test next! will keep you posted.

Thanks for your help!

RebeccaHirst avatar Mar 24 '22 17:03 RebeccaHirst

In addition to the amplifier series, we'll need the version of Net Station running on their data acquisition computer (separate from our python module that has the name embedded). It's likely a 5.0 or higher. Just helps us debug on our side.

pmolfese avatar Mar 24 '22 17:03 pmolfese

@RebeccaHirst I noticed that you linked the index page; you can see a more nicely formatted version of our documentation here. Perhaps we should package the example as a python function @pmolfese, that way we could have people just use their pip-installed package...

jbteves avatar Mar 24 '22 17:03 jbteves

OK I have updates:

Can confirm its a 300 amp system and the version of the netstation software is Net Station Acquisition 5.4.2 (r29917).

Thanks @jbteves for the link to additional docs, do let me know if there is anything else I can do our side to help.

Appreciate your help with debugging!

Becca

RebeccaHirst avatar Mar 25 '22 14:03 RebeccaHirst

Hi @RebeccaHirst - thanks for the update! That explains some of the quirks I saw in the PsychoPy experiment. Just to verify, can you find out the version of MacOS that the acquisition computer is using? The reason is that the NTP server for 300 series amps is the Mac! And to make it even more nuanced, the Mac has to be version 10.12 or older. See this correspondence with EGI:

On the 300, the Mac is the NTP server. Theoretically, you should just be able to use the 
mac’s IP, provided the mac has been configured to act as a NTP server, we have a script 
for that. The OS does have to be 10.12 or older, apple removed that ability in 10.13. 

Thanks for uploading the experiment. I couldn't get it to run on my PsychoPy install, but dug through the code and also mocked up a few other experiments.

After chatting with Josh and some bug checks on our setup last night, I think the issue is that you have the ns.send_event code set to "each frame" when it should be "begin routine" (trial level). Every frame would be every refresh of the screen and at least on my display is a LOT of events per each presentation. I think the number of events is overwhelming the setup. Can you make that change and report back?

-Peter

pmolfese avatar Mar 25 '22 17:03 pmolfese

To clarify: a connection reset typically occurs when a server receives packets it can't process. What may be happening is that you send packets so quickly that when the next one arrives, it hasn't finished with the previous one, can't process the packets, and responds with a reset.

jbteves avatar Mar 25 '22 17:03 jbteves

Hi @pmolfese and @jbteves - sorry for our delay - we (me and @kimDundas) needed to wait for our collaborator to get back in the lab and try the example.py script. Updates below:

  1. " I think the issue is that you have the ns.send_event code set to "each frame" when it should be "begin routine" (trial level)." - yes of course - I see I may have made an error on those codes, but I am not sure that was the problem in this case, because we actually got the error before the "Training_Instruc" routine (where the trigger is sent in the Begin Routine tab - perhaps there might be an issue with trying to send a trigger in the first routine though? if that is not long enough to establish a connection?

  2. We tried running the example.py script and here are some approaches and error messages we encountered (hopefully they are helpful!) :

  3. running the script from https://github.com/nimh-sfim/egi-pynetstation/blob/main/example.py

########## Running: C:\Users\work\Desktop\Drones\testscriptapril7.py ###########
usage: testscriptapril7.py [-h] {local,amp}
testscriptapril7.py: error: the following arguments are required: mode
################# Experiment ended with exit code 2 [pid:2528] #################

These errors were occurring as I was trying to figure out how to set the arguments for the run mode:
########## Running: C:\Users\work\Desktop\Drones\testscriptapril7.py ###########
Traceback (most recent call last):
  File "C:\Users\work\Desktop\Drones\testscriptapril7.py", line 61, in <module>
    main()
  File "C:\Users\work\Desktop\Drones\testscriptapril7.py", line 20, in main
    p.add_argument('mode','amp')
  File "C:\Users\work\Documents\Paradigms\Psychopy 2022\lib\argparse.py", line 1354, in add_argument
    kwargs = self._get_optional_kwargs(*args, **kwargs)
  File "C:\Users\work\Documents\Paradigms\Psychopy 2022\lib\argparse.py", line 1489, in _get_optional_kwargs
    raise ValueError(msg % args)
################# Experiment ended with exit code 1 [pid:2796] #################

########## Running: C:\Users\work\Desktop\Drones\testscriptapril7.py ###########
usage: testscriptapril7.py [-h] {amp}
################# Experiment ended with exit code 2 [pid:1268] #################


########## Running: C:\Users\work\Desktop\Drones\testscriptapril7.py ###########
usage: testscriptapril7.py [-h] {a,m,p}
################# Experiment ended with exit code 2 [pid:3064] #################

We then changed the amp ID to 10.0.0.51, ns IP 10.0.0.42 and this didn't return the original error but did return just an exit code:

Traceback (most recent call last):
################# Experiment ended with exit code 1 [pid:8024] #################

Thanks again for your help here - hopefully these errors are useful!

RebeccaHirst avatar Apr 08 '22 15:04 RebeccaHirst

Hi @RebeccaHirst, you should run the example script like this:

python example.py amp

if you have the amp connected. The IP addresses look correct.

jbteves avatar Apr 08 '22 15:04 jbteves

@RebeccaHirst, just to add on - since you have a 300 series amplifier: the IP for the amplifier should match the IP for Net Station. That's because in newer (400-series), the amplifier serves as a NTP server. Whereas with the 300-series, the computer serves as the NTP server.

pmolfese avatar Apr 08 '22 15:04 pmolfese

For info, we just tested with a user (remotely) and we successfully got triggers sent.

The first stage of adding support is to provide the package as part of the standard PsychoPy install so it can be used within Code Components. That's done in #4544 and will be included in the next release (at least the major summer release, maybe not intervening bug-fix releases).

The next stage will be deciding what/if Component(s) should be added to Builder to make this easier. For that we'll need to talk a bit about what use-cases are likely to occur. For comparison, Emotiv support includes a pair of Components, one for starting/stopping recording, and another for sending markers/triggers to the package. It looks from your code like that's pretty much what a netstation component should do as well, right? There's not much else involved? For another comparison BrainProducts have an API that allows all manner of things like initiating different modes and querying the hardware. image image

peircej avatar Apr 28 '22 16:04 peircej

That's great that it's working for more users and ready to be merged in! It'll definitely make it easier for new users once they don't have to add it to their paths, etc.

The main widgets that might be useful:

  • Setup/Connect (supply relevant ports and IPs: IP_ns, IP_amp, port)
  • Start/Begin Recording
  • Send Event (perhaps an option to do this on flip) - Events can contain: event_type, time of event (defaults to now), duration, label, description, and a data dict
  • End/Stop Recording
  • Disconnect
  • Resync (with the caveat that we haven't found this terribly useful so far, but EGI made it, so @jbteves added it)

pmolfese avatar Apr 28 '22 20:04 pmolfese

For Send Event, I would recommend putting a tooltip to not do this on every routine, as the amplifier will not be able to serve the requests fast enough and ultimately cause a crash in the Netstation object.

For Resync, I would recommend putting a tooltip indicating that the authors implemented it for completion but it seems to actually worsen timings...

For Send Event, please consider that the data dict is restricted as follows: Keys must be 4 characters, and values must be a float, integer, or string of <256 characters.

jbteves avatar Apr 28 '22 20:04 jbteves

SendEvent works for every routine. Just not "every frame". But in theory the widget would only allow one.

Sorry we mix terminology since so many softwares name things differently... 🧐

pmolfese avatar Apr 28 '22 20:04 pmolfese

I think we can now close this issue as the package is included in PsychoPy.

pmolfese avatar Jul 05 '22 18:07 pmolfese