Giter VIP home page Giter VIP logo

Comments (65)

seanmcleod avatar seanmcleod commented on May 24, 2024 2

@agodemar I've just started looking into this. I'm just using telnet at the moment for initial testing.

On my initial test it looks like there are some potential issues in FGInputSocket::Read(). For example the connected status is true even before there is an actual connection.

I'll be away for the next 2 days walking up Table Mountain, so will only have an update on Fri.

from jsbsim.

Teeg93 avatar Teeg93 commented on May 24, 2024 2

I've just worked through this problem - I found that the issue was indeed with the Rascal model. The static / dynamic friction coefficients in the tail landing gear are set 10x higher than the front landing gear. This was causing the tail to act as a pivot point (aircraft couldn't overcome the static friction), hence the random crazy spiraling motion. Reduced them by 10x and all seems to be working now.

Edit:
I was also having issues with the position of the aircraft in ArduPilot. Turns out the initialization file sets the geocentric latitude, whereas we want (in virtually all applications) to use the geodetic latitude. I changed the reset script created in SIM_JSBSim.cpp to add type="geodetic" to the latitude initialization.

from jsbsim.

bcoconni avatar bcoconni commented on May 24, 2024 1

@WillemEerland

Either that or the default changed from UDP to TCP

I dug the commit history and found that:

@seanmcleod

We should make the string comparison for protocol type case-insensitive in JSBSim.

Given our inconsistent history, I agree πŸ˜ƒ

@WillemEerland

Not sure what the policy is on leaving the issue open, but I'm hoping to resolve this issue within the next two weeks,

No rush. The issue can stay open as long as it needs to be resolved. We shall ping there from time to time to make sure that it is still alive πŸ˜‰

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024 1

@WillemEerland @bcoconni I've submitted a pull request to make the check for "UDP" case insensitive, see - #80

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024 1

OK - new approach. I've replaced the README.md with some points how to get the latest version of JSBSim working with ArduPilot/ArduPlane.

I will push for a modification in the documentation at

http://ardupilot.org/dev/docs/setting-up-sitl-on-linux.html

to include a link to

https://github.com/WillemEerland/ardupilot/tree/jsbsim

and see if there is any response from the ArduPilot community, as it stands though, I think this issue is closed from the JSBSim side.

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024 1

