Help with grgsm

892 views
Skip to first unread message

Snehasish Kar

unread,
Jun 29, 2017, 4:37:09 PM6/29/17
to ptrk...@gmail.com, gr-...@googlegroups.com

Hello Piotr Krysik


Have some doubts, it would be great if you clear them.


  1. Is RACCH decoding supported by grgsm? Because If I try to check for channel request message made by MS using the uplink decoding present at https://github.com/ptrkrysik/examples/tree/f4f7c95b491fd6ce0b47d4f16b5019d69e5e22e8/uplink_decoding_demo , I don't see it in the wireshark. Please help me whether i am going wrong somewhere?
  2. Again I want to capture more than one downlink and uplink channel using a single USRP? Can you please let me know, how to achieve this using the wideband processing.py Can I have the grc file for the same.


Please help me with it.


BR

Snehasish


Piotr Krysik

unread,
Jul 21, 2017, 10:13:11 AM7/21/17
to Snehasish Kar, gr-...@googlegroups.com
W dniu 29.06.2017 o 22:37, Snehasish Kar pisze:
> Hello Piotr Krysik
>
>
> Have some doubts, it would be great if you clear them.
>
>
> 1. Is RACCH decoding supported by grgsm? Because If I try to check for
> channel request message made by MS using the uplink decoding present
> at
> https://github.com/ptrkrysik/examples/tree/f4f7c95b491fd6ce0b47d4f16b5019d69e5e22e8/uplink_decoding_demo
> , I don't see it in the wireshark. Please help me whether i am going
> wrong somewhere?
There is no RACCH reception and decoding in gr-gsm yet. The demo you
mentioned is hand-crafted in order to work with data captured by me. For
it to work in any other case you need i.e. to change Kc key value in the
decryption block.

> 2. Again I want to capture more than one downlink and uplink channel
> using a single USRP? Can you please let me know, how to achieve this
> using the wideband processing.py Can I have the grc file for thesame.
>
To do this you need to capture whole band with interesting ARFCNs. There
is for example uhd_rx_cfile program that does that. You can quite easily
do this by connecting UHD source block and file sink in
gnuradio-companion, too. Then you need to channelize the file, for
example using grgsm_channelize.py helper application. To process
frequency hopping data you will need to learn how to use gr-gsm blocks
in gnuradio-companion, as there is no out-of-the box application that
supports this situation yet. I have some example of this, which I don't
remember if was published. I was talking about it during the Camp++ in
Hungary:
https://www.youtube.com/watch?v=xnXMKRIqkZ4


--
Best Regards,
Piotr Krysik

Piotr Krysik

unread,
Jul 21, 2017, 12:23:10 PM7/21/17
to Snehasish Kar, gr-...@googlegroups.com
W dniu 21.07.2017 o 16:13, Piotr Krysik pisze:
I posted the link to the whole presentation when I wanted to point to a
particular section about frequency hopping:
https://youtu.be/xnXMKRIqkZ4?t=3757

snehas...@vehere.com

unread,
Jul 26, 2017, 10:56:25 AM7/26/17
to gr-gsm, snehas...@live.com

Hello
On Friday, July 21, 2017 at 7:43:11 PM UTC+5:30, Piotr Krysik wrote:

>>W dniu 29.06.2017 o 22:37, Snehasish Kar pisze:
> Hello Piotr Krysik
>
>
> Have some doubts, it would be great if you clear them.
>
>
>  1. Is RACCH decoding supported by grgsm? Because If I try to check for
>     channel request message made by MS using the uplink decoding present
>     at
>     https://github.com/ptrkrysik/examples/tree/f4f7c95b491fd6ce0b47d4f16b5019d69e5e22e8/uplink_decoding_demo
>     , I don't see it in the wireshark. Please help me whether i am going
>     wrong somewhere?
There is no RACCH reception and decoding in gr-gsm yet. The demo you
mentioned is hand-crafted in order to work with data captured by me. For
it to work in any other case you need i.e. to change Kc key value in the
decryption block.


In order to test it I have created my own network using openbts with no ciphering. But still I am not able to listen to the uplink voice. I have attached the snapshot of grc file for your reference.

Vasil Velichkov

unread,
Jul 26, 2017, 8:52:30 PM7/26/17
to gr-gsm, snehas...@vehere.com
Hi Snehasish,

On Wednesday, July 26, 2017 at 5:56:25 PM UTC+3, snehas...@live.com wrote:

Hello

In order to test it I have created my own network using openbts with no ciphering. But still I am not able to listen to the uplink voice.

 I think you need to add multiple "TCH/F Demapper" blocks (1 per timeslot) similar to the multiple "SDCCH/8 Demapper" blocks that you already have added.
 
I have attached the snapshot of grc file for your reference.


It would be easier if you could share the grc file together with the downlink and uplink sample files.

BR
Vasil

Snehasish Kar

unread,
Jul 27, 2017, 2:46:07 AM7/27/17
to Vasil Velichkov, gr-gsm
Hello

But wont the uplink be coming in the same TCH/F timeslot as downlink? I was getting downlink voice in  TCH/F timeslot 3 so I have set the demapper to 3. I have attached the grc file for your reference.

BR
Snehasish

uplink_decoding_uhd.grc

snehas...@vehere.com

unread,
Jul 28, 2017, 2:37:30 PM7/28/17
to gr-gsm, vvvel...@gmail.com, snehas...@vehere.com, Piotr Krysik
Any update on this.

BR
Snehasish

Vasil Velichkov

unread,
Jul 28, 2017, 3:32:33 PM7/28/17
to gr-gsm, snehas...@vehere.com
Hi Vasil,


On Friday, July 28, 2017 at 9:37:30 PM UTC+3, snehas...@vehere.com wrote:
Any update on this.

BR
Snehasish

On Thursday, July 27, 2017 at 12:16:07 PM UTC+5:30, Snehasish Kar wrote:
But wont the uplink be coming in the same TCH/F timeslot as downlink?

The same timeslot is used for uplink and downlink.
 
I was getting downlink voice in  TCH/F timeslot 3 so I have set the demapper to 3.

Ok. This was not obvious looking at the screenshot that you'd only shared and that's why I've asked for the grc file.
 
I have attached the grc file for your reference.

Could you capture the downlink and uplink using two File Sink blocks (or uhd_rx_cfile program) and share the cfiles?

BR
Vasil

snehas...@vehere.com

unread,
Jul 28, 2017, 4:36:09 PM7/28/17
to gr-gsm, snehas...@vehere.com, Vasil Velichkov
Hii Vaisl

I will do that tomorrow morning and send you the same. Piotr once said, we need to sync the clock to gps clock for frequency offset correction. How to achieve this with uhd_source in gnuradio ?

snehas...@vehere.com

unread,
Jul 29, 2017, 6:14:43 AM7/29/17
to gr-gsm, snehas...@vehere.com, Vasil Velichkov

snehas...@vehere.com

unread,
Jul 30, 2017, 5:56:16 PM7/30/17
to gr-gsm, snehas...@vehere.com, Vasil Velichkov

Hello Vasil


On Saturday, July 29, 2017 at 1:02:33 AM UTC+5:30, Vasil Velichkov wrote:

Any update for me?

Vasil Velichkov

unread,
Jul 30, 2017, 8:53:54 PM7/30/17
to gr-gsm, snehas...@vehere.com
Hi Snehasish,

Could you try with the attached flowgraph?

BR
Vasil
uplink_decoding_openbts_ts2_ts3.grc

snehas...@vehere.com

unread,
Jul 31, 2017, 7:45:36 AM7/31/17
to gr-gsm, snehas...@vehere.com


I could hear the recorded voice now, trying to receive it in livemon. So training sequence was the problem?

BR
Snehasish

Vasil Velichkov

unread,
Jul 31, 2017, 2:31:09 PM7/31/17
to gr-...@googlegroups.com
Hi Snehasish,

On 31.07.2017 14:45, snehas...@live.com wrote:
> I could hear the recorded voice now, trying to receive it in livemon.
> So training sequence was the problem?

Yes, the main problem was the wrong training sequence that was specified
in the "GSM Receiver" block.

Also I've changed the arfcn (Cell allocation) from [0] to [6] and the FC
parameter from 950M to 936.2M, replaced "BCCH + CCCH Demapper" with
"BCCH + CCCH + SDCCH/4 Demapper" and removed "SDCCH/8 Demapper" blocks
as this is how your openbts is configured. And I've used the
"Up/Down-link splitter" block to write in two separate voice files.

BR
Vasil

snehas...@vehere.com

unread,
Jul 31, 2017, 3:17:46 PM7/31/17
to gr-gsm
Thanks for that, can you send me a pcap containing sdcch/8 configuration. Also does AMR decoding work uplink, I am not able to decode uplink calls with amr codec, for downlink i am able to get.


BR
Snehasish

Vasil Velichkov

unread,
Jul 31, 2017, 3:40:45 PM7/31/17
to gr-...@googlegroups.com
Hi Snehasish,


On 31.07.2017 22:17, snehas...@vehere.com wrote:
can you send me a pcap containing sdcch/8 configuration. 

Follow this instructions https://github.com/ptrkrysik/gr-gsm/wiki/Usage:-Decoding-How-To
The vf_call6_a725_d174_g5_Kc1EF00BAB3BAC7002.cfile contains SDCCH8 on timeslot 1


Also does AMR decoding work uplink, I am not able to decode uplink calls with amr codec, for downlink i am able to get.

I don't know.

BR
Vasil

snehas...@vehere.com

unread,
Jul 31, 2017, 4:37:02 PM7/31/17
to gr-gsm
Thanks anyway! But I hope amr decoding for uplink is supported too.

BR
Snehasish

Piotr Krysik

unread,
Aug 25, 2017, 12:08:41 PM8/25/17
to gr-...@googlegroups.com
W dniu 28.07.2017 o 22:36, snehas...@live.com pisze:

>
> I will do that tomorrow morning and send you the same. Piotr once said,
> we need to sync the clock to gps clock for frequency offset correction.
> How to achieve this with uhd_source in gnuradio ?
>

Hi Snehasish,

I might have said something like this but in some particular context. It
might have been in some discussion about receiving uplink only. In this
situation not having to do reference clock correction would make a lot
of things much easier. To receive uplink and downlink you don't need
this as downlink is providing signals that enable sample rate/carrier
frequency offsets correction (of course if you have GPS synchronization
it is better because you can remove correction blocks used by gr-gsm).

Best Regards,
Piotr Krysik

snehas...@vehere.com

unread,
Sep 1, 2017, 10:47:43 AM9/1/17
to gr-gsm, Vasil Velichkov

Hello Vasil

Need a bit help, last time the grc file you shared with me was working correctly. Now I was trying to implement a channelizer in my GPU, for that instead of UHD_source of gnuradio, I have written my own code in c using the UHD API, so again  we are facing the same issue of not receiving the voice from uplink channel. Below is the link to the downlink and uplink files along with the grc file. Please check if I am going wrong somewhere.

ARFCN= 123
TSEQ= 7
Timeslot No : 2 & 3
usrp_data.dat : downlink capture file
usrp_data1.dat : uplink capture file


Vasil Velichkov

unread,
Sep 1, 2017, 1:57:44 PM9/1/17
to gr-gsm, snehas...@vehere.com
Hi Snehasish,


On Friday, September 1, 2017 at 5:47:43 PM UTC+3, snehas...@live.com wrote:

Hello Vasil

Need a bit help, last time the grc file you shared with me was working correctly. Now I was trying to implement a channelizer in my GPU, for that instead of UHD_source of gnuradio, I have written my own code in c using the UHD API, so again  we are facing the same issue of not receiving the voice from uplink channel. Below is the link to the downlink and uplink files along with the grc file. Please check if I am going wrong somewhere.


Your latest flow graph looks correct, it does decode both uplink voice and messages from the *.cfiles you've previously shared but not from your new  *.dat files. So it seems there is some problems in the way you are capturing using the UHD API but looking only at the usrp_data1.dat I can't say what the problem is.
 
BR
Vasil

snehas...@vehere.com

unread,
Sep 4, 2017, 2:50:17 AM9/4/17
to gr-gsm, Vasil Velichkov

Hello Vasil

I am using the below code to capture both downlink and uplink, please check it once:

#include <uhd/types/tune_request.hpp>
#include <uhd/utils/thread_priority.hpp>
#include <uhd/utils/safe_main.hpp>
#include <uhd/usrp/multi_usrp.hpp>
#include <uhd/exception.hpp>
#include <boost/program_options.hpp>
#include <boost/format.hpp>
#include <boost/thread.hpp>
#include <uhd/stream.hpp>
#include <iostream>
#include <fstream>
#include <csignal>
#include <complex>
#include <pthread.h>

#include "../include/conf.hpp"


static bool stop_signal_called = false;

void sig_int_handler(int){
    stop_signal_called = true;
}

typedef boost::function<uhd::sensor_value_t (const std::string&)> get_sensor_fn_t;

//template<typename samp_type>
void recv_to_file(struct data2hosts data){
    uhd::usrp::multi_usrp::sptr usrp = data.usrp;
        const std::string file = data.file;
        size_t samps_per_buff = data.spb;
        unsigned long long num_requested_samples = data.total_num_samps;
        bool null = NULL;
        bool enable_size_map = data.enable_size_map;
        bool continue_on_bad_packet = data.continue_on_bad_packet;

    unsigned long long num_total_samps = 0;
    //create a receive streamer
    uhd::stream_args_t stream_args("fc32","sc16");//cpu_format,wire_format);
    stream_args.channels.push_back(data.chan_no);
    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args);
    uhd::rx_metadata_t md;
    std::vector<std::complex<float>> buff(samps_per_buff);///////
    std::ofstream outfile;
    if (not null)
        outfile.open(file.c_str(), std::ofstream::binary);
    bool overflow_message = true;

    //setup streaming
    uhd::stream_cmd_t stream_cmd((num_requested_samples == 0)?
        uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS:
        uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE
    );
    stream_cmd.num_samps = size_t(num_requested_samples);
    stream_cmd.stream_now = true;
    stream_cmd.time_spec = uhd::time_spec_t();
    rx_stream->issue_stream_cmd(stream_cmd);

    boost::system_time start = boost::get_system_time();
    //unsigned long long ticks_requested = (long)(time_requested * (double)boost::posix_time::time_duration::ticks_per_second());
    boost::posix_time::time_duration ticks_diff;
    boost::system_time last_update = start;
    unsigned long long last_update_samps = 0;

    typedef std::map<size_t,size_t> SizeMap;
    SizeMap mapSizes;

    while(not stop_signal_called and (num_requested_samples != num_total_samps or num_requested_samples == 0)) {
        boost::system_time now = boost::get_system_time();

        size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, 3.0, enable_size_map);
        //std::cout << boost::format("No of bytes received %u")% num_rx_samps << std::endl;
        if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) {
            std::cout << boost::format("Timeout while streaming") << std::endl;
            break;
        }
        if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW){
            if (overflow_message) {
                overflow_message = false;
                std::cerr << boost::format(
                    "Got an overflow indication. Please consider the following:\n"
                    "  Your write medium must sustain a rate of %fMB/s.\n"
                    "  Dropped samples will not be written to the file.\n"
                    "  Please modify this example for your purposes.\n"
                    "  This message will not appear again.\n"
                ) % (usrp->get_rx_rate()*sizeof(std::complex<float>)/1e6);////////////////
            }
            continue;
        }
        if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){
            std::string error = str(boost::format("Receiver error: %s") % md.strerror());
            if (continue_on_bad_packet){
                std::cerr << error << std::endl;
                continue;
            }
            else
                throw std::runtime_error(error);
        }

        if (enable_size_map) {
            SizeMap::iterator it = mapSizes.find(num_rx_samps);
            if (it == mapSizes.end())
                mapSizes[num_rx_samps] = 0;
            mapSizes[num_rx_samps] += 1;
        }

        num_total_samps += num_rx_samps;

        if (outfile.is_open())
            outfile.write((const char*)&buff.front(), num_rx_samps*sizeof(std::complex<float>));//////////////

        ticks_diff = now - start;
        /*if (ticks_requested > 0){
            if ((unsigned long long)ticks_diff.ticks() > ticks_requested)
                break;
        }*/
    }

    stream_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
    rx_stream->issue_stream_cmd(stream_cmd);

    if (outfile.is_open())
     outfile.close();
}

bool check_locked_sensor(std::vector<std::string> sensor_names, const char* sensor_name, get_sensor_fn_t get_sensor_fn, double setup_time){
    if (std::find(sensor_names.begin(), sensor_names.end(), sensor_name) == sensor_names.end())
        return false;

    boost::system_time start = boost::get_system_time();
    boost::system_time first_lock_time;

    std::cout << boost::format("Waiting for \"%s\": ") % sensor_name;
    std::cout.flush();

    while (true) {
        if ((not first_lock_time.is_not_a_date_time()) and
                (boost::get_system_time() > (first_lock_time + boost::posix_time::seconds(setup_time))))
        {
            std::cout << " locked." << std::endl;
            break;
        }
        if (get_sensor_fn(sensor_name).to_bool()){
            if (first_lock_time.is_not_a_date_time())
                first_lock_time = boost::get_system_time();
            std::cout << "+";
            std::cout.flush();
        }
        else {
            first_lock_time = boost::system_time();    //reset to 'not a date time'

            if (boost::get_system_time() > (start + boost::posix_time::seconds(setup_time))){
                std::cout << std::endl;
                throw std::runtime_error(str(boost::format("timed out waiting for consecutive locks on sensor \"%s\"") % sensor_name));
            }
            std::cout << "_";
            std::cout.flush();
        }
        boost::this_thread::sleep(boost::posix_time::milliseconds(100));
    }
    std::cout << std::endl;
    return true;
}

int UHD_SAFE_MAIN(int argc, char *argv[]){
    uhd::set_thread_priority_safe();
    int i=0;
    std::string args, type ;
    struct rfSettings rfParam[3];
    struct data2hosts data[2];
    double rate, freq, gain, bw, setupTime, totalTime = 0;;
    pthread_t tid[2]={0x00};
    std::string ant("RX2");
    std::string file1("/media/nameme/usrp_data.dat");
    std::string file2("/media/nameme/usrp_data1.dat");
    std::string wirefmt("sc16");
    bool enable_size_map = true;
    bool continue_on_bad_packet = true;
    size_t total_num_samps = 0, spb = 1000000;
    pthread_t tid1,tid2;
    memset(rfParam,0x00,sizeof(rfParam));

    /*****************************Koyel to check***************************/

    rfParam[0].freq = 935800000;
    strcpy(rfParam[0].band,"P-GSM");
    rfParam[0].bandwidth = 25000000;
    rfParam[0].samplerate = 50000000;
    rfParam[0].total_time = 0x00;
    rfParam[0].setup_time = 1.0;
    rfParam[0].gain = 30.0;

    rfParam[1].freq = 925000000;
    strcpy(rfParam[1].band,"E-GSM");
    rfParam[1].bandwidth = 35000000;
    rfParam[1].samplerate = 70000000;
    rfParam[1].setup_time = 1.0;
    rfParam[1].gain = 30.0;

   /* rfParam[2].freq = 1805200000;
    strcpy(rfParam[2].band,"DCS");
    rfParam[2].bandwidth =  100000000; //75000000;
    rfParam[2].samplerate = 200000000;//200000000; //149200000;
    rfParam[2].setup_time = 1.0;
    rfParam[2].gain = 30.0;*/
    rfParam[2].freq = 959600000;//935800000;
    strcpy(rfParam[2].band,"DCS");
    rfParam[2].bandwidth =  250000; //75000000;
    rfParam[2].samplerate = 1000000;//200000000; //149200000;
    rfParam[2].setup_time = 1.0;
    rfParam[2].gain = 30.0;


    /*************************************************************************/

    std::string subdev("A:0 B:0");
    std::string sync("internal");
    for(i=0;i<3;i++){
        if(strcmp(rfParam[i].band,"DCS")==0x00){
            rate = rfParam[i].samplerate;
            freq = rfParam[i].freq;
            gain = rfParam[i].gain;
            bw = rfParam[i].bandwidth;
            setupTime = rfParam[i].setup_time;
            totalTime = rfParam[i].total_time;
            break;
        }
    }
    //create a usrp device
    std::cout << boost::format("\nCreating the USRP device with args: \"%s\"...\n") % args;
    static uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);

    //set subdevice
    usrp->set_rx_subdev_spec(subdev);

    //set antenna
    std::cout << boost::format("Setting antenna to:     %s\n") % ant;
    usrp->set_rx_antenna(ant);
    usrp->set_rx_antenna(ant);

    //tune to a particular frequency
    std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl;
    uhd::tune_request_t tune_request(freq,0);
    tune_request.args = uhd::device_addr_t("mode_n=integer");
    usrp->set_rx_freq(tune_request,0);
    uhd::tune_request_t tune_requestUplink(freq-45000000);
    usrp->set_rx_freq(tune_requestUplink,1);
    std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq(0)/1e6) << std::endl << std::endl;
    std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq(1)/1e6) << std::endl << std::endl;

    //set sample rate
    std::cout << boost::format("Setting sample rate to: %d\n") % rate;
    usrp->set_rx_rate(rate);
    usrp->set_rx_rate(rate);
    std::cout << boost::format("Actual sample rate:     %d \n") % usrp->get_rx_rate(0);
    std::cout << boost::format("Actual sample rate:     %d \n") % usrp->get_rx_rate(1);

    //set gain
    std::cout << boost::format("Setting gain to: %d\n") % gain;
    usrp->set_rx_gain(gain,0);
    usrp->set_rx_gain(gain,1);
    std::cout << boost::format("Actual gain:     %d\n") % usrp->get_rx_gain(0);
    std::cout << boost::format("Actual gain:     %d\n") % usrp->get_rx_gain(1);

    //set the bandwidth
    std::cout << boost::format("Setting bandwidth to %f MHz")%(bw/1e6) << std::endl;
    usrp->set_rx_bandwidth(bw,0);
    usrp->set_rx_bandwidth(bw,1);
    std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % (usrp->get_rx_bandwidth(0)/1e6) << std::endl << std::endl;
    std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % (usrp->get_rx_bandwidth(1)/1e6) << std::endl << std::endl;


    boost::this_thread::sleep(boost::posix_time::seconds(rfParam[0].setup_time)); //allow for some setup time
    check_locked_sensor(usrp->get_rx_sensor_names(0), "lo_locked", boost::bind(&uhd::usrp::multi_usrp::get_rx_sensor, usrp, _1, 0), rfParam[0].setup_time);
    // Get an rx_streamer from the device
    //check_locked_sensor(usrp->get_rx_sensor_names(0), "lo_locked", boost::bind(&uhd::usrp::multi_usrp::get_rx_sensor, usrp, _1, 0), rfParam[0].setup_time);
    std::signal(SIGINT, &sig_int_handler);
    std::cout << "Press Ctrl + C to stop streaming..." << std::endl;
    data[0].usrp = usrp;
    data[0].file = file1;
    data[0].total_num_samps = total_num_samps;
    data[0].spb = spb;
    data[0].enable_size_map = enable_size_map;
    data[0].continue_on_bad_packet = continue_on_bad_packet;
    data[0].chan_no = 0;

    data[1].usrp = usrp;
    data[1].file = file2;
    data[1].total_num_samps = total_num_samps;
    data[1].spb = spb;
    data[1].enable_size_map = enable_size_map;
    data[1].continue_on_bad_packet = continue_on_bad_packet;
    data[1].chan_no = 1;

/*#define recv_to_file_args(format) \
    (usrp, file1, spb, total_num_samps, NULL, enable_size_map, continue_on_bad_packet)
    recv_to_file<std::complex<float> >recv_to_file_args("fc32");*/
    boost::thread thread1(&recv_to_file,data[0]);
    boost::thread thread2(&recv_to_file,data[1]);
    thread1.join();
    thread2.join();
    //finished
     std::cout << std::endl << "Done!" << std::endl << std::endl;
Reply all
Reply to author
Forward
0 new messages