Yes! Thanks to Tridge the modifications have been implemented and the documentation updated
( see http://ardupilot.org/dev/docs/setting-up-sitl-on-linux.html )

However, in order to help you debug what's going on from the JSBSim side, perhaps you can add "--debug" when you run sim_vehicle.py. This should give you (and us) additional information.

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

Glancing through the SIM_JSBSim.cpp I don't see anything obvious that isn't supported by the current JSBSim code. It looks like it's pretty much using the FlightGear protocol via a local socket.

So unless someone else replies with specific experience using the latest JSBSim source with Ardupilot I'd suggest simply building the latest JSBSim source and trying it out and if something doesn't work then post what the issue is.

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

Thanks for your reply. At least now I know the functionality should work.

I've been making quite a bit of progress today, and will post more when I run into specific issues, as you suggested. I was merely hoping someone had done this already and could save me the effort by sharing their experiences.

Once I've got it working I'll write up a tutorial to go in the documentation.

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

And fixed... The problem seems to be that the latest JSBSim only recognises "UDP" in the XML files, when it sees "udp" it switches to TCP.

Anyways, I modified the ArduPilot code slightly:

  • removing the check for "(ArduPilot)" in the version
  • changing "udp" to "UDP"

https://github.com/WillemEerland/ardupilot/tree/sitl

Perhaps we can assume that if someone writes "udp" in the XML, they want a UDP port, and not TCP?

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

Good to know that no changes were required in the latest version of JSBSim. We should make the string comparison for protocol type case-insensitive in JSBSim.

Does that mean the older fork of JSBSim that was used for SITL with Ardupilot was case-insentitive with regards to the protocol type?

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

Either that or the default changed from UDP to TCP

While this "fixes" the connection part, it is not functioning as expected, and am trying to fix this on the ArduPilot side:

ArduPilot/ardupilot#8710

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

I'm going to follow #74 and see if I manage to implement ArduPilot & JSBSim using this modification. Not sure what the policy is on leaving the issue open, but I'm hoping to resolve this issue within the next two weeks, especially since @agodemar already has a working version* waiting to be merged.

  • although still lacking the lock-step mechanism used for speedup and debugging purposes.

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

Great! To that end, some additional information (and videos):

@bcoconni Replacing "step" with "iterate 1" is exactly what I tried after looking at @tridge his modification, see

ArduPilot/ardupilot@master...WillemEerland:sitl

then when I run ArduPlane (with the UDP fix, and replacing "step" with "iterate 1") the following happens

  • the ground speed starts at a really high initial value (perhaps because the simulation runs from the moment it starts and doesn't wait for ArduPilot, if so, this could be related to issue #74 )
  • the heading is going crazy, I'm not sure why this happens

http://tinypic.com/r/15ea4oo/9 (video startup)

then when I start the "mode auto", it starts to rotate really fast (probably because of the faulty heading signal)

http://tinypic.com/r/2eow1zs/9 (video "mode auto")

Also, I realise this may happen due to a change in the communication protocol? The main file that communicates between ArduPilot and JSBSim is located here:

https://github.com/WillemEerland/ardupilot/blob/sitl/libraries/SITL/SIM_JSBSim.cpp

I think this reads the socket using the FGNetFDM class. However, it is unclear to me how this happens, the JSBSim code is definitely easier to read. Even hard-coding the heading to 0 doesn’t seem to keep the program from going crazy.

Any ideas on what to try next?

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

@seanmcleod Thank you. This fixes establishing the connection part.

In the meantime, I have also tried to run JSBSim from #81 in combination with a modified version of ArduPilot ( https://github.com/WillemEerland/ardupilot/tree/LatestJSBSim ) where I modified the following:

  • removed the version check
  • replace "step" by "iterate 1" (2x)
  • included "action=BLOCKING_INPUT" in the FDM output XML

But the behaviour is the same, where the heading indication is going haywire, nor am I able to control the speed of the simulation, e.g. pause or fast-forward.

I'm going to wait till #81 is checked and implemented, then I'll come back to this.

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

The BLOCKING_INPUT needs to be set for the input element not the output element.

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

@WillemEerland I have merged the #81 submitted by @bcoconni

You can now test it and give us feedback if you wish

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

I have not yet been able to successfully use this new feature. I tried to the following modification to the ArduPilot code

ArduPilot/ardupilot@master...WillemEerland:updateJSBSim

after which it connects to both ports (input and output) successfully:

Creating input TCP socket on port 5505
Successfully bound to TCP input socket on port 5505

Creating UDP socket on port 5504
Successfully connected to socket for output ...

I have also tried to remove the check for "JSBSim Execution beginning", and force it to send send_servos(), however, then the code appears to be stuck in the loop where ArduPilot is sending the input signals ( found in send_servos() ), and there are no signals being received:

    while (sock_fgfdm.recv(&fdm, sizeof(fdm), 100) != sizeof(fdm)) {
        send_servos(input);
        check_stdout();
    }

@agodemar JSBSim is working in combination with the WiReS server?

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

@WillemEerland Nope. It is stuck in the FGInputSocket::Read function here:

void FGInputSocket::Read(bool Holding)
{
  // ...
  if (BlockingInput)
    socket->WaitUntilReadable(); // block until a transmission is received
  data = socket->Receive(); // read data
  // ...
}

Looks like the FGfdmSocket::WaitUntilReadable is waiting forever:

void FGfdmSocket::WaitUntilReadable(void)
{
  fd_set fds;
  FD_ZERO(&fds);
  FD_SET(sckt_in, &fds);
  select(sckt_in+1, &fds, NULL, NULL, NULL);
}

I couldn't figure out the way to break this infinite loop without shutting down the socket.

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@agodemar do you start the Winres server before you run Jsbsim?

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

@seanmcleod Yes I do. It receives data from the JSBSim output socket on one port, then it starts sending data back on the JSBSim input port with a synchronous (blocking) write. The first transfer from WinRes server to JSBSim occurs but the WaitUntilReadable operation gets stuck, unless I shutdown the socket from the sender side.

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

Would this be because there is no return function as is found in @tridge his modified version?

void FGfdmSocket::Wait(void)
{
    fd_set fds;
    if (sckt_in <= 0) {
        return;
    }
    FD_ZERO(&fds);
    FD_SET(sckt_in, &fds);
    select(sckt_in+1, &fds, NULL, NULL, NULL);
}

I have tried to insert if (sckt_in <= 0) { return; } and while it establishes the connection, I still have the problem with the sensor data (although I realize this may be an issue on the ArduPilot side).

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

@WillemEerland thanks for the hint. I have double checked this: In my case the problem is that select waits forever due timeout. So introduced the possibility to specify a timeout value in microsecs:

<input type="SOCKET" port="1139" 
  action="BLOCKING_INPUT" 
  timeout_microsec="10000" 
  block_command="Block_Socket" 
  unblock_command="Unblock_Socket" />

See this commit to my fork

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@agodemar but if select doesn't return then it implies that the system doesn't think there is any readable data available for the input socket. So adding a timeout which allows JSBSim execution to continue then undoes the lock-step processing between JSBSim and the external server that you're trying to achieve.

So we need to figure out why there isn't any readable data available which is causing select to block indefinitely. Maybe add the socket to the errorfds set as well, in case there is some socket error that is occurring. If select then returns because of an error with the socket then we can try and figure out what the error is what is causing the socket error.

I did take a quick look the other day at your simple WinResServer in order to use it to debug, but I noticed it uses boost which I don't currently have installed. So when I get a bit more time I'll either install boost or write a simple server using the basic socket APIs.

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

And just to confirm @agodemar you are testing with a TCP input socket right and @WillemEerland you're testing with a UDP input socket right?

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

@seanmcleod Right: I'm using TCP.

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

@seanmcleod @WillemEerland what about using the function poll?

It seems that select behaves differently sometimes on Windows and Linux.

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@agodemar I suggest we first figure out what is going wrong before moving away from select.

In the example link you sent about select behaving differently the poster had a bug in terms of what he passed for the first argument to select. On Windows it ignores the 1st argument since Windows is able to work out the equivalent info that the 1st argument supplies by looking at the file descriptor sets passed in for the subsequent arguments.

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

@seanmcleod I'm using TCP as well. I have tried changing the protocol to UDP, but it still shows up as a TCP socket at the initialization.

I'll attempt to setup a test using Beej's guide. This will teach me a bit more about these internet sockets, and hopefully help in debugging JSBSim (for now and in the future).

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@WillemEerland it looks like if you specify an input element with type empty or set to SOCKET (case sensitive) then you get an FGInputSocket which only supports TCP.

There is an input socket which supports UDP but it's input protocol is very different. So they're both hard-coded to support either TCP or UDP, the FGUDPInputSocket also has a hard-coded port number of 5139.

  if (type.empty() || type == "SOCKET") {
    Input = new FGInputSocket(FDMExec);
  } else if (type == "QTJSBSIM") {
    Input = new FGUDPInputSocket(FDMExec);
  }

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@agodemar @WillemEerland I've just done a bit more testing and so far it appears to be working as expected.

So I added the following line of debug output in FGInputSocket::Read() to print out when any data is received via the input socket and to display the simulation frame at the time of receiving the socket data.

  if (BlockingInput)
    socket->WaitUntilReadable(); // block until a transmission is received
  data = socket->Receive(); // read data

  if(data.size() > 0)
    cout << "FGInputSocket::Read - Sim Frame - " << this->FDMExec->GetFrame() << " Data: " << data  << endl;

I then ran JSBSim using the c1723.xml script after adding a blocking input element.

<input type="SOCKET" port="1139" action="BLOCKING_INPUT"/>  

After starting JSBSim I then simply used the telnet client to connect to the input socket:

C:\Users\Sean>telnet localhost 1139

After connecting to the JSBSim socket I then simply typed in a character at a time (`abcdefghi'), the telnet client sends a packet after each character typed.

Connected to JSBSim server
                          JSBSim> abcdefghi

And I saw the following output from JSBSim:

Takeoff report for Left Main Gear (Liftoff at time: 28.091667 seconds)
  Distance traveled:                1020.279065 ft,     310.981059 meters
  Distance traveled (over 50'):     1433.475302 ft,     436.923272 meters
  [Altitude (ASL): 54.713622 ft. / 16.676712 m  | Temperature: 58.804883 F / 14.891602 C]
  [Velocity (KCAS): 55.957937]
FGInputSocket::Read - Sim Frame - 3526 Data: a
FGInputSocket::Read - Sim Frame - 3527 Data: b
FGInputSocket::Read - Sim Frame - 3528 Data: c
FGInputSocket::Read - Sim Frame - 3529 Data: d
FGInputSocket::Read - Sim Frame - 3530 Data: e
FGInputSocket::Read - Sim Frame - 3531 Data: f
FGInputSocket::Read - Sim Frame - 3532 Data: g
FGInputSocket::Read - Sim Frame - 3533 Data: h
FGInputSocket::Read - Sim Frame - 3534 Data: i

Which shows that JSBSim only advanced the simulation 1 simulation frame at a time in-between receiving each character I typed into the telnet client, i.e. in lock step and where there are multiple seconds between me typing each character.

The reason that the starting simulation frame is 3526 before receiving my first character is that I only ran the telnet command roughly 30s after I started the JSBSim execution. So until my telnet connection JSBSim had no incoming TCP connection and so the simulation ran along, but once I connected it then only advanced in lock step to my character input.

So at least in my test setup as just described JSBSim is performing as expected.

@agodemar can you put the same debug output into your build and run it with your simplified WinRes server and report your results?

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

@seanmcleod
immagine
It works when I use telnet for a Windows cmd shell. It works also if I use telnet from a WSL bash shell:
immagine

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@agodemar as an aside I couldn't find a way to put the default Windows telnet client into line mode, i.e. to only send a data packet after you hit return as opposed to for every character typed. The advantage of the line mode option is you can then easily test out commands like get {property name} etc. from the telnet client.

So with the debug code in your build of JSBSim what do you see when you run it with your basic/test version of your WinRes server?

Does it never receive an input packet from the WinRes server, or does it receive a couple and then hang waiting for input?

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

@seanmcleod First step: If I send a string in one shot then I close the socket, socket->Receive() gets the string and detects a closed socket:

immagine

This is done by running this simple program. Here are the basic operations:

std::string raw_ip_address = "127.0.0.1";
asio::io_service ios;
// Endpoint outbound connection
asio::ip::tcp::endpoint
  ep_outbound(asio::ip::address::from_string(raw_ip_address), output_port_num);
// Allocating and opening the socket
asio::ip::tcp::socket sock_outbound(ios, ep_outbound.protocol());
sock_outbound.connect(ep_outbound);
std::cout << "Socket for outbound data connected" << std::endl;
// Allocating and filling the buffer for output
std::string message_outbound = ">>>>>> abcdefghi <<<<<<";
// Write the buffer content to outbound socket
writeToSocket(sock_outbound, message_outbound);

where

void writeToSocket(asio::ip::tcp::socket &sock, std::string buf)
{
  std::size_t total_bytes_written = 0;
  // Run writing loop until all data is written to the socket.
  while (total_bytes_written != buf.length())
  {
    total_bytes_written += sock.write_some(
      asio::buffer(buf.c_str() + total_bytes_written, buf.length() - total_bytes_written));
  }
  std::cout << "Total bytes written " << total_bytes_written << std::endl;
}

I'll try next an asynchronous write to the JSBSim server. I suspect that the issue with select comes out right before the instruction socket->Receive() in FGInputSocket::Read. The call to socket->WaitUntilReadable() might hang forever if the socket is not closed or shut down. This latter case is undesirable because I want to keep a socket open in my app for outbound data for the whole duration of a JSBSim run.

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@agodemar I don't quite follow. Are you saying in your first comment that if you don't close the socket after writing then the data doesn't get to JSBSim?

As we've seen with our telnet tests you can keep the socket connected and send multiple packets through to JSBSim which it receives individually and advances 1 sim frame at a time.

So my guess is you have some buffering going on within your boost based network writes where the data isn't being transmitted until the buffer fills up or you explicitly tell it to flush.

In fact taking a quick look at the boost documentation for write_some:

"The write_some operation may not transmit all of the data to the peer. Consider using the write function if you need to ensure that all data is written before the blocking operation completes."

At the moment I'm inclined to believe that the issue isn't with the select side but rather on the boost sending side.

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

So my guess is you have some buffering going on within your boost based network writes where the data isn't being transmitted until the buffer fills up or you explicitly tell it to flush.

@seanmcleod thanks for the hint, I'll investigate.

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@agodemar you may also want to look into enabling the TCP_NODELAY socket option.

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

@seanmcleod Update:

This is what I have now:

  1. From my external application MinimalClientServer I connect successfully with JSBSim listening on port 1139.
  2. Right after connection, the message "Connected to JSBSim server" is received ("JSBSim> " is ignored).
  3. Send a get command to JSBSim and receive response.
  4. close the socket.
std::string raw_ip_address = "127.0.0.1";
SyncTCPClient client(raw_ip_address, output_port_num);
std::string response;
		
// Sync connect.
std::cout << "Sending connect request to the server... " << std::endl;
client.connect();
// receive JSBSim welcome
std::cout << "Receive response: " + client.receiveResponse() << std::endl;
// send command/receive response
std::cout << "Sending command: " + command << std::endl;
response = client.sendCommand(command);
std::cout << "Response received: " << response << std::endl;
// Close the connection and free resources.
client.close();

Full code of MinimalClientServer.C here

Here is my WSL bash window where I've run more times my app with the following command line:

MinimalClientServer -o 1139 -j "get position/h-agl-ft"

immagine

Here is part of JSBSim log:

$> .\JSBSim.exe --script=scripts/C1723.xml

immagine

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@agodemar have you had a chance to look into the possible buffering happening with the boost classes/methods you're using?

I grabbed some minimal sample socket code that uses the socket API directly without any libraries like boost etc. to see whether I could reproduce any sort of buffering issues and whether socket options like TCP_NODELAY are needed or not.

Here is the code. It basically sits in a loop sending a 1-byte packet with a configurable sleep between each send.

#ifndef UNICODE
#define UNICODE
#endif
#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>

// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")

#define DEFAULT_PORT 1139

int main()
{
  // Declare and initialize variables.
  int iResult;
  WSADATA wsaData;

  SOCKET ConnectSocket = INVALID_SOCKET;
  struct sockaddr_in clientService;

  // Initialize Winsock
  iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
  if (iResult != NO_ERROR) {
    wprintf(L"WSAStartup failed with error: %d\n", iResult);
    return 1;
  }

  // Create a SOCKET for connecting to server
  ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (ConnectSocket == INVALID_SOCKET) {
    wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
    WSACleanup();
    return 1;
  }

  // The sockaddr_in structure specifies the address family,
  // IP address, and port of the server to be connected to.
  clientService.sin_family = AF_INET;
  clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
  clientService.sin_port = htons(DEFAULT_PORT);

  // Connect to server.
  iResult = connect(ConnectSocket, (SOCKADDR*) &clientService, sizeof(clientService));
  if (iResult == SOCKET_ERROR) {
    wprintf(L"connect failed with error: %d\n", WSAGetLastError());
    closesocket(ConnectSocket);
    WSACleanup();
    return 1;
  }

  // Send data
  DWORD sleepTime = 0;
  char *sendbuf = "abcdefghijklmnopqrstuvwxyz";
  int i = 0;
  while (true)
  {
    // Send an initial buffer
    iResult = send(ConnectSocket, &sendbuf[i], 1, 0);
    if (i++ >= strlen(sendbuf))
      i = 0;
    if (iResult == SOCKET_ERROR) {
      wprintf(L"send failed with error: %d\n", WSAGetLastError());
      closesocket(ConnectSocket);
      WSACleanup();
      return 1;
    }

    printf("Bytes Sent: %d\n", iResult);

    Sleep(sleepTime);
  }

  // shutdown the connection since no more data will be sent
  iResult = shutdown(ConnectSocket, SD_SEND);
  if (iResult == SOCKET_ERROR) {
    wprintf(L"shutdown failed with error: %d\n", WSAGetLastError());
    closesocket(ConnectSocket);
    WSACleanup();
    return 1;
  }

  // close the socket
  iResult = closesocket(ConnectSocket);
  if (iResult == SOCKET_ERROR) {
    wprintf(L"close failed with error: %d\n", WSAGetLastError());
    WSACleanup();
    return 1;
  }

  WSACleanup();
  return 0;
}

And here are the results with a Sleep(10) - 10 milliseconds between each send:

FGInputSocket::Read - Sim Frame - 353 Data: m
FGInputSocket::Read - Sim Frame - 354 Data: n
FGInputSocket::Read - Sim Frame - 355 Data: o
FGInputSocket::Read - Sim Frame - 356 Data: p
FGInputSocket::Read - Sim Frame - 357 Data: q
FGInputSocket::Read - Sim Frame - 358 Data: r
FGInputSocket::Read - Sim Frame - 359 Data: s
FGInputSocket::Read - Sim Frame - 360 Data: t

Begin roll (Event 1) executed at time: 3.008333
    Cal. airspeed (kts):   = 0.103979
    Altitude (AGL, ft):    = 4.312505
    Latitude (geod, deg):  = 29.759390
    Altitude (geod, ft):   = 17180.648270
    Throttle pos:          = 1.000000
    Mixture pos:           = 0.999480
    Weight (lbs):          = 2479.983615
    Propeller RPM:         = 2166.835695

FGInputSocket::Read - Sim Frame - 361 Data: uvwxyz
FGInputSocket::Read - Sim Frame - 362 Data:
FGInputSocket::Read - Sim Frame - 363 Data: a
FGInputSocket::Read - Sim Frame - 364 Data: b
FGInputSocket::Read - Sim Frame - 365 Data: c
FGInputSocket::Read - Sim Frame - 366 Data: d
FGInputSocket::Read - Sim Frame - 367 Data: e
FGInputSocket::Read - Sim Frame - 368 Data: fgh
FGInputSocket::Read - Sim Frame - 369 Data: i

So a couple of things. JSBSim never hangs waiting for data. 99% of the time it receives a single byte (the blank Data: is because I also include the null terminator as a single byte), but every now and then it receives more than 1 byte, e.g. Sim Frame - 361 Data: uvwxyz after JSBSim had been busy for a while printing out event data etc.

And now with a Sleep(0), i.e. it basically gives up a time slice to the OS scheduler.

FGInputSocket::Read - Sim Frame - 3024 Data: wxyz abcdefghijklmnopqrstuvwxyz abcdefghi
FGInputSocket::Read - Sim Frame - 3025 Data: jklmnopqrstuvwxyz abcdefghijklmnopqrs
FGInputSocket::Read - Sim Frame - 3026 Data: tuvwxyz abcdefghijkl
FGInputSocket::Read - Sim Frame - 3027 Data: mnopqrstuvwxyz
FGInputSocket::Read - Sim Frame - 3028 Data:  abcdefghijklmn
FGInputSocket::Read - Sim Frame - 3029 Data: opqrstuvwxyz ab
FGInputSocket::Read - Sim Frame - 3030 Data: cdefghijklmnopqrst
FGInputSocket::Read - Sim Frame - 3031 Data: uvwxyz abcdefg
FGInputSocket::Read - Sim Frame - 3032 Data: hijklmnopqrstuvwxyz abcdefghijk
FGInputSocket::Read - Sim Frame - 3033 Data: lmnopqrstuvwxyz abcdefghi

So in this case again no hang by JSBSim, but on average the sender is sending faster than JSBSim is processing the simulation loop so on each input read it receives 20-30 bytes.

And lastly with no sleep (I just commented the Sleep() call out:

FGInputSocket::Read - Sim Frame - 1597 Data: tuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghijklmno
FGInputSocket::Read - Sim Frame - 1598 Data: pqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefg
FGInputSocket::Read - Sim Frame - 1599 Data: hijklmnopqrstuvwxyz abcdefghijklmnopqrstu
FGInputSocket::Read - Sim Frame - 1600 Data: vwxyz abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrst
FGInputSocket::Read - Sim Frame - 1601 Data: uvwxyz abcdefghijklmnopqrstuvwxyz
FGInputSocket::Read - Sim Frame - 1602 Data: abcdefghijklmnopqrstuvwxyz abcdefghijklm
FGInputSocket::Read - Sim Frame - 1603 Data: nopqrstuvwxyz abcdefghijklmnopqrstuv
FGInputSocket::Read - Sim Frame - 1604 Data: wxyz abcdefghijklmnopqrstuvwxyz abcdefghi
FGInputSocket::Read - Sim Frame - 1605 Data: jklmnopqrstuvwxyz abcdefghijklmnopqrstuvw
FGInputSocket::Read - Sim Frame - 1606 Data: xyz abcdefghijklmnopqrstuvwxyz abcde
FGInputSocket::Read - Sim Frame - 1607 Data: fghijklmnopqrstuvwxyz abcdefghijklmnopqrs

Much the same as the Sleep(10), no JSBSim hang in WaitUntilReadable() but on average more data received per simulation loop.

As I mentioned in none of these cases did I enable any specific socket options like TCP_NODELAY etc. and all the tests ran without JSBSim hanging.

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@agodemar I had started typing up my response above much earlier, then had some dinner, finished the response and so only saw your response after I submitted mine.

What happens if you use your code and sit in a loop like I did above sending either a bogus 1 byte message like I did or sending a valid command like your get position/h-agl-ft? Does JSBSim continue to execute a single simulation frame after each command you send, or does it hang in WaitForReadable()?

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

@seanmcleod after I send my first command get position/h-agl-ft, I get the correct reply. Then I parse the reply, e.g. position/h-agl-ft = 2292.03, and send a second command set position/h-agl-ft 3292.03. At this point JSBSim hangs in WaitForReadable()

SyncTCPClient client(raw_ip_address, output_port_num);
std::string response;
std::string command = "get position/h-agl-ft";

// Sync connect.
std::cout << "Sending connect request to the server... " << std::endl;
client.connect();
// receive JSBSim welcome
std::cout << "Receive response: " + client.receiveResponse() << std::endl;
// send command/receive response
std::cout << "Sending command: " + command << std::endl;
response = client.sendCommand(command);
std::cout << "Response received: " << response << std::endl;
double h_agl_ft;
if (wires::get_double_response(response, h_agl_ft))
{
	std::cout << "Double value: " << h_agl_ft << std::endl;
	std::string command2 = "set position/h-agl-ft " + std::to_string(h_agl_ft + 1000.0);
	std::string response2;
	response2 = client.sendCommand(command2);
	std::cout << "Response received: " << response2 << std::endl;
}
else
	std::cout << "Unable to parse double value" << std::endl;
// Close the connection and free resources.
client.close();

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

@seanmcleod it looks like it receives the set command but afterwards hangs in WaitForReadable() with select giving a readable result, i.e. as if the buffer is not flushed.

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

Hmm, @agodemar I still don't see a hang/block in WaitForReadable(). I modified my basic test program to instead of simply sending out a 1 byte packet to read the output sent from JSBSim and to send the following set position/h-agl-ft 2503.45\r\n in a loop, i.e.

  char recvbuf[512];
  while (true)
  {
    // Receive from JSBSim
    memset(recvbuf, 0, sizeof(recvbuf));
    iResult = recv(ConnectSocket, recvbuf, sizeof(recvbuf)-1, 0);
    if (iResult > 0)
      printf("Received: %s\n", recvbuf);
    else if (iResult == 0)
    {
      wprintf(L"Connection closed");
      break;
    }
    else
    {
      wprintf(L"recv failed: %d\n", WSAGetLastError());
      break;
    }

    // Send to JSBSim
    char *sendbuf = "set position/h-agl-ft 2503.45\r\n";
    iResult = send(ConnectSocket, sendbuf, strlen(sendbuf), 0);
    if (iResult == SOCKET_ERROR) {
      wprintf(L"send failed with error: %d\n", WSAGetLastError());
      closesocket(ConnectSocket);
      WSACleanup();
      return 1;
    }

    printf("Bytes Sent: %d\n", iResult);
  }

And it runs without any hangs, here is the output from JSBSim:

FGInputSocket::Read - Sim Frame - 1898 Data: set position/h-agl-ft 2503.45

FGInputSocket::Read - Sim Frame - 1899 Data: set position/h-agl-ft 2503.45

FGInputSocket::Read - Sim Frame - 1900 Data: set position/h-agl-ft 2503.45

FGInputSocket::Read - Sim Frame - 1901 Data: set position/h-agl-ft 2503.45

And the output from my test program:

Received: JSBSim>
Bytes Sent: 31
Received: JSBSim>
Bytes Sent: 31
Received: JSBSim>
Bytes Sent: 31
Received: JSBSim>
Bytes Sent: 31
Received: JSBSim>
Bytes Sent: 31

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@agodemar I managed to find a way to hang/deadlock things. Basically it boils down to how you terminate the command string you send to JSBSim. I noticed that I'm terminating my command with \r\n since I noticed the code in FGInputSocket.cpp looks for that terminator:

    // parse lines
    while (1) {
      string_start = data.find_first_not_of("\r\n", start);
      if (string_start == string::npos) break;
      string_end = data.find_first_of("\r\n", string_start);
      if (string_end == string::npos) break;
      line = data.substr(string_start, string_end-string_start);
      if (line.size() == 0) break;

      // now parse individual line
      vector <string> tokens = split(line,' ');

And sure enough if I remove the \r\n terminator from my string then the 2 programs (JSBSim and my test TCP client program) hang/deadlock after sending the 1st command.

Basically when FGInputSocket::Read() doesn't find the terminator it exits the Read() without writing any response back to the client. So my test client after sending the 1st command now hangs forever in recv waiting for a reply from JSBSim which never comes and JSBSim loops round and calls WaitForReadable() waiting for data to be written to it which never comes.

If you are only getting a reponse to your 1st command, the get ... are you possibly terminating this command but not your set ... command?

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@agodemar have you managed to get this working if you explicitly terminate all your command strings?

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

Alright, I had an epiphany. It seems to me, what you guys are doing are querying/sending to the same TCP port, right?

Because ArduPilot doesn't use this, it just needed the "step" functionality, which can easily be replaced by "iterate 1" (besides the initial connection problems of UDP/udp). I've reread the comment from @tridge in #81 , and it's literally what he's saying. However...

Combining this with the problems of the Rascal stated in #90 and I'm suspecting that the (old) model of the Rascal is simply not flying very well in the latest version of JSBSim. These suspicions are confirmed via some initial tests, where have a ground trim and add full thrust, causing a -70 deg angle for beta. This would explain the behaviour I've seen in the ArduPilot SITL simulation, where the Rascal starts to rotate like crazy.

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@WillemEerland yes and no in terms of using the same TCP port. In @agodemar's original setup he had an <output> element sending position data (lat, lon, alt) to his WinRes server. The WinRes server would then connect to the socket defined in an <input> element sending it wind velocity for each timestep. So 2 separate TCP sockets.

However when @agodemar ran into issues trying to get this to work I simplified things in terms of trying to debug/reproduce his blocking/hanging issues by only creating an input socket via the <input> element. In this case using telnet to send commands/data to JSBSim and reading replies and/or requesting data via a get command.

And yes it would make it easier to test and debug whether this lockstep functionality is working with ArduPilot's SITL if you test it with a flight model that is known to be stable with JSBSim πŸ˜‰.

from jsbsim.

agodemar avatar agodemar commented on May 24, 2024

UPDATE
@seanmcleod Moving forward towards a reasonable behaviour.
My test does rely on the asio::read_until function that I wrap into my class SyncTCPClient as follows:

std::string SyncTCPClient::receiveResponse()
{
	asio::streambuf buf;
	std::cout << "[receiveResponse - read_until newline] ..." << std::endl;
	asio::read_until(m_sock, buf, '\n');
	std::cout << "[receiveResponse - read_until newline] done." << std::endl;
	std::istream input(&buf);
	std::string response;
	std::getline(input, response);
	return response;
}

The above function reads JSBSim reply once a command has been sent on the JSBSim server input channel. This is implemented as follows:

void SyncTCPClient::sendRequest(const std::string &request)
{
	std::ostringstream oss;
	oss << request << std::flush; // <======== FLUSH
	asio::write(m_sock, asio::buffer(oss.str()));
}
std::string SyncTCPClient::sendCommand(std::string command)
{
	sendRequest(command + "\r\n"); // <====== \r\n
	return receiveResponse();
};

The function SyncTCPClient::sendCommand can be used to pass a set <property> command to JSBSim. In fact, the string sent with sendCommand is digested by JSBSim's FGInputSocket::Read. This latter function parses the string and checks the validity of the command, eg.:

  • get <property>
  • set <property> <value>
  • resume
  • iterate <int>
  • quit
  • info
  • help

and finally calls Reply that sends back through the socket a message, a string, to the client. In my test the function SyncTCPClient::receiveResponse expects a reply that has to be a non-empty string terminating with a \n character (see how asio::read_until is used above). That's the point where my test got stuck: in the actual JSBSim behaviour, when a set command is successful, the JSBSim server replies with an empty string followed by the prompt JSBSim> . No new line character. See FGInputSocket.cpp, line 173.

I modified that line as

socket->Reply("set successful\n");

which I think is a reasonable reply, that allows my test to smoothly read until that \n and to run until completion.

So, thats how I send two commands synchronously from my client: first a get command then a set command:

SyncTCPClient client(raw_ip_address, output_port_num);
std::string response;
std::string command = "get position/h-agl-ft";

// Sync connect.
std::cout << "Sending connect request to the server... " << std::endl;
client.connect();
// receive JSBSim welcome
std::cout << "Receive response: " + client.receiveResponse() << std::endl;
// send command/receive response
std::cout << "Sending command: " + command << std::endl;
response = client.sendCommand(command);
std::cout << "Response received: " << response << std::endl;
double h_agl_ft;
if (wires::get_double_response(response, h_agl_ft))
{
	std::cout << "Double value: " << h_agl_ft << std::endl;
	std::string command2 = "set position/h-agl-ft " + std::to_string(h_agl_ft + 1000.0);
	std::string response2;
	response2 = client.sendCommand(command2);
	std::cout << "Response received: " << response2 << std::endl;
}
else
	std::cout << "Unable to parse double value" << std::endl;
// Close the connection and free resources.
client.close();

And these are the two logs:

MinimalClientServer.C

$./MinimalClientServer -o 1139
Input port number set to: 1138
Output port number set to: 1139
Command: 'get position/h-agl-ft' to JSBSim
Sending connect request to the server...
[receiveResponse - read_until newline] ...
[receiveResponse - read_until newline] done.
Receive response: Connected to JSBSim server
Sending command: get position/h-agl-ft
[receiveResponse - read_until newline] ...
[receiveResponse - read_until newline] done.
Response received: position/h-agl-ft =        4.305
Double value: 4.305
Sending command: set position/h-agl-ft 1004.305000
[receiveResponse - read_until newline] ...
[receiveResponse - read_until newline] done.
Response received: set successful
Closing client... done.

JSBSim

$JSBSim.exe --script=scripts/c1723.xml
...
Winsock DLL loaded ...
Creating input TCP socket on port 1139
Successfully bound to TCP input socket on port 1139
...
[FGfdmSocket::WaitUntilReadable] readable
FGInputSocket::Read - Sim Frame - 1 Data: get position/h-agl-ft
...
[FGfdmSocket::WaitUntilReadable] readable
FGInputSocket::Read - Sim Frame - 2 Data: set position/h-agl-ft 1004.305000
...
[FGfdmSocket::WaitUntilReadable] readable
Socket Closed. back to listening
...

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@WillemEerland have you managed to find a usable aircraft model to use for testing the latest JSBSim with these changes with Ardupilot's SITL?

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

@seanmcleod I have not. I have tried the updated flight model of the Rascal from #90 but was not successful in even starting the simulation (I haven't verified, however this is an indication for me that it is unsuccessful in a ground trim). Instead of going through endless iterations of trying and updating parameters, I have tried to generate a "new generic" aircraft using aeromatic++, and verified that this model is able to trim on the ground and in cruise. I then ran into problems of powering the aircraft with an electric engine and eventually gave up. I have already tried replacing it with the c172 (and flying that with the ArduPilot autopilot), however, the ArduPilot script uses a static script to start the engines, and doesn't uses the fuel mixing. Simply replacing it with an equally powerful electric engine wouldn't work either (your previous post regarding the -1, 0, 1 and which engines it starts actually clarified some things for me why it wouldn't just trim during cruise when I set the engines to be running at the start).

If you (or anyone else) can point to a general "small" aircraft, similar to the Rascal, I'd be very to happy to see if it works. I do feel the communication between ArduPilot (actually ArduPlane) and JSBSim is working well, it's just that JSBSim's inner workings have changed quite a bit since 2010, and therefore the original Rascal aircraft model doesn't work any more.

On the original Rascal aircraft model: Using ONLY JSBSim + a simple script, I'm able to ground trim the aircraft, however, when I increase the thrust, it shows similar unstable behaviour. Hence I concluded it's more an aircraft model problem, not just the ArduPilot communication.

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

@TugGoat That is great news! I was really struggling with the aircraft model itself. Would you be willing to post your updated Rascal model? I will contribute next Sunday to finally tackling this problem, it should definitely be possible to fly around the ArduPilot autopilot with the latest JSBSim core.

Edit: Or if it's really just this small change, well, I'll figure it out myself. Feel a bit stupid if it's really just this though.

from jsbsim.

Teeg93 avatar Teeg93 commented on May 24, 2024

@WillemEerland It was not intuitive to debug at all. I guess JSBSim must have updated the way aircraft deal with ground reactions. I only modified the Rascal.xml file https://github.com/TugGoat/Rascal

from jsbsim.

bcoconni avatar bcoconni commented on May 24, 2024

I guess JSBSim must have updated the way aircraft deal with ground reactions.

This is correct. The ground reactions has been modified to use a much more stable algorithm than it used to. It is based on the paper from Erin Catto "Iterative Dynamics with Temporal Coherence". The paper exposes how Lagrange multipliers and the projected Gauss Seidel algorithm can be used to determine friction forces.

The change has been implemented during the summer 2010. So if you were using a version of JSBSim released before July 2010, you were indeed using a different ground reaction code.

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

Brilliant. I've got the latest ArduPilot (ArduPlane autopilot) running with the latest JSBSim using @TugGoat 's updated Rascal model.

I'll write up the documentation so this can be replicated by others.

from jsbsim.

seanmcleod avatar seanmcleod commented on May 24, 2024

@WillemEerland once you've written up the documentation post a link here in case anyone comes across this issue while searching on JSBSim and Ardupilot etc. and then I'll close the issue.

from jsbsim.

WillemEerland avatar WillemEerland commented on May 24, 2024

@tridge We ( big thanks to @TugGoat ) resolved the issue of using the latest JSBSim for the ArduPilot SITL simulation. The modifications to ardupilot are found at:

https://github.com/WillemEerland/ardupilot/tree/jsbsim

I'm hoping you can advice on how best to proceed, we need a simultaneous update to the code and wiki (using JSBSim). Updating one and not the other may confuse the users. Since you've got the older version of JSBSim on your repository, I thought you would be the best to contact. Perhaps you can fork the latest version of JSBSim so you can have more control over the updates (the current stable version may become unstable in the future).

http://ardupilot.org/dev/docs/setting-up-sitl-on-linux.html (using JSBSim)

Thanks!

from jsbsim.

josheeg avatar josheeg commented on May 24, 2024

The documentation for setting up a SITL simulation using ArduPilot (ArduPlane) and JSBSim, found at

http://ardupilot.org/dev/docs/setting-up-sitl-on-linux.html#setting-up-sitl-on-linux

points to the following version of JSBSim:

www.github.com/tridge/jsbsim (updated last Feb 2016)

and only has minor modifications (includes a stepping function) to

www.github.com/arktools/jsbsim (updated last Dec 2012)

Any advice on how to use the latest JSBSim to use with ArduPilot? Any advice is appreciated.

edit: and a quick link to the file that "communicates" with JSBSim

https://github.com/ArduPilot/ardupilot/blob/master/libraries/SITL/SIM_JSBSim.cpp

THey updated their docs yesterday
http://ardupilot.org/dev/docs/setting-up-sitl-on-linux.html
I falllowed thsi tutorial after getting the scripts from vm sitl example.
I got the rasscal in jsbsim to takeoff and fly and tried other aircraft thowse didnt work in the same area as the Rascal folder. Also Engine folder and content requirements met for engine etc.
screenshot from 2018-12-20 10-21-19

from jsbsim.

josheeg avatar josheeg commented on May 24, 2024

Neat it compiled SITL in debug configuration. I wondered what that option would do.
I now see a slightly more verbose output.

I found if I enter arm throttle after mode auto this error will pop up. apm: prearm: ahrs not healthy it takes some time to setup the virtual controller.

I gave both the Rascal and the Boeing314 a minute after mode auto to then enter arm throttle.
I also made sure after drawing a box mission that the first waypoint was set to takeoff.

The Rascal took off whale the Boeing314 did not move.

from jsbsim.

professorfabioandrade avatar professorfabioandrade commented on May 24, 2024

I've just worked through this problem - I found that the issue was indeed with the Rascal model. The static / dynamic friction coefficients in the tail landing gear are set 10x higher than the front landing gear. This was causing the tail to act as a pivot point (aircraft couldn't overcome the static friction), hence the random crazy spiraling motion. Reduced them by 10x and all seems to be working now.

Edit:
I was also having issues with the position of the aircraft in ArduPilot. Turns out the initialization file sets the geocentric latitude, whereas we want (in virtually all applications) to use the geodetic latitude. I changed the reset script created in SIM_JSBSim.cpp to add type="geodetic" to the latitude initialization.

Thanks man! I was also having issues with that and fixed it by deleting type="geodetic" in the SIM_JSBSim.cpp file.

from jsbsim.

josheeg avatar josheeg commented on May 24, 2024

I've just worked through this problem - I found that the issue was indeed with the Rascal model. The static / dynamic friction coefficients in the tail landing gear are set 10x higher than the front landing gear. This was causing the tail to act as a pivot point (aircraft couldn't overcome the static friction), hence the random crazy spiraling motion. Reduced them by 10x and all seems to be working now.
Edit:
I was also having issues with the position of the aircraft in ArduPilot. Turns out the initialization file sets the geocentric latitude, whereas we want (in virtually all applications) to use the geodetic latitude. I changed the reset script created in SIM_JSBSim.cpp to add type="geodetic" to the latitude initialization.

Thanks man! I was also having issues with that and fixed it by deleting type="geodetic" in the SIM_JSBSim.cpp file.

great I am glad it helped you, did you make the changes in the github stuff so I should rerun the install?
to update my sources or just local?

from jsbsim.

professorfabioandrade avatar professorfabioandrade commented on May 24, 2024

Hey @josheeg, I was quoting @TugGoat. I don't know if you are talking about the problem with JSBSim getting the wrong latitude from Ardupilot. If so, I just changed the file in my repo where I am working on using external wind maps on the JSBSim simulations.

from jsbsim.

linuxguy123 avatar linuxguy123 commented on May 24, 2024

@WillemEerland It was not intuitive to debug at all. I guess JSBSim must have updated the way aircraft deal with ground reactions. I only modified the Rascal.xml file https://github.com/TugGoat/Rascal

This file seems to have been removed. Could you publish it again ? Thanks.

from jsbsim.

bcoconni avatar bcoconni commented on May 24, 2024

I only modified the Rascal.xml file https://github.com/TugGoat/Rascal

This file seems to have been removed. Could you publish it again ? Thanks.

The guy has changed its name it seems it is now https://github.com/Teeg93/Rascal

from jsbsim.

linuxguy123 avatar linuxguy123 commented on May 24, 2024

from jsbsim.

hdbalyozsht avatar hdbalyozsht commented on May 24, 2024

I am new to jsbsim project. I try to run with ardupilot. However I couldn't run it.

This is last thing I see at console

Trim (Event 1) executed at time: 0.010000

0: GEAR_CONTACT: 0.01 seconds: LEFT_MLG 1
1: GEAR_CONTACT: 0.01 seconds: RIGHT_MLG 1
2: GEAR_CONTACT: 0.01 seconds: TAIL_LG 1

I have post ardupilot outputs to ArduPilot/ardupilot#10995

from jsbsim.

bcoconni avatar bcoconni commented on May 24, 2024

@hdbalyozsht, generally speaking the standard etiquette requests that closed issues as well as months-old (or even worst years-old) threads shall not be resurrected. Most people do not check old topics for new comments so your contribution will most likely remain unnoticed.

More specifically, it is difficult to tell if your problem is related to JSBSim or to Ardupilot. As Ardupilot is at a higher level in the stack I'd suggest reporting your problem to the Ardupilot project first and if, and only if, they narrow your problem down to an issue in JSBSim then you should open a new issue in JSBSim. Don't resurrect old issues.

Also I've seen that you're trying to resurrect an 18 months old issue in the Ardupilot project. Don't. Open a new issue in the Ardupilot project otherwise, your request will never be addressed.

Good bug hunting πŸ˜‰

from jsbsim.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